先看绘制效果:
直线箭头
曲线箭头:
直线箭头
#pragma once
#include <QVector>
#include <QPointF>
#include "DrawGeoBase.h"
//当前标绘要素 - 直箭头
class DrawGeoStraightArrow :public DrawGeoBase {
public:
DrawGeoStraightArrow();
~DrawGeoStraightArrow();
virtual void draw(QPainter* p)override;
virtual void mousePress(QMouseEvent * event)override;
virtual void mouseRelease(QMouseEvent *event)override;
virtual void mouseMove(QMouseEvent * event)override;
protected:
int _countOfLeftClick = 0;//鼠标左键点击的次数
QPointF _arrowBottomPt, _arrowTopPt;
};
#include "DrawGeoStraightArrow.h"
#include <QPainter>
#include <QMouseEvent>
#include <QDateTime>
#include "CoordSysManager\CoordSysManager.h"
#include "CfgProp\GeoStraightArrowProp.h"
#include "UserDefineDraw\CfgProp\UserGeoCfgManager.h"
#include "FileStorage\StorageStraightArrowGeo.h"
#include "UserDefineDraw\Model\UserDefineDataManager.h"
#include "InterfaceImpl\IMap2DUpdate.h"
#include "UI\CurrentDrawFinishManager.h"
#include "HCore\GlobalUse.h"
DrawGeoStraightArrow::DrawGeoStraightArrow() {
}
DrawGeoStraightArrow::~DrawGeoStraightArrow() {
}
void DrawGeoStraightArrow::draw(QPainter* p) {
p->setRenderHint(QPainter::Antialiasing, true);
GeoStraightArrowProp*straightProp = dynamic_cast<GeoStraightArrowProp*>(UserGeoCfgMgr()->getGeoProp(USER_DRAW_STRAIGHT_ARROW));
if (straightProp != nullptr) {
p->setPen(QPen(straightProp->getLineColor(), straightProp->getLineWidth(), (Qt::PenStyle)straightProp->getLineType()));
if (straightProp->getUseFillColor()) {
p->setBrush(straightProp->getFillColor());
}
}
if (_countOfLeftClick == 0){
return;
}
QPointF ptButtom;
CoordInst()->lonlat2Screen(_arrowBottomPt.x(), _arrowBottomPt.y(), ptButtom);
QVector<QPointF> ptList;
QLineF arrowLine(ptButtom, _arrowTopPt);
#if 0
p->drawLine(arrowLine);
#endif
//底部左边
QLineF perpendLine1 = arrowLine.normalVector();
perpendLine1.setLength(perpendLine1.length() * straightProp->getArrowBottomLengthScale());
#if 0
p->drawLine(perpendLine1);
#endif
ptList.push_back(perpendLine1.p2());
if (straightProp->getArrowEndType()){//箭头燕尾
ptList.push_back(arrowLine.pointAt(0.2));
} else {
ptList.push_back(perpendLine1.p1());
}
//底部右边
perpendLine1.setAngle(arrowLine.angle() - 90.0);
#if 0
p->drawLine(perpendLine1);
#endif
ptList.push_back(perpendLine1.p2());
//左箭头
QLineF leftArrow;
leftArrow.setP1(_arrowTopPt);
leftArrow.setLength(arrowLine.length() / 8.0);
leftArrow.setAngle(QLineF(_arrowTopPt, ptButtom).angle() - straightProp->getArrowAngle() / 2);
#if 0
p->drawLine(leftArrow);
#endif
//左箭头回线
QLineF leftArrowBack;
leftArrowBack.setP1(leftArrow.p2());
leftArrowBack.setLength(leftArrow.length() / 3.0);
leftArrowBack.setAngle(leftArrow.angle() - 30.0 + 180);
#if 0
p->drawLine(leftArrowBack);
#endif
//右箭头
QLineF rightArrow;
rightArrow.setP1(_arrowTopPt);
rightArrow.setLength(arrowLine.length() / 8.0);
rightArrow.setAngle(QLineF(_arrowTopPt, ptButtom).angle() + straightProp->getArrowAngle() / 2);
#if 0
p->drawLine(rightArrow);
#endif
//右箭头回线
QLineF rightArrowBack;
rightArrowBack.setP1(rightArrow.p2());
rightArrowBack.setLength(rightArrow.length() / 3.0);
rightArrowBack.setAngle(rightArrow.angle() + 30.0 + 180);
#if 0
p->drawLine(rightArrowBack);
#endif
ptList.push_back(rightArrowBack.p2());
ptList.push_back(rightArrowBack.p1());
ptList.push_back(_arrowTopPt);
ptList.push_back(leftArrowBack.p1());
ptList.push_back(leftArrowBack.p2());
//绘制多边形
p->drawPolygon(ptList);
}
void DrawGeoStraightArrow::mousePress(QMouseEvent * event) {
if (event->button() == Qt::LeftButton) {
QPointF pt = event->pos();
_countOfLeftClick++;
if (_countOfLeftClick == 1) {
double lon = 0.0, lat = 0.0;
CoordInst()->screen2LonLat(pt.x(), pt.y(), lon, lat);
_arrowBottomPt = QPointF(lon, lat);
_arrowTopPt = pt;
} else if (_countOfLeftClick == 2) {
_countOfLeftClick = 0;
double lon = 0.0, lat = 0.0;
CoordInst()->screen2LonLat(pt.x(), pt.y(), lon, lat);
_arrowTopPt = QPointF(lon, lat);
GeoStraightArrowProp*straightArrowProp = dynamic_cast<GeoStraightArrowProp*>(UserGeoCfgMgr()->getGeoProp(USER_DRAW_STRAIGHT_ARROW));
StraightArrowGeo* straightArrow = new StorageStraightArrowGeo;
straightArrow->setGeoID(QDateTime::currentMSecsSinceEpoch());//设置ID
straightArrow->setGeoName(QStringLiteral("直箭头"));//设置名称
straightArrow->setStraightArrowProp(straightArrowProp);//保存直线箭头属性
straightArrow->setStraightArrowBottomPoint(UserGeoPoint(_arrowBottomPt.x(), _arrowBottomPt.y(), 0.0));
straightArrow->setStraightArrowTopPoint(UserGeoPoint(_arrowTopPt.x(), _arrowTopPt.y(), 0.0));
UserDefineDataMgr()->addUseGeo(straightArrow);
GlobalUseInst()->load2DMapUpdate()->update2DMap();
}
}
}
void DrawGeoStraightArrow::mouseRelease(QMouseEvent *event) {
if (event->button() == Qt::RightButton) {
CurrentDrawFinishMgr()->currentFinish(USER_DRAW_STRAIGHT_ARROW);
} else if (event->button() == Qt::MidButton) {//删除直线箭头
QVector<BaseGeo*>&geoList = UserDefineDataMgr()->getUseGeoList();
int num = geoList.size();
for (int i = num - 1; i >= 0; i--) {
BaseGeo* geo = geoList.at(i);
if (geo->getUserDrawDataType() == USER_DRAW_STRAIGHT_ARROW) {
UserDefineDataMgr()->removeUseGeo(geo);
break;
} else {
break;
}
}
}
}
void DrawGeoStraightArrow::mouseMove(QMouseEvent * event) {
GlobalUseInst()->load2DMapUpdate()->setCursor(Qt::CrossCursor);
if (_countOfLeftClick == 1) {
_arrowTopPt = event->pos();
}
}
曲线箭头
#pragma once
#include "DrawGeoBase.h"
#include <vector>
#include <QPointF>
#include <QLineF>
#include <QPainterPath>
//当前绘制的几何图形 - 路径箭头
class DrawGeoPathArrow :public DrawGeoBase {
public:
DrawGeoPathArrow();
~DrawGeoPathArrow();
virtual void draw(QPainter* p)override;
virtual void mousePress(QMouseEvent * event)override;
virtual void mouseRelease(QMouseEvent *event)override;
virtual void mouseMove(QMouseEvent * event)override;
private:
std::vector<QPointF> calculateControlPoints(const std::vector<QPointF> &points);
std::vector<qreal> firstControlPoints(const std::vector<qreal>& vector);
QPainterPath drawArrowPath(QPainter* p,std::vector<QPointF>&ptList);
std::vector<QPointF> drawArrow(QPainter* p, QLineF&line , QLineF&lineBottom);
private:
std::vector<QPointF*> _pathList;
QPointF* _currentPoint = new QPointF;//当前鼠标点
};
#include "DrawGeoPathArrow.h"
#include <QPainter>
#include <QMouseEvent>
#include <QDateTime>
#include "CoordSysManager\CoordSysManager.h"
#include "CfgProp\GeoPathArrowProp.h"
#include "UserDefineDraw\CfgProp\UserGeoCfgManager.h"
#include "FileStorage/StoragePathArrowGeo.h"
#include "UserDefineDraw\Model\UserDefineDataManager.h"
#include "InterfaceImpl\IMap2DUpdate.h"
#include "UI\CurrentDrawFinishManager.h"
#include "HCore\GlobalUse.h"
#define SEG_NUM 20
DrawGeoPathArrow::DrawGeoPathArrow() {
}
DrawGeoPathArrow::~DrawGeoPathArrow() {
}
void DrawGeoPathArrow::draw(QPainter* p) {
p->setRenderHint(QPainter::Antialiasing, true);
GeoPathArrowProp*pathArrowProp = dynamic_cast<GeoPathArrowProp*>(UserGeoCfgMgr()->getGeoProp(USER_DRAW_PATH_ARROW));
if (pathArrowProp != nullptr) {
QPen pen;
pen.setColor(pathArrowProp->getLineColor());
pen.setWidth(pathArrowProp->getLineWidth());
pen.setStyle((Qt::PenStyle)pathArrowProp->getLineType());
p->setPen(pen);
}
p->setBrush(QBrush(pathArrowProp->getFillColor()));
//箭头内部主曲线
QPainterPath mainPathArrow;
int num = _pathList.size();
if (num <= 2){
return;
}
std::vector<QPointF> screenPtList;
for (int i = 0; i < num - 1; i++) {
QPoint pt;
CoordInst()->lonlat2Screen(_pathList.at(i)->x(), _pathList.at(i)->y(), pt);
screenPtList.push_back(pt);
}
//保存当前鼠标点
screenPtList.push_back(QPointF(_pathList[num-1]->x(), _pathList[num - 1]->y()));
std::vector<QPointF> controlPoints = calculateControlPoints(screenPtList);
mainPathArrow.moveTo(screenPtList.at(0));
p->drawEllipse(screenPtList.at(0), 3, 3);
for (int i = 0; i < num -1; i++) {
QPointF point = screenPtList.at(i + 1);
p->drawEllipse(point,3,3);
mainPathArrow.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point);
#if 0
//绘制控制点
p->drawEllipse(controlPoints[2 * i], 3, 3);
p->drawEllipse(controlPoints[2 * i + 1], 3, 3);
#endif
}
#if 0
//绘制主曲线
p->drawPath(pathArrow);
#endif
//封闭箭头曲线
QPainterPath _arrowPath;
_arrowPath.setFillRule(Qt::OddEvenFill);
//绘制中间的点与路径垂直的直线
std::vector<QPointF> leftPath;
std::vector<QPointF> rightPath;
//把中心主曲线分成20段,每一都要截取垂线,最后一个十分之一作为箭头绘制
QLineF lineBottom1;
QLineF lineBottom2;
for (int i = 0; i <= 19; i++) {
QPointF pt = mainPathArrow.pointAtPercent(i / 20.0);
double angle = mainPathArrow.angleAtPercent(i / 20.0);
lineBottom1.setP1(pt);
lineBottom1.setAngle(angle + 90);
double scale = 0.03 - i / (20.0 * 50);
lineBottom1.setLength(mainPathArrow.length()*scale);
if (i == 0) {
#if 0
p->drawLine(lineBottom1);
#endif
}
leftPath.push_back(lineBottom1.p2());
lineBottom2 = lineBottom1;
lineBottom2.setAngle(lineBottom1.angle() + 180);
if (i == 0) {
#if 0
p->drawLine(lineBottom2);
#endif
}
rightPath.push_back(lineBottom2.p2());
}
QPainterPath pathRight = drawArrowPath(p, rightPath);
_arrowPath.addPath(pathRight);
QPainterPath pathLeft = drawArrowPath(p, leftPath);
//绘制箭头 - 路径的最后一个十分之一
QLineF line;
line.setP2(mainPathArrow.pointAtPercent(0.90));
line.setP1(mainPathArrow.pointAtPercent(1.0));
QLineF lineBottom;//箭头底部
lineBottom.setP1(lineBottom1.p2());
lineBottom.setP2(lineBottom2.p2());
std::vector<QPointF> ptArrowList = drawArrow(p, line, lineBottom);
int arrowNum = ptArrowList.size();
for (int i = 0; i < arrowNum;i++){
_arrowPath.lineTo(ptArrowList.at(i));
}
_arrowPath.connectPath(pathLeft.toReversed());
_arrowPath.closeSubpath();
p->drawPath(_arrowPath);
}
void DrawGeoPathArrow::mousePress(QMouseEvent * event) {
QPointF pt = event->pos();
if (event->button() == Qt::LeftButton) {
double lon = 0.0, lat = 0.0;
CoordInst()->screen2LonLat(pt.x(), pt.y(), lon, lat);
QPointF* pathPoint = new QPointF(lon, lat);
if (_pathList.size() < 2) {
_pathList.push_back(pathPoint);
if (_pathList.size() == 2) {//保存当前点
_pathList.push_back(_currentPoint);
}
} else {
_pathList.pop_back();//删除当前点
_pathList.push_back(pathPoint);//保存鼠标点
_pathList.push_back(_currentPoint);//保存当前点
}
} else if (event->button() == Qt::RightButton) {//右键添加
int num = _pathList.size();
if (num <= 2 ){
for (int i = 0; i < num;i++){
QPointF* ptGeo = _pathList.at(i);
delete ptGeo;
}
_pathList.clear();
return;
}
GeoPathArrowProp*straightArrowProp = dynamic_cast<GeoPathArrowProp*>(UserGeoCfgMgr()->getGeoProp(USER_DRAW_PATH_ARROW));
PathArrowGeo* pathArrow = new StoragePathArrowGeo;
pathArrow->setGeoID(QDateTime::currentMSecsSinceEpoch());//设置ID
pathArrow->setGeoName(QStringLiteral("进攻箭头"));//设置名称
pathArrow->setPathArrowProp(straightArrowProp);//保存直线箭头属性
for (int i = 0; i < num - 1; i++) {
QPointF* ptGeo = _pathList.at(i);
pathArrow->addPathArrowPoint(UserGeoPoint(ptGeo->x(), ptGeo->y(), 0.0));
//删除数据
delete ptGeo;
}
_pathList.clear();
//保存右键点击的点
double lon = 0.0, lat = 0.0;
CoordInst()->screen2LonLat(pt.x(), pt.y(), lon, lat);
pathArrow->addPathArrowPoint(UserGeoPoint(lon, lat, 0.0));
UserDefineDataMgr()->addUseGeo(pathArrow);
GlobalUseInst()->load2DMapUpdate()->update2DMap();
}
}
void DrawGeoPathArrow::mouseRelease(QMouseEvent *event) {
if (event->button() == Qt::MidButton) {
_pathList.pop_back();//移除当前点
_pathList.pop_back();//移除最后一个点
_pathList.push_back(_currentPoint);//添加当前点
}
}
void DrawGeoPathArrow::mouseMove(QMouseEvent * event) {
GlobalUseInst()->load2DMapUpdate()->setCursor(Qt::CrossCursor);
QPointF pt = event->pos();
_currentPoint->setX(pt.x());
_currentPoint->setY(pt.y());
}
std::vector<QPointF> DrawGeoPathArrow::calculateControlPoints(const std::vector<QPointF> &points) {
std::vector<QPointF> controlPoints;
controlPoints.resize(points.size() * 2 - 2);
int n = points.size() - 1;
if (n == 1) {
//for n==1
controlPoints[0].setX((2 * points[0].x() + points[1].x()) / 3);
controlPoints[0].setY((2 * points[0].y() + points[1].y()) / 3);
controlPoints[1].setX(2 * controlPoints[0].x() - points[0].x());
controlPoints[1].setY(2 * controlPoints[0].y() - points[0].y());
return controlPoints;
}
std::vector<qreal> vector;
vector.resize(n);
vector[0] = points[0].x() + 2 * points[1].x();
for (int i = 1; i < n - 1; ++i) {
vector[i] = 4 * points[i].x() + 2 * points[i + 1].x();
}
vector[n - 1] = (8 * points[n - 1].x() + points[n].x()) / 2.0;
std::vector<qreal> xControl = firstControlPoints(vector);
vector[0] = points[0].y() + 2 * points[1].y();
for (int i = 1; i < n - 1; ++i) {
vector[i] = 4 * points[i].y() + 2 * points[i + 1].y();
}
vector[n - 1] = (8 * points[n - 1].y() + points[n].y()) / 2.0;
std::vector<qreal> yControl = firstControlPoints(vector);
for (int i = 0, j = 0; i < n; ++i, ++j) {
controlPoints[j].setX(xControl[i]);
controlPoints[j].setY(yControl[i]);
j++;
if (i < n - 1) {
controlPoints[j].setX(2 * points[i + 1].x() - xControl[i + 1]);
controlPoints[j].setY(2 * points[i + 1].y() - yControl[i + 1]);
} else {
controlPoints[j].setX((points[n].x() + xControl[n - 1]) / 2);
controlPoints[j].setY((points[n].y() + yControl[n - 1]) / 2);
}
}
return controlPoints;
}
std::vector<qreal> DrawGeoPathArrow::firstControlPoints(const std::vector<qreal>& vector) {
std::vector<qreal> result;
int count = vector.size();
result.resize(count);
result[0] = vector[0] / 2.0;
std::vector<qreal> temp;
temp.resize(count);
temp[0] = 0;
qreal b = 2.0;
for (int i = 1; i < count; i++) {
temp[i] = 1 / b;
b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
result[i] = (vector[i] - result[i - 1]) / b;
}
for (int i = 1; i < count; i++) {
result[count - i - 1] -= temp[count - i] * result[count - i];
}
return result;
}
QPainterPath DrawGeoPathArrow::drawArrowPath(QPainter* p, std::vector<QPointF>&ptList) {
QPainterPath path;
std::vector<QPointF> controlPoints = calculateControlPoints(ptList);
path.moveTo(ptList.at(0));
int num = ptList.size();
for (int i = 0; i < num - 1; i++) {
QPointF point = ptList.at(i + 1);
#if 0
p->drawEllipse(point, 3, 3);
#endif
path.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point);
}
#if 0
p->drawPath(path);
#endif
return path;
}
std::vector<QPointF> DrawGeoPathArrow::drawArrow(QPainter* p, QLineF&arrowLine, QLineF&lineBottom) {
#if 0
p->drawLine(arrowLine);
p->drawLine(lineBottom);
#endif
std::vector<QPointF> ptList;
//左箭头
QLineF leftArrow;
leftArrow.setP1(arrowLine.p1());
leftArrow.setLength(arrowLine.length() / 2.0);
leftArrow.setAngle(arrowLine.angle() - 30);
#if 0
p->drawLine(leftArrow);
#endif
//左箭头回线
QLineF leftArrowBack;
leftArrowBack.setP1(leftArrow.p2());
leftArrowBack.setLength(leftArrow.length() / 3.0);
leftArrowBack.setAngle(leftArrow.angle() - 30.0 + 180);
#if 0
p->drawLine(leftArrowBack);
#endif
//右箭头
QLineF rightArrow;
rightArrow.setP1(arrowLine.p1());
rightArrow.setLength(arrowLine.length() / 2.0);
rightArrow.setAngle(arrowLine.angle() + 30);
#if 0
p->drawLine(rightArrow);
#endif
//右箭头回线
QLineF rightArrowBack;
rightArrowBack.setP1(rightArrow.p2());
rightArrowBack.setLength(rightArrow.length() / 3.0);
rightArrowBack.setAngle(rightArrow.angle() + 30.0 + 180);
#if 0
p->drawLine(rightArrowBack);
#endif
//左边线
QLineF leftSide(lineBottom.p1(), leftArrowBack.p2());
#if 0
p->drawLine(leftSide);
#endif
//右边线
QLineF rightSide(lineBottom.p2(), rightArrowBack.p2());
#if 0
p->drawLine(rightSide);
#endif
ptList.push_back(rightSide.p1());
ptList.push_back(rightSide.p2());
ptList.push_back(rightArrowBack.p1());
ptList.push_back(arrowLine.p1());
ptList.push_back(leftArrow.p2());
ptList.push_back(leftArrowBack.p2());
ptList.push_back(leftSide.p1());
return ptList;
}