%% options copyright owner = Dirk Krause copyright year = 2018-xxxx SPDX-License-Identifier: BSD-3-Clause %% module /* This module contains the methods to draw the arrowheads. */ #include "wxdkdraw.h" #ifndef WXDKDRAWAPPH_INCLUDED #include "WxdkdrawApp.h" #endif #ifndef WXDKDRAWFRAME_H_INCLUDED #include "WxdkdrawFrame.h" #endif $!trace-include void WxdkdrawFrame::Control::ArrowVertices( dk4_gra_point_t *pt, Wxd_drawing_t const *pDrw, Wxd_object_t const *pObj, double xpos, double ypos, double rot, uint8_t atype, uint8_t alength, uint8_t awidth ) { double lw; /* Line width */ double l; /* Arrowhead length */ double w; /* Arrowhead width */ double w2; /* Half arrowhead width */ size_t szUsed = 0; /* Number of elements used in pt array */ size_t i; /* Traverse pt array */ $? "+ ArrowVertices" if ((1 <= atype) && (29 >= atype)) { lw = (double)(pObj->lw) * (double)(pDrw->baselw); l = lw * ((double)alength); w = lw * ((double)awidth); w2 = w / 2.0; switch (atype) { case 1: case 2: case 3: { szUsed = 3; pt[0].x = 0.0 - l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0; pt[2].x = 0.0 - l; pt[2].y = 0.0 - w2; } break; case 4: case 5: { szUsed = 4; pt[0].x = 0.0 - l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0; pt[2].x = 0.0 - l; pt[2].y = 0.0 - w2; pt[3].x = 0.0 - l; pt[3].y = 0.0; } break; case 6: case 7: case 8: case 9: { szUsed = 4; pt[0].x = 0.0 - l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0; pt[2].x = 0.0 - l; pt[2].y = 0.0 - w2; pt[3].x = 0.0 - 0.75 * l; pt[3].y = 0.0; } break; case 10: case 11: case 12: case 13: { szUsed = 4; pt[0].x = 0.0 - 0.75 * l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0; pt[2].x = 0.0 - 0.75 * l; pt[2].y = 0.0 - w2; pt[3].x = 0.0 - l; pt[3].y = 0.0; } break; case 14: case 15: case 16: case 17: { szUsed = 4; pt[0].x = 0.0 - 0.5 * l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0; pt[2].x = 0.0 - 0.5 * l; pt[2].y = 0.0 - w2; pt[3].x = 0.0 - l; pt[3].y = 0.0; } break; case 18: case 19: case 22: { szUsed = 4; pt[0].x = 0.0 - l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = w2; pt[2].x = 0.0; pt[2].y = 0.0 - w2; pt[3].x = 0.0 - l; pt[3].y = 0.0 - w2; } break; case 20: case 21: { szUsed = 6; pt[0].x = 0.0 - l; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = w2; pt[2].x = 0.0; pt[2].y = 0.0 - w2; pt[3].x = 0.0 - l; pt[3].y = 0.0 - w2; pt[4].x = 0.0; pt[4].y = 0.0; pt[5].x = 0.0 - l; pt[5].y = 0.0; } break; case 23: case 24: { szUsed = 3; pt[0].x = 0.0; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0 - w2; pt[2].x = 0.0 - l; pt[2].y = 0.0; } break; case 25: case 26: { szUsed = 4; pt[0].x = 0.0; pt[0].y = w2; pt[1].x = 0.0 - l; pt[1].y = 0.0; pt[2].x = 0.0; pt[2].y = 0.0 - w2; pt[3].x = 0.0; pt[3].y = 0.0; } break; case 27: case 28: { szUsed = 1; pt[0].x = 0.0 - (l / 2.0); pt[0].y = 0.0; } break; case 29: { szUsed = 2; pt[0].x = 0.0; pt[0].y = w2; pt[1].x = 0.0; pt[1].y = 0.0 - w2; } break; } /* Finally: - Rotate all points - Shift points to position - Translate WXD coordinates to pixel coordinates */ for (i = 0; i < szUsed; i++) { dk4gratool_rotate_point(&(pt[i]), rot); pt[i].x += xpos; pt[i].y += ypos; pt[i].x = ConvX(pt[i].x); pt[i].y = ConvY(pt[i].y); } } $? "- ArrowVertices" } void WxdkdrawFrame::Control::DrawOneArrowhead( wxGraphicsContext *gc, Wxd_drawing_t const *pDrw, Wxd_object_t const *pObj, int const *sc, double xpos, double ypos, double rot, uint8_t atype, uint8_t alength, uint8_t awidth ) { dk4_gra_point_t pt[8]; /* Vertices coordinates in pixels */ int plw; /* Pen line width */ wxColour cs(sc[0], sc[1], sc[2]); /* Stroke colour */ wxColour cw(255, 255, 255); /* White colour */ $? "+ DrawOneArrowhead" $? ". xpos = %g", xpos $? ". ypos = %g", ypos $? ". angle = %g", rot $? ". type = %d", (int)atype $? ". length = %d", (int)alength $? ". width = %d", (int)awidth plw = LineWidthForObject(pDrw, pObj); wxPen pen(cs, plw, wxPENSTYLE_SOLID); pen.SetCap(wxCAP_ROUND); pen.SetJoin(wxJOIN_MITER); ArrowVertices(pt, pDrw, pObj, xpos, ypos, rot, atype, alength, awidth); switch (atype) { case 1: { wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 2: case 23: { wxBrush bru(cw); wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 3: case 24: { wxBrush bru(cs); wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 4: case 8: case 12: case 16: case 25: { wxBrush bru(cs); wxBrush brv(cw); wxGraphicsPath p = gc->CreatePath(); wxGraphicsPath q = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); q.MoveToPoint(pt[0].x, pt[0].y); q.AddLineToPoint(pt[1].x, pt[1].y); q.AddLineToPoint(pt[3].x, pt[3].y); q.AddLineToPoint(pt[0].x, pt[0].y); q.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(brv); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(q); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 5: case 9: case 13: case 17: case 26: { wxBrush bru(cs); wxBrush brv(cw); wxGraphicsPath p = gc->CreatePath(); wxGraphicsPath q = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); q.MoveToPoint(pt[3].x, pt[3].y); q.AddLineToPoint(pt[1].x, pt[1].y); q.AddLineToPoint(pt[2].x, pt[2].y); q.AddLineToPoint(pt[3].x, pt[3].y); q.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(brv); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(q); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 6: case 10: case 14: case 18: { wxBrush bru(cw); wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 7: case 11: case 15: case 19: { wxBrush bru(cs); wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 20: { wxBrush bru(cs); wxBrush brv(cw); wxGraphicsPath p = gc->CreatePath(); wxGraphicsPath q = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); q.MoveToPoint(pt[0].x, pt[0].y); q.AddLineToPoint(pt[1].x, pt[1].y); q.AddLineToPoint(pt[4].x, pt[4].y); q.AddLineToPoint(pt[5].x, pt[5].y); q.AddLineToPoint(pt[0].x, pt[0].y); q.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(brv); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(q); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 21: { wxBrush bru(cs); wxBrush brv(cw); wxGraphicsPath p = gc->CreatePath(); wxGraphicsPath q = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); p.AddLineToPoint(pt[0].x, pt[0].y); p.CloseSubpath(); q.MoveToPoint(pt[3].x, pt[3].y); q.AddLineToPoint(pt[2].x, pt[2].y); q.AddLineToPoint(pt[4].x, pt[4].y); q.AddLineToPoint(pt[5].x, pt[5].y); q.AddLineToPoint(pt[3].x, pt[3].y); q.CloseSubpath(); gc->SetPen(wxNullPen); gc->SetBrush(brv); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(q); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 22: { wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); p.AddLineToPoint(pt[2].x, pt[2].y); p.AddLineToPoint(pt[3].x, pt[3].y); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 27: { wxBrush bru(cw); wxGraphicsPath p = gc->CreatePath(); p.AddCircle( pt[0].x, pt[0].y, ConvDist( ((double)(pObj->lw) * (double)(pDrw->baselw) * ((double)alength)) / 2.0 ) ); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 28: { wxBrush bru(cs); wxGraphicsPath p = gc->CreatePath(); p.AddCircle( pt[0].x, pt[0].y, ConvDist( ((double)(pObj->lw) * (double)(pDrw->baselw) * ((double)alength)) / 2.0 ) ); gc->SetPen(wxNullPen); gc->SetBrush(bru); gc->FillPath(p); gc->SetBrush(wxNullBrush); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; case 29: { wxGraphicsPath p = gc->CreatePath(); p.MoveToPoint(pt[0].x, pt[0].y); p.AddLineToPoint(pt[1].x, pt[1].y); gc->SetPen(pen); gc->StrokePath(p); gc->SetPen(wxNullPen); } break; } $? "- DrawOneArrowhead" } void WxdkdrawFrame::Control::AddArrowheads( wxGraphicsContext *gc, Wxd_drawing_t const *pDrw, Wxd_object_t const *pObj, int const *sc, Wxd_point_t const *pShift ) { dk4_xsp_2d_t xsp; /* 2D X-spline */ double pa[2]; /* Point A */ double pb[2]; /* Point B */ double rot; /* Rotation counterclockwise in radians */ int32_t i32ShiftX = 0L; /* X shift distance */ int32_t i32ShiftY = 0L; /* Y shift distance */ $? "+ AddArrowheads" if (NULL != pShift) { i32ShiftX = pShift->x; i32ShiftY = pShift->y; } if ((uint8_t)0 != pObj->aft) { switch (pObj->ot) { case WXD_OT_POLYLINE : { if ((NULL != (pObj->det).p.p) && (1 < (pObj->det).p.n)) { rot = atan2( ( (double)(((pObj->det).p.p)[(pObj->det).p.n - 1].y) - (double)(((pObj->det).p.p)[(pObj->det).p.n - 2].y) ), ( (double)(((pObj->det).p.p)[(pObj->det).p.n - 1].x) - (double)(((pObj->det).p.p)[(pObj->det).p.n - 2].x) ) ); if (0 != dk4ma_is_finite(rot)) { DrawOneArrowhead( gc, pDrw, pObj, sc, ( (double)(((pObj->det).p.p)[(pObj->det).p.n-1].x) + (double)i32ShiftX ), ( (double)(((pObj->det).p.p)[(pObj->det).p.n-1].y) + (double)i32ShiftY ), rot, pObj->aft, pObj->afl, pObj->afw ); } } } break; case WXD_OT_O_SPLINE : { if ((NULL != (pObj->det).s.p) && (2 <= (pObj->det).s.n)) { dk4xsp2d_reset(&xsp); dk4xsp2d_reset_points(&xsp); dk4xsp2d_set_point( &xsp, ((pObj->det).s.p)[(pObj->det).s.n - 3].x, ((pObj->det).s.p)[(pObj->det).s.n - 3].y, ((pObj->det).s.p)[(pObj->det).s.n - 3].s, 0 ); dk4xsp2d_set_point( &xsp, ((pObj->det).s.p)[(pObj->det).s.n - 2].x, ((pObj->det).s.p)[(pObj->det).s.n - 2].y, ((pObj->det).s.p)[(pObj->det).s.n - 2].s, 1 ); dk4xsp2d_set_point( &xsp, ((pObj->det).s.p)[(pObj->det).s.n - 1].x, ((pObj->det).s.p)[(pObj->det).s.n - 1].y, ((pObj->det).s.p)[(pObj->det).s.n - 1].s, 2 ); if (0 != dk4xsp2d_calculate_value(pb, &xsp, 1.0, NULL)) { if (0 != dk4xsp2d_calculate_value(pa,&xsp,0.95,NULL)) { rot = atan2((pb[1]-pa[1]), (pb[0]-pa[0])); if (0 != dk4ma_is_finite(rot)) { DrawOneArrowhead( gc, pDrw, pObj, sc, (pb[0] + (double)i32ShiftX), (pb[1] + (double)i32ShiftY), rot, pObj->aft, pObj->afl, pObj->afw ); } } } } } break; case WXD_OT_O_ARC : { if ((int8_t)0 != (pObj->det).a.d) { if ((int8_t)0 < (pObj->det).a.d) { rot = (pObj->det).a.b + M_PI_2; } else { rot = (pObj->det).a.b - M_PI_2; } DrawOneArrowhead( gc, pDrw, pObj, sc, ((double)((pObj->det).a.x3) + (double)i32ShiftX), ((double)((pObj->det).a.y3) + (double)i32ShiftY), rot, pObj->aft, pObj->afl, pObj->afw ); } else { if ( ((pObj->det).a.x3 != (pObj->det).a.x2) || ((pObj->det).a.y3 != (pObj->det).a.y2) ) { rot = atan2( ( (double)((pObj->det).a.y3) - (double)((pObj->det).a.y2) ), ( (double)((pObj->det).a.x3) - (double)((pObj->det).a.x2) ) ); if (0 != dk4ma_is_finite(rot)) { DrawOneArrowhead( gc, pDrw, pObj, sc, ((double)((pObj->det).a.x3)+(double)i32ShiftX), ((double)((pObj->det).a.y3)+(double)i32ShiftY), rot, pObj->aft, pObj->afl, pObj->afw ); } } } } break; } } if ((uint8_t)0 != pObj->abt) { switch (pObj->ot) { case WXD_OT_POLYLINE : { if ((NULL != (pObj->det).p.p) && (1 < (pObj->det).p.n)) { rot = atan2( ( (double)(((pObj->det).p.p)[0].y) - (double)(((pObj->det).p.p)[1].y) ), ( (double)(((pObj->det).p.p)[0].x) - (double)(((pObj->det).p.p)[1].x) ) ); if (0 != dk4ma_is_finite(rot)) { DrawOneArrowhead( gc, pDrw, pObj, sc, ( ((pObj->det).p.p)[0].x + i32ShiftX ), ( ((pObj->det).p.p)[0].y + i32ShiftY ), rot, pObj->abt, pObj->abl, pObj->abw ); } } } break; case WXD_OT_O_SPLINE : { if ((NULL != (pObj->det).s.p) && (2 <= (pObj->det).s.n)) { dk4xsp2d_reset(&xsp); dk4xsp2d_reset_points(&xsp); dk4xsp2d_set_point( &xsp, ((pObj->det).s.p)[0].x, ((pObj->det).s.p)[0].y, ((pObj->det).s.p)[0].s, 1 ); dk4xsp2d_set_point( &xsp, ((pObj->det).s.p)[1].x, ((pObj->det).s.p)[1].y, ((pObj->det).s.p)[1].s, 2 ); dk4xsp2d_set_point( &xsp, ((pObj->det).s.p)[2].x, ((pObj->det).s.p)[2].y, ((pObj->det).s.p)[2].s, 3 ); if (0 != dk4xsp2d_calculate_value(pb, &xsp, 0.0, NULL)) { if (0 != dk4xsp2d_calculate_value(pa,&xsp,0.05,NULL)) { rot = atan2((pb[1]-pa[1]), (pb[0]-pa[0])); if (0 != dk4ma_is_finite(rot)) { DrawOneArrowhead( gc, pDrw, pObj, sc, (pb[0] + (double)i32ShiftX), (pb[1] + (double)i32ShiftY), rot, pObj->abt, pObj->abl, pObj->abw ); } } } } } break; case WXD_OT_O_ARC : { if ((int8_t)0 != (pObj->det).a.d) { if ((int8_t)0 < (pObj->det).a.d) { rot = (pObj->det).a.a - M_PI_2; } else { rot = (pObj->det).a.a + M_PI_2; } DrawOneArrowhead( gc, pDrw, pObj, sc, ((double)((pObj->det).a.x1)+(double)i32ShiftX), ((double)((pObj->det).a.y1)+(double)i32ShiftY), rot, pObj->abt, pObj->abl, pObj->abw ); } else { if ( ((pObj->det).a.x1 != (pObj->det).a.x2) || ((pObj->det).a.y1 != (pObj->det).a.y2) ) { rot = atan2( ( (double)((pObj->det).a.y1) - (double)((pObj->det).a.y2) ), ( (double)((pObj->det).a.x1) - (double)((pObj->det).a.x2) ) ); if (0 != dk4ma_is_finite(rot)) { DrawOneArrowhead( gc, pDrw, pObj, sc, ((double)((pObj->det).a.x1)+(double)i32ShiftX), ((double)((pObj->det).a.y1)+(double)i32ShiftY), rot, pObj->aft, pObj->afl, pObj->afw ); } } } } break; } } $? "- AddArrowheads" } /* vim: set ai sw=4 ts=4 : */