在计算机集合中一个常用的功能就是判断交集的关系,点是否在直线上也是常用的一种判断关系。
点是否在直线上使用的算法有两个:
1.利用向量的叉积来计算:
a.构成线段的两点 A (x1,y2)和 B (x2,y2)和点C(x,y)
b.线段的向量(x2-x1,y2-y1)
c.p点和p2构成的向量:(x2-x,y2-y)
d.这两个向量的叉积等于:(x2-x1)(y2-y)-(x2-x)(y2-y1)
结果:等于0则者三点在同一条直线上
e.在利用C点是否在以点A和点B构成的矩形内,判断C点在线段AB上还是在AB的线段外
2.利用距离和来计算:
a.计算出线段AB的长度
b.计算出AC的长度和BC的长度
c.如果AB=AC+BC,则说明C点在线段AB上
先看一下实现的效果:
定义直线:
#pragma once
#include <QPointF>
#include <QColor>
//线段
class LineSeg {
public:
LineSeg();
~LineSeg();
inline void setStartPoint(QPointF pt) {
_ptStart = pt;
}
inline void setEndPoint(QPointF pt) {
_ptEnd = pt;
}
inline QPointF&getStartPoint() {
return _ptStart;
}
inline QPointF&getEndPoint() {
return _ptEnd;
}
inline void setLineColor(QColor color) {
_color = color;
}
inline QColor&getLineColor() {
return _color;
}
inline void setLineWidth(int w) {
_width = w;
}
inline int&getLineWidth() {
return _width;
}
QPointF getVector();//获取线段的向量
double getLength();//获取线段的长度
private:
QPointF _ptStart;//线段的起点
QPointF _ptEnd;//线段的终点
QColor _color = QColor(255, 0, 0);
int _width = 2;
};
#include "LineSeg.h"
LineSeg::LineSeg() {
}
LineSeg::~LineSeg() {
}
QPointF LineSeg::getVector() {
return QPointF(_ptEnd.x() - _ptStart.x(), _ptEnd.y() - _ptStart.y());
}
double LineSeg::getLength() {
return sqrt((_ptEnd.x() - _ptStart.x())*(_ptEnd.x() - _ptStart.x()) + (_ptEnd.y() - _ptStart.y())*(_ptEnd.y() - _ptStart.y()));
}
定义鼠标事件:
#pragma once
#include <QtWidgets/QMainWindow>
#include <QVector>
#include <QPen>
#include <QMenu>
#include "ui_QtGuiGeoPointInLine.h"
class QMouseEvent;
class LineSeg;
class QtGuiGeoPointInLine : public QMainWindow {
Q_OBJECT
public:
QtGuiGeoPointInLine(QWidget *parent = Q_NULLPTR);
private:
void mousePressEvent(QMouseEvent *event)override;
void mouseMoveEvent(QMouseEvent *event)override;
void mouseReleaseEvent(QMouseEvent *event)override;
void paintEvent(QPaintEvent *event)override;
bool isInLine(QPointF&pt,LineSeg*line);
private slots:
void slotActionPoint();
private:
Ui::QtGuiGeoPointInLineClass ui;
QPen _pen;
bool _lbtnDown = false;
QVector<LineSeg*> _lines;
LineSeg* _oneLine = nullptr;
QMenu* _menu = nullptr;
QAction* _action = nullptr;
bool _isStartPointInLine = false;
};
#include<QMouseEvent>
#include<QPainter>
#include<QRectF>
#include<QDebug>
#include "QtGuiGeoPointInLine.h"
#include "LineSeg.h"
QtGuiGeoPointInLine::QtGuiGeoPointInLine(QWidget *parent)
: QMainWindow(parent) {
ui.setupUi(this);
ui.centralWidget->setMouseTracking(true);
setMouseTracking(true);
_pen.setStyle(Qt::SolidLine);
_menu = new QMenu(QStringLiteral("测试"));
_action = new QAction(QStringLiteral("点"));
connect(_action, SIGNAL(triggered()), this, SLOT(slotActionPoint()));
_menu->addAction(_action);
ui.menuBar->addMenu(_menu);
}
void QtGuiGeoPointInLine::mousePressEvent(QMouseEvent *event) {
switch (event->button()) {
case Qt::LeftButton:
_lbtnDown = true;
_oneLine = new LineSeg;
_oneLine->setStartPoint(event->localPos());
_oneLine->setEndPoint(event->localPos());
break;
}
update();
}
void QtGuiGeoPointInLine::mouseMoveEvent(QMouseEvent *event) {
QPointF ptMove = event->localPos();
if (_lbtnDown) {
_oneLine->setEndPoint(ptMove);
}
if (_isStartPointInLine) {
for (auto v : _lines) {
if (isInLine(ptMove, v)) {
v->setLineWidth(5);
} else {
v->setLineWidth(2);
}
}
}
update();
}
void QtGuiGeoPointInLine::mouseReleaseEvent(QMouseEvent *event) {
switch (event->button()) {
case Qt::LeftButton:
_lbtnDown = false;
_oneLine->setEndPoint(event->pos());
_lines.push_back(_oneLine);
break;
}
update();
}
void QtGuiGeoPointInLine::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true); //设置渲染,启动反锯齿
int numLine = _lines.size();
for (auto v:_lines){
_pen.setColor(v->getLineColor());
_pen.setWidth(v->getLineWidth());
painter.setPen(_pen);
painter.drawLine(v->getStartPoint(), v->getEndPoint());
}
//绘制弹簧线条
if (_lbtnDown){
painter.drawLine(_oneLine->getStartPoint(), _oneLine->getEndPoint());
}
}
bool QtGuiGeoPointInLine::isInLine(QPointF&pt, LineSeg*line) {
#if 0//利用叉积计算
QPointF v1 = QPointF(pt.x() - line->getEndPoint().x(), pt.y() - line->getEndPoint().y());
QPointF v2 = line->getVector();
double v3 = v1.x()*v2.y() - v1.y()*v2.x();
qDebug() << "v1 = " << v1 << " v2 = " << v2 << " v3 = " << v3;
QRectF rect(line->getStartPoint(), line->getEndPoint());
if (abs(v3) < 1e-8 &&rect.contains(pt.toPoint())) {
return true;
}
return false;
#else//点如果在线段上,则点到线段两个端点之间的距离和等于线段的长度
double len1 = sqrt((line->getStartPoint().x() - pt.x())*(line->getStartPoint().x() - pt.x()) + (line->getStartPoint().y() - pt.y())*(line->getStartPoint().y() - pt.y()));
double len2 = sqrt((line->getEndPoint().x() - pt.x())*(line->getEndPoint().x() - pt.x()) + (line->getEndPoint().y() - pt.y())*(line->getEndPoint().y() - pt.y()));
double lenLine = line->getLength();
qDebug() << "line->getLength() = " << lenLine << " len1 + len2 = " << len1 + len2;
if (lenLine + 0.01 >= (len1+len2)&& (len1 + len2)>= lenLine) {
return true;
}else {
return false;
}
#endif
}
void QtGuiGeoPointInLine::slotActionPoint() {
_isStartPointInLine = !_isStartPointInLine;
if (_isStartPointInLine){
setCursor(Qt::CrossCursor);
} else {
setCursor(Qt::ArrowCursor);
}
}
主函数调用:
#include "QtGuiGeoPointInLine.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtGuiGeoPointInLine w;
w.show();
return a.exec();
}
aaa