Qt绘制直箭头和平滑的曲线箭头

137 篇文章 44 订阅

先看绘制效果:
直线箭头
在这里插入图片描述
曲线箭头:
在这里插入图片描述

#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;
}

aaa

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wb175208

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值