📄 lines.cpp
字号:
void wxLineShape::SetAttachments(int from_attach, int to_attach)
{
m_attachmentFrom = from_attach;
m_attachmentTo = to_attach;
}
bool wxLineShape::HitTest(double x, double y, int *attachment, double *distance)
{
if (!m_lineControlPoints)
return false;
// Look at label regions in case mouse is over a label
bool inLabelRegion = false;
for (int i = 0; i < 3; i ++)
{
wxNode *regionNode = m_regions.Item(i);
if (regionNode)
{
wxShapeRegion *region = (wxShapeRegion *)regionNode->GetData();
if (region->m_formattedText.GetCount() > 0)
{
double xp, yp, cx, cy, cw, ch;
GetLabelPosition(i, &xp, &yp);
// Offset region from default label position
region->GetPosition(&cx, &cy);
region->GetSize(&cw, &ch);
cx += xp;
cy += yp;
double rLeft = (double)(cx - (cw/2.0));
double rTop = (double)(cy - (ch/2.0));
double rRight = (double)(cx + (cw/2.0));
double rBottom = (double)(cy + (ch/2.0));
if (x > rLeft && x < rRight && y > rTop && y < rBottom)
{
inLabelRegion = true;
i = 3;
}
}
}
}
wxNode *node = m_lineControlPoints->GetFirst();
while (node && node->GetNext())
{
wxRealPoint *point1 = (wxRealPoint *)node->GetData();
wxRealPoint *point2 = (wxRealPoint *)node->GetNext()->GetData();
// For inaccurate mousing allow 8 pixel corridor
int extra = 4;
double dx = point2->x - point1->x;
double dy = point2->y - point1->y;
double seg_len = sqrt(dx*dx+dy*dy);
double distance_from_seg =
seg_len*((x-point1->x)*dy-(y-point1->y)*dx)/(dy*dy+dx*dx);
double distance_from_prev =
seg_len*((y-point1->y)*dy+(x-point1->x)*dx)/(dy*dy+dx*dx);
if ((fabs(distance_from_seg) < extra &&
distance_from_prev >= 0 && distance_from_prev <= seg_len)
|| inLabelRegion)
{
*attachment = 0;
*distance = distance_from_seg;
return true;
}
node = node->GetNext();
}
return false;
}
void wxLineShape::DrawArrows(wxDC& dc)
{
// Distance along line of each arrow: space them out evenly.
double startArrowPos = 0.0;
double endArrowPos = 0.0;
double middleArrowPos = 0.0;
wxNode *node = m_arcArrows.GetFirst();
while (node)
{
wxArrowHead *arrow = (wxArrowHead *)node->GetData();
switch (arrow->GetArrowEnd())
{
case ARROW_POSITION_START:
{
if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
// If specified, x offset is proportional to line length
DrawArrow(dc, arrow, arrow->GetXOffset(), true);
else
{
DrawArrow(dc, arrow, startArrowPos, false); // Absolute distance
startArrowPos += arrow->GetSize() + arrow->GetSpacing();
}
break;
}
case ARROW_POSITION_END:
{
if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
DrawArrow(dc, arrow, arrow->GetXOffset(), true);
else
{
DrawArrow(dc, arrow, endArrowPos, false);
endArrowPos += arrow->GetSize() + arrow->GetSpacing();
}
break;
}
case ARROW_POSITION_MIDDLE:
{
arrow->SetXOffset(middleArrowPos);
if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
DrawArrow(dc, arrow, arrow->GetXOffset(), true);
else
{
DrawArrow(dc, arrow, middleArrowPos, false);
middleArrowPos += arrow->GetSize() + arrow->GetSpacing();
}
break;
}
}
node = node->GetNext();
}
}
void wxLineShape::DrawArrow(wxDC& dc, wxArrowHead *arrow, double xOffset, bool proportionalOffset)
{
wxNode *first_line_node = m_lineControlPoints->GetFirst();
wxRealPoint *first_line_point = (wxRealPoint *)first_line_node->GetData();
wxNode *second_line_node = first_line_node->GetNext();
wxRealPoint *second_line_point = (wxRealPoint *)second_line_node->GetData();
wxNode *last_line_node = m_lineControlPoints->GetLast();
wxRealPoint *last_line_point = (wxRealPoint *)last_line_node->GetData();
wxNode *second_last_line_node = last_line_node->GetPrevious();
wxRealPoint *second_last_line_point = (wxRealPoint *)second_last_line_node->GetData();
// Position where we want to start drawing
double positionOnLineX = 0.0, positionOnLineY = 0.0;
// Position of start point of line, at the end of which we draw the arrow.
double startPositionX = 0.0 , startPositionY = 0.0;
switch (arrow->GetPosition())
{
case ARROW_POSITION_START:
{
// If we're using a proportional offset, calculate just where this will
// be on the line.
double realOffset = xOffset;
if (proportionalOffset)
{
double totalLength =
(double)sqrt((second_line_point->x - first_line_point->x)*(second_line_point->x - first_line_point->x) +
(second_line_point->y - first_line_point->y)*(second_line_point->y - first_line_point->y));
realOffset = (double)(xOffset * totalLength);
}
GetPointOnLine(second_line_point->x, second_line_point->y,
first_line_point->x, first_line_point->y,
realOffset, &positionOnLineX, &positionOnLineY);
startPositionX = second_line_point->x;
startPositionY = second_line_point->y;
break;
}
case ARROW_POSITION_END:
{
// If we're using a proportional offset, calculate just where this will
// be on the line.
double realOffset = xOffset;
if (proportionalOffset)
{
double totalLength =
(double)sqrt((second_last_line_point->x - last_line_point->x)*(second_last_line_point->x - last_line_point->x) +
(second_last_line_point->y - last_line_point->y)*(second_last_line_point->y - last_line_point->y));
realOffset = (double)(xOffset * totalLength);
}
GetPointOnLine(second_last_line_point->x, second_last_line_point->y,
last_line_point->x, last_line_point->y,
realOffset, &positionOnLineX, &positionOnLineY);
startPositionX = second_last_line_point->x;
startPositionY = second_last_line_point->y;
break;
}
case ARROW_POSITION_MIDDLE:
{
// Choose a point half way between the last and penultimate points
double x = ((last_line_point->x + second_last_line_point->x)/2);
double y = ((last_line_point->y + second_last_line_point->y)/2);
// If we're using a proportional offset, calculate just where this will
// be on the line.
double realOffset = xOffset;
if (proportionalOffset)
{
double totalLength =
(double)sqrt((second_last_line_point->x - x)*(second_last_line_point->x - x) +
(second_last_line_point->y - y)*(second_last_line_point->y - y));
realOffset = (double)(xOffset * totalLength);
}
GetPointOnLine(second_last_line_point->x, second_last_line_point->y,
x, y, realOffset, &positionOnLineX, &positionOnLineY);
startPositionX = second_last_line_point->x;
startPositionY = second_last_line_point->y;
break;
}
}
/*
* Add yOffset to arrow, if any
*/
const double myPi = (double) M_PI;
// The translation that the y offset may give
double deltaX = 0.0;
double deltaY = 0.0;
if ((arrow->GetYOffset() != 0.0) && !m_ignoreArrowOffsets)
{
/*
|(x4, y4)
|d
|
(x1, y1)--------------(x3, y3)------------------(x2, y2)
x4 = x3 - d * sin(theta)
y4 = y3 + d * cos(theta)
Where theta = tan(-1) of (y3-y1)/(x3-x1)
*/
double x1 = startPositionX;
double y1 = startPositionY;
double x3 = positionOnLineX;
double y3 = positionOnLineY;
double d = -arrow->GetYOffset(); // Negate so +offset is above line
double theta;
if (x3 == x1)
theta = (double)(myPi/2.0);
else
theta = (double)atan((y3-y1)/(x3-x1));
double x4 = (double)(x3 - (d*sin(theta)));
double y4 = (double)(y3 + (d*cos(theta)));
deltaX = x4 - positionOnLineX;
deltaY = y4 - positionOnLineY;
}
switch (arrow->_GetType())
{
case ARROW_ARROW:
{
double arrowLength = arrow->GetSize();
double arrowWidth = (double)(arrowLength/3.0);
double tip_x, tip_y, side1_x, side1_y, side2_x, side2_y;
oglGetArrowPoints(startPositionX+deltaX, startPositionY+deltaY,
positionOnLineX+deltaX, positionOnLineY+deltaY,
arrowLength, arrowWidth, &tip_x, &tip_y,
&side1_x, &side1_y, &side2_x, &side2_y);
wxPoint points[4];
points[0].x = (int) tip_x; points[0].y = (int) tip_y;
points[1].x = (int) side1_x; points[1].y = (int) side1_y;
points[2].x = (int) side2_x; points[2].y = (int) side2_y;
points[3].x = (int) tip_x; points[3].y = (int) tip_y;
dc.SetPen(* m_pen);
dc.SetBrush(* m_brush);
dc.DrawPolygon(4, points);
break;
}
case ARROW_HOLLOW_CIRCLE:
case ARROW_FILLED_CIRCLE:
{
// Find point on line of centre of circle, which is a radius away
// from the end position
double diameter = (double)(arrow->GetSize());
double x, y;
GetPointOnLine(startPositionX+deltaX, startPositionY+deltaY,
positionOnLineX+deltaX, positionOnLineY+deltaY,
(double)(diameter/2.0),
&x, &y);
// Convert ellipse centre to top-left coordinates
double x1 = (double)(x - (diameter/2.0));
double y1 = (double)(y - (diameter/2.0));
dc.SetPen(* m_pen);
if (arrow->_GetType() == ARROW_HOLLOW_CIRCLE)
dc.SetBrush(GetBackgroundBrush());
else
dc.SetBrush(* m_brush);
dc.DrawEllipse((long) x1, (long) y1, (long) diameter, (long) diameter);
break;
}
case ARROW_SINGLE_OBLIQUE:
{
break;
}
case ARROW_METAFILE:
{
if (arrow->GetMetaFile())
{
// Find point on line of centre of object, which is a half-width away
// from the end position
/*
* width
* <-- start pos <-----><-- positionOnLineX
* _____
* --------------| x | <-- e.g. rectangular arrowhead
* -----
*/
double x, y;
GetPointOnLine(startPositionX, startPositionY,
positionOnLineX, positionOnLineY,
(double)(arrow->GetMetaFile()->m_width/2.0),
&x, &y);
// Calculate theta for rotating the metafile.
/*
|
| o(x2, y2) 'o' represents the arrowhead.
| /
| /
| /theta
| /(x1, y1)
|______________________
*/
double theta = 0.0;
double x1 = startPositionX;
double y1 = startPositionY;
double x2 = positionOnLineX;
double y2 = positionOnLineY;
if ((x1 == x2) && (y1 == y2))
theta = 0.0;
else if ((x1 == x2) && (y1 > y2))
theta = (double)(3.0*myPi/2.0);
else if ((x1 == x2) && (y2 > y1))
theta = (double)(myPi/2.0);
else if ((x2 > x1) && (y2 >= y1))
theta = (double)atan((y2 - y1)/(x2 - x1));
else if (x2 < x1)
theta = (double)(myPi + atan((y2 - y1)/(x2 - x1)));
else if ((x2 > x1) && (y2 < y1))
theta = (double)(2*myPi + atan((y2 - y1)/(x2 - x1)));
else
{
wxLogFatalError(wxT("Unknown arrowhead rotation case in lines.cc"));
}
// Rotate about the centre of the object, then place
// the object on the line.
if (arrow->GetMetaFile()->GetRotateable())
arrow->GetMetaFile()->Rotate(0.0, 0.0, theta);
if (m_erasing)
{
// If erasing, just draw a rectangle.
double minX, minY, maxX, maxY;
arrow->GetMetaFile()->GetBounds(&minX, &minY, &maxX, &maxY);
// Make erasing rectangle slightly bigger or you get droppings.
int extraPixels = 4;
dc.DrawRectangle((long)(deltaX + x + minX - (extraPixels/2.0)), (long)(deltaY + y + minY - (extraPixels/2.0)),
(long)(maxX - minX + extraPixels), (long)(maxY - minY + extraPixels));
}
else
arrow->GetMetaFile()->Draw(dc, x+deltaX, y+deltaY);
}
break;
}
default:
{
}
}
}
void wxLineShape::OnErase(wxDC& dc)
{
const wxPen *old_pen = m_pen;
const wxBrush *old_brush = m_brush;
wxPen bg_pen = GetBackgroundPen();
wxBrush bg_brush = GetBackgroundBrush();
SetPen(&bg_pen);
SetBrush(&bg_brush);
double bound_x, bound_y;
GetBoundingBoxMax(&bound_x, &bound_y);
if (m_font) dc.SetFont(* m_font);
// Undraw text regions
for (int i = 0; i < 3; i++)
{
wxNode *node = m_regions.Item(i);
if (node)
{
double x, y;
wxShapeRegion *region = (wxShapeRegion *)node->GetData();
GetLabelPosition(i, &x, &y);
EraseRegion(dc, region, x, y);
}
}
// Undraw line
dc.SetPen(GetBackgroundPen());
dc.SetBrush(GetBackgroundBrush());
// Drawing over the line only seems to work if the line has a thickness
// of 1.
if (old_pen && (old_pen->GetWidth() > 1))
{
dc.DrawRectangle((long)(m_xpos - (bound_x/2.0) - 2.0), (long)(m_ypos - (bound_y/2.0) - 2.0),
(long)(bound_x+4.0), (long)(bound_y+4.0));
}
else
{
m_erasing = true;
GetEventHandler()->OnDraw(dc);
GetEventHandler()->OnEraseControlPoints(dc);
m_erasing = false;
}
if (old_pen) SetPen(old_pen);
if (old_brush) SetBrush(old_brush);
}
void wxLineShape::GetBoundingBoxMin(double *w, double *h)
{
double x1 = 10000;
double y1 = 10000;
double x2 = -10000;
double y2 = -10000;
wxNode *node = m_lineControlPoints->GetFirst();
while (node)
{
wxRealPoint *point = (wxRealPoint *)node->GetData();
if (point->x < x1) x1 = point->x;
if (point->y < y1) y1 = point->y;
if (point->x > x2) x2 = point->x;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -