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
;
51 wxPen
*white_background_pen
;
52 wxPen
*transparent_pen
;
53 wxBrush
*white_background_brush
;
54 wxPen
*black_foreground_pen
;
56 char *GraphicsBuffer
= NULL
;
57 wxCursor
*GraphicsBullseyeCursor
= NULL
;
59 wxList
wxObjectCopyMapping(wxKEY_INTEGER
);
61 void wxOGLInitialize()
63 GraphicsBullseyeCursor
= new wxCursor(wxCURSOR_BULLSEYE
);
65 g_oglNormalFont
= new wxFont(10, wxSWISS
, wxNORMAL
, wxNORMAL
);
67 black_pen
= new wxPen("BLACK", 1, wxSOLID
);
69 white_background_pen
= new wxPen("WHITE", 1, wxSOLID
);
70 transparent_pen
= new wxPen("WHITE", 1, wxTRANSPARENT
);
71 white_background_brush
= new wxBrush("WHITE", wxSOLID
);
72 black_foreground_pen
= new wxPen("BLACK", 1, wxSOLID
);
74 OGLInitializeConstraintTypes();
76 // Initialize big buffer used when writing images
77 GraphicsBuffer
= new char[3000];
79 if (!oglPopupDivisionMenu
)
81 oglPopupDivisionMenu
= new wxMenu("", (wxFunction
)oglGraphicsDivisionMenuProc
);
82 oglPopupDivisionMenu
->Append(DIVISION_MENU_SPLIT_HORIZONTALLY
, "Split horizontally");
83 oglPopupDivisionMenu
->Append(DIVISION_MENU_SPLIT_VERTICALLY
, "Split vertically");
84 oglPopupDivisionMenu
->AppendSeparator();
85 oglPopupDivisionMenu
->Append(DIVISION_MENU_EDIT_LEFT_EDGE
, "Edit left edge");
86 oglPopupDivisionMenu
->Append(DIVISION_MENU_EDIT_TOP_EDGE
, "Edit top edge");
94 delete[] GraphicsBuffer
;
95 GraphicsBuffer
= NULL
;
97 GraphicsBuffer
= NULL
;
98 if (oglPopupDivisionMenu
)
100 delete oglPopupDivisionMenu
;
101 oglPopupDivisionMenu
= NULL
;
105 delete g_oglNormalFont
;
106 g_oglNormalFont
= NULL
;
113 if (white_background_pen
)
115 delete white_background_pen
;
116 white_background_pen
= NULL
;
120 delete transparent_pen
;
121 transparent_pen
= NULL
;
123 if (white_background_brush
)
125 delete white_background_brush
;
126 white_background_brush
= NULL
;
128 if (black_foreground_pen
)
130 delete black_foreground_pen
;
131 black_foreground_pen
= NULL
;
134 OGLCleanUpConstraintTypes();
137 wxFont
*MatchFont(int point_size
)
139 wxFont
*font
= wxTheFontList
->FindOrCreateFont(point_size
, wxSWISS
, wxNORMAL
, wxNORMAL
);
153 font
= swiss_font_12
;
156 font
= swiss_font_14
;
159 font
= swiss_font_18
;
162 font
= swiss_font_24
;
166 font
= swiss_font_10
;
173 int FontSizeDialog(wxFrame
*parent
, int old_size
)
178 sprintf(buf
, "%d", old_size
);
179 wxString ans
= wxGetTextFromUser("Enter point size", "Font size", buf
, parent
);
183 int new_size
= atoi(ans
);
184 if ((new_size
<= 0) || (new_size
> 40))
186 wxMessageBox("Invalid point size!", "Error", wxOK
);
200 char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
204 sscanf(ans, "%d", &size);
205 return MatchFont(size);
211 // Centre a list of strings in the given box. xOffset and yOffset are the
212 // the positions that these lines should be relative to, and this might be
213 // the same as m_xpos, m_ypos, but might be zero if formatting from left-justifying.
214 void CentreText(wxDC
& dc
, wxList
*text_list
,
215 float m_xpos
, float m_ypos
, float width
, float height
,
218 int n
= text_list
->Number();
220 if (!text_list
|| (n
== 0))
223 // First, get maximum dimensions of box enclosing text
225 float char_height
= 0;
227 float current_width
= 0;
229 // Store text extents for speed
230 float *widths
= new float[n
];
232 wxNode
*current
= text_list
->First();
236 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
237 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
238 widths
[i
] = current_width
;
240 if (current_width
> max_width
)
241 max_width
= current_width
;
242 current
= current
->Next();
246 float max_height
= n
*char_height
;
248 float xoffset
, yoffset
, xOffset
, yOffset
;
250 if (formatMode
& FORMAT_CENTRE_VERT
)
252 if (max_height
< height
)
253 yoffset
= (float)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
255 yoffset
= (float)(m_ypos
- (height
/2.0));
264 if (formatMode
& FORMAT_CENTRE_HORIZ
)
266 xoffset
= (float)(m_xpos
- width
/2.0);
275 current
= text_list
->First();
280 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
283 if ((formatMode
& FORMAT_CENTRE_HORIZ
) && (widths
[i
] < width
))
284 x
= (float)((width
- widths
[i
])/2.0 + xoffset
);
287 float y
= (float)(i
*char_height
+ yoffset
);
289 line
->SetX( x
- xOffset
); line
->SetY( y
- yOffset
);
290 current
= current
->Next();
297 // Centre a list of strings in the given box
298 void CentreTextNoClipping(wxDC
& dc
, wxList
*text_list
,
299 float m_xpos
, float m_ypos
, float width
, float height
)
301 int n
= text_list
->Number();
303 if (!text_list
|| (n
== 0))
306 // First, get maximum dimensions of box enclosing text
308 float char_height
= 0;
310 float current_width
= 0;
312 // Store text extents for speed
313 float *widths
= new float[n
];
315 wxNode
*current
= text_list
->First();
319 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
320 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
321 widths
[i
] = current_width
;
323 if (current_width
> max_width
)
324 max_width
= current_width
;
325 current
= current
->Next();
329 float max_height
= n
*char_height
;
331 float yoffset
= (float)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
333 float xoffset
= (float)(m_xpos
- width
/2.0);
335 current
= text_list
->First();
340 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
342 float x
= (float)((width
- widths
[i
])/2.0 + xoffset
);
343 float y
= (float)(i
*char_height
+ yoffset
);
345 line
->SetX( x
- m_xpos
); line
->SetY( y
- m_ypos
);
346 current
= current
->Next();
352 void GetCentredTextExtent(wxDC
& dc
, wxList
*text_list
,
353 float m_xpos
, float m_ypos
, float width
, float height
,
354 float *actual_width
, float *actual_height
)
356 int n
= text_list
->Number();
358 if (!text_list
|| (n
== 0))
365 // First, get maximum dimensions of box enclosing text
367 float char_height
= 0;
369 float current_width
= 0;
371 wxNode
*current
= text_list
->First();
375 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
376 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
378 if (current_width
> max_width
)
379 max_width
= current_width
;
380 current
= current
->Next();
384 *actual_height
= n
*char_height
;
385 *actual_width
= max_width
;
388 // Format a string to a list of strings that fit in the given box.
389 // Interpret %n and 10 or 13 as a new line.
390 wxList
*FormatText(wxDC
& dc
, const wxString
& text
, float width
, float height
, int formatMode
)
392 // First, parse the string into a list of words
395 // Make new lines into NULL strings at this point
396 int i
= 0; int j
= 0; int len
= strlen(text
);
397 char word
[200]; word
[0] = 0;
398 bool end_word
= FALSE
; bool new_line
= FALSE
;
407 { word
[j
] = '%'; j
++; }
411 { new_line
= TRUE
; end_word
= TRUE
; i
++; }
413 { word
[j
] = '%'; j
++; word
[j
] = text
[i
]; j
++; i
++; }
419 new_line
= TRUE
; end_word
= TRUE
; i
++;
424 new_line
= TRUE
; end_word
= TRUE
; i
++;
439 if (i
== len
) end_word
= TRUE
;
444 word_list
.Append((wxObject
*)copystring(word
));
449 word_list
.Append((wxObject
*)NULL
);
453 // Now, make a list of strings which can fit in the box
454 wxList
*string_list
= new wxList
;
458 wxNode
*node
= word_list
.First();
463 char *keep_string
= copystring(buffer
);
465 char *s
= (char *)node
->Data();
469 if (strlen(keep_string
) > 0)
470 string_list
->Append((wxObject
*)keep_string
);
472 delete[] keep_string
;
482 dc
.GetTextExtent(buffer
, &x
, &y
);
484 // Don't fit within the bounding box if we're fitting shape to contents
485 if ((x
> width
) && !(formatMode
& FORMAT_SIZE_TO_CONTENTS
))
487 // Deal with first word being wider than box
488 if (strlen(keep_string
) > 0)
489 string_list
->Append((wxObject
*)keep_string
);
491 delete[] keep_string
;
498 delete[] keep_string
;
504 string_list
->Append((wxObject
*)copystring(buffer
));
509 void DrawFormattedText(wxDC
& dc
, wxList
*text_list
,
510 float m_xpos
, float m_ypos
, float width
, float height
,
513 float xoffset
, yoffset
;
514 if (formatMode
& FORMAT_CENTRE_HORIZ
)
517 xoffset
= (float)(m_xpos
- (width
/ 2.0));
519 if (formatMode
& FORMAT_CENTRE_VERT
)
522 yoffset
= (float)(m_ypos
- (height
/ 2.0));
524 dc
.SetClippingRegion(
525 (float)(m_xpos
- width
/2.0), (float)(m_ypos
- height
/2.0),
526 (float)width
, (float)height
);
528 wxNode
*current
= text_list
->First();
531 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
533 dc
.DrawText(line
->GetText(), xoffset
+ line
->GetX(), yoffset
+ line
->GetY());
534 current
= current
->Next();
537 dc
.DestroyClippingRegion();
541 * Find centroid given list of points comprising polyline
545 void find_polyline_centroid(wxList
*points
, float *x
, float *y
)
550 wxNode
*node
= points
->First();
553 wxRealPoint
*point
= (wxRealPoint
*)node
->Data();
559 *x
= (xcount
/points
->Number());
560 *y
= (ycount
/points
->Number());
564 * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
565 * If so, ratio1 gives the proportion along the first line
566 * that the intersection occurs (or something like that).
567 * Used by functions below.
570 void check_line_intersection(float x1
, float y1
, float x2
, float y2
,
571 float x3
, float y3
, float x4
, float y4
,
572 float *ratio1
, float *ratio2
)
574 float denominator_term
= (y4
- y3
)*(x2
- x1
) - (y2
- y1
)*(x4
- x3
);
575 float numerator_term
= (x3
- x1
)*(y4
- y3
) + (x4
- x3
)*(y1
- y3
);
578 float length_ratio
= 1.0;
581 // Check for parallel lines
582 if ((denominator_term
< 0.005) && (denominator_term
> -0.005))
583 line_constant
= -1.0;
585 line_constant
= numerator_term
/denominator_term
;
587 // Check for intersection
588 if ((line_constant
< 1.0) && (line_constant
> 0.0))
590 // Now must check that other line hits
591 if (((y4
- y3
) < 0.005) && ((y4
- y3
) > -0.005))
592 k_line
= ((x1
- x3
) + line_constant
*(x2
- x1
))/(x4
- x3
);
594 k_line
= ((y1
- y3
) + line_constant
*(y2
- y1
))/(y4
- y3
);
596 if ((k_line
>= 0.0) && (k_line
< 1.0))
597 length_ratio
= line_constant
;
601 *ratio1
= length_ratio
;
606 * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
607 * (*x3, *y3) is the point where it hits.
610 void find_end_for_polyline(float n
, float xvec
[], float yvec
[],
611 float x1
, float y1
, float x2
, float y2
, float *x3
, float *y3
)
614 float lastx
= xvec
[0];
615 float lasty
= yvec
[0];
617 float min_ratio
= 1.0;
621 for (i
= 1; i
< n
; i
++)
623 check_line_intersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[i
], yvec
[i
],
624 &line_ratio
, &other_ratio
);
628 if (line_ratio
< min_ratio
)
629 min_ratio
= line_ratio
;
632 // Do last (implicit) line if last and first floats are not identical
633 if (!(xvec
[0] == lastx
&& yvec
[0] == lasty
))
635 check_line_intersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[0], yvec
[0],
636 &line_ratio
, &other_ratio
);
638 if (line_ratio
< min_ratio
)
639 min_ratio
= line_ratio
;
642 *x3
= (x1
+ (x2
- x1
)*min_ratio
);
643 *y3
= (y1
+ (y2
- y1
)*min_ratio
);
648 * Find where the line hits the box.
652 void find_end_for_box(float width
, float height
,
653 float x1
, float y1
, // Centre of box (possibly)
654 float x2
, float y2
, // other end of line
655 float *x3
, float *y3
) // End on box edge
660 xvec
[0] = (float)(x1
- width
/2.0);
661 yvec
[0] = (float)(y1
- height
/2.0);
662 xvec
[1] = (float)(x1
- width
/2.0);
663 yvec
[1] = (float)(y1
+ height
/2.0);
664 xvec
[2] = (float)(x1
+ width
/2.0);
665 yvec
[2] = (float)(y1
+ height
/2.0);
666 xvec
[3] = (float)(x1
+ width
/2.0);
667 yvec
[3] = (float)(y1
- height
/2.0);
668 xvec
[4] = (float)(x1
- width
/2.0);
669 yvec
[4] = (float)(y1
- height
/2.0);
671 find_end_for_polyline(5, xvec
, yvec
, x2
, y2
, x1
, y1
, x3
, y3
);
675 * Find where the line hits the circle.
679 void find_end_for_circle(float radius
,
680 float x1
, float y1
, // Centre of circle
681 float x2
, float y2
, // Other end of line
682 float *x3
, float *y3
)
684 float H
= (float)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
693 *y3
= radius
* (y2
- y1
)/H
+ y1
;
694 *x3
= radius
* (x2
- x1
)/H
+ x1
;
699 * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
700 * return the position of the tip of the arrow and the left and right vertices of the arrow.
704 void get_arrow_points(float x1
, float y1
, float x2
, float y2
,
705 float length
, float width
,
706 float *tip_x
, float *tip_y
,
707 float *side1_x
, float *side1_y
,
708 float *side2_x
, float *side2_y
)
710 float l
= (float)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
715 float i_bar
= (x2
- x1
)/l
;
716 float j_bar
= (y2
- y1
)/l
;
718 float x3
= (- length
*i_bar
) + x2
;
719 float y3
= (- length
*j_bar
) + y2
;
721 *side1_x
= width
*(-j_bar
) + x3
;
722 *side1_y
= width
*i_bar
+ y3
;
724 *side2_x
= -width
*(-j_bar
) + x3
;
725 *side2_y
= -width
*i_bar
+ y3
;
727 *tip_x
= x2
; *tip_y
= y2
;
731 * Given an ellipse and endpoints of a line, returns the point at which
732 * the line touches the ellipse in values x4, y4.
733 * This function assumes that the centre of the ellipse is at x1, y1, and the
734 * ellipse has a width of width1 and a height of height1. It also assumes you are
735 * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
736 * This function calculates the x,y coordinates of the intersection point of
737 * the arc with the ellipse.
738 * Author: Ian Harrison
741 void draw_arc_to_ellipse(float x1
, float y1
, float width1
, float height1
, float x2
, float y2
, float x3
, float y3
,
742 float *x4
, float *y4
)
744 float a1
= (float)(width1
/2.0);
745 float b1
= (float)(height1
/2.0);
747 // These are required to give top left x and y coordinates for DrawEllipse
748 // float top_left_x1 = (float)(x1 - a1);
749 // float top_left_y1 = (float)(y1 - b1);
751 // Check for vertical line
752 if (fabs(x2 - x3) < 0.05)
756 *y4 = (float)(y1 - b1);
758 *y4 = (float)(y1 + b1);
762 // Check that x2 != x3
763 if (fabs(x2
- x3
) < 0.05)
767 *y4
= (float)(y1
- sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
769 *y4
= (float)(y1
+ sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
773 // Calculate the x and y coordinates of the point where arc intersects ellipse
775 float A
, B
, C
, D
, E
, F
, G
, H
, K
;
776 float ellipse1_x
, ellipse1_y
;
778 A
= (float)(1/(a1
* a1
));
779 B
= (float)((y3
- y2
) * (y3
- y2
)) / ((x3
- x2
) * (x3
- x2
) * b1
* b1
);
780 C
= (float)(2 * (y3
- y2
) * (y2
- y1
)) / ((x3
- x2
) * b1
* b1
);
781 D
= (float)((y2
- y1
) * (y2
- y1
)) / (b1
* b1
);
783 F
= (float)(C
- (2 * A
* x1
) - (2 * B
* x2
));
784 G
= (float)((A
* x1
* x1
) + (B
* x2
* x2
) - (C
* x2
) + D
- 1);
785 H
= (float)((y3
- y2
) / (x3
- x2
));
786 K
= (float)((F
* F
) - (4 * E
* G
));
789 // In this case the line intersects the ellipse, so calculate intersection
793 ellipse1_x
= (float)(((F
* -1) + sqrt(K
)) / (2 * E
));
794 ellipse1_y
= (float)((H
* (ellipse1_x
- x2
)) + y2
);
798 ellipse1_x
= (float)(((F
* -1) - sqrt(K
)) / (2 * E
));
799 ellipse1_y
= (float)((H
* (ellipse1_x
- x2
)) + y2
);
803 // in this case, arc does not intersect ellipse, so just draw arc
812 // Draw a little circle (radius = 2) at the end of the arc where it hits
815 float circle_x = ellipse1_x - 2.0;
816 float circle_y = ellipse1_y - 2.0;
817 m_canvas->DrawEllipse(circle_x, circle_y, 4.0, 4.0);
821 // Update a list item from a list of strings
822 void UpdateListBox(wxListBox
*item
, wxList
*list
)
828 wxNode
*node
= list
->First();
831 char *s
= (char *)node
->Data();
838 * Hex<->Dec conversion
841 // Array used in DecToHex conversion routine.
842 static char sg_HexArray
[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
843 'C', 'D', 'E', 'F' };
845 // Convert 2-digit hex number to decimal
846 unsigned int oglHexToDec(char* buf
)
848 int firstDigit
, secondDigit
;
851 firstDigit
= buf
[0] - 'A' + 10;
853 firstDigit
= buf
[0] - '0';
856 secondDigit
= buf
[1] - 'A' + 10;
858 secondDigit
= buf
[1] - '0';
860 return firstDigit
* 16 + secondDigit
;
863 // Convert decimal integer to 2-character hex string
864 void oglDecToHex(unsigned int dec
, char *buf
)
866 int firstDigit
= (int)(dec
/16.0);
867 int secondDigit
= (int)(dec
- (firstDigit
*16.0));
868 buf
[0] = sg_HexArray
[firstDigit
];
869 buf
[1] = sg_HexArray
[secondDigit
];
873 // 3-digit hex to wxColour
874 wxColour
oglHexToColour(const wxString
& hex
)
876 if (hex
.Length() == 6)
879 strncpy(buf
, hex
, 7);
880 unsigned int r
= oglHexToDec((char *)buf
);
881 unsigned int g
= oglHexToDec((char *)(buf
+2));
882 unsigned int b
= oglHexToDec((char *)(buf
+4));
883 return wxColour(r
, g
, b
);
886 return wxColour(0,0,0);
889 // RGB to 3-digit hex
890 wxString
oglColourToHex(const wxColour
& colour
)
893 unsigned int red
= colour
.Red();
894 unsigned int green
= colour
.Green();
895 unsigned int blue
= colour
.Blue();
897 oglDecToHex(red
, buf
);
898 oglDecToHex(green
, buf
+2);
899 oglDecToHex(blue
, buf
+4);
901 return wxString(buf
);