C++
#include <QtGui>
static qreal percentageByPoint(const QPainterPath & path, const QPointF & p, qreal precision=0.5, qreal width=3.0){
qreal percentage = -1;
QPainterPathStroker stroker;
stroker.setWidth(width);
QPainterPath strokepath = stroker.createStroke(path);
if(strokepath.contains(p)){
std::vector<qreal> d;
qreal t=0.0;
while(t<=100.0){
d.push_back(QVector2D(p - path.pointAtPercent(t/100)).length());
t+= precision;
}
std::vector<qreal>::iterator result = std::min_element(d.begin(), d.end());
int j= std::distance(d.begin(), result);
percentage = j*precision;
}
return percentage;
}
int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
QPainterPath path;
path.moveTo( QPointF(10.00, -10.00) );
path.cubicTo(
QPointF(114.19, -10.00),
QPointF(145.80, -150.00),
QPointF(250.00, -150.00)
);
// User Clicked Point
QPointF p(187.00, -130.00);
qreal percentage = percentageByPoint(path, p);
qDebug() << percentage;
return 0;
}
python:
def percentageByPoint(path, point, precision = 0.5, width = 3.0):
percentage = -1.0
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(width)
strokepath = stroker.createStroke(path)
if strokepath.contains(point):
t = 0.0
d = []
while t <= 100.0:
d.append(QtGui.QVector2D(point - path.pointAtPercent(t / 100)).length())
t += precision
percentage = d.index(min(d)) * precision
return percentage
if __name__ == '__main__':
path = QtGui.QPainterPath()
path.moveTo(QtCore.QPointF(10.00, -10.00))
path.cubicTo(
QtCore.QPointF(114.19, -10.00),
QtCore.QPointF(145.80, -150.00),
QtCore.QPointF(250.00, -150.00)
)
point = QtCore.QPointF(187.00, -130.00)
percentage = percentageByPoint(path, point)
print(percentage)
Output:
76.5
Ok. i see. So it is not enough to actually test for mouse click on line http://stackoverflow.com/questions/26849632/see-if-a-point-lies-on-a-linevector,For some clarification, this is what I want: http://paste.ugfx.org/sores/7505d64a54e0/264c3e10630e.jpg The green line is the QPainterPath that should be returned by SchematicWire::shape()., Joel Bodenmann last edited by @mrjj @mrjj I don't think that that's going to help because the mouse-click-detection should be handled by the QGraphicsView/QGraphicsScene and for that they use QGraphcisItem::shape(). Reply Quote 0 2 Replies Last reply ,Thanks for linking the article - that might be handy in case of I really have to implement this manually. QPainter::strokePath won't work because I really need the resulting polygon to be returned by my shape() function.
As mentioned above you can use QPainterPathStroker. The problem is it will create tiny loops on the inside of the joints, so you need to simplify the path afterwards.
Sample code:
QVector<QPoint> points { QPoint(30,30), QPoint(100, 100), QPoint(200,50) };
QPainterPath basePath;
basePath.addPolygon(QPolygon(points));
QPainterPathStroker str;
str.setCapStyle(Qt::RoundCap);
str.setWidth(10.0);
QPainterPath resultPath = str.createStroke(basePath).simplified();
GetPath is where all the calculations takes place. It will return the QPainterPath object generated from the polygon points added to RoundedPolygon.,Quadratic Bezier curves have one control point. The theory says that a quadratic Bezier curve between points P0 and P2 with control point P1 is defined as follows:,QPainter’s method drawPath is used to draw a collection of lines and curves from QPainterPath object, which has to be given as parameter, with active pen and brush.,When travelling from point P1 to P2 in a straight line and by completing 30 percent of the distance, we can determine our location using the formula 0.7 • P1 + 0.3 • P2. In general, if we achieve a fraction of the full distance, and α = 1 denotes full distance, the current location is at (1 - α) • P1 + α • P2.
So let’s see the class declaration:
class RoundedPolygon: public QPolygon {
public: RoundedPolygon() {
SetRadius(10);
}
void SetRadius(unsigned int iRadius) {
m_iRadius = iRadius;
}
const QPainterPath & GetPath();
private: QPointF GetLineStart(int i) const;
QPointF GetLineEnd(int i) const;
float GetDistance(QPoint pt1, QPoint pt2) const;
private: QPainterPath m_path;
unsigned int m_iRadius;
};
Let’s see the implementation and I will start with the private methods:
float RoundedPolygon::GetDistance(QPoint pt1, QPoint pt2) const {
float fD = (pt1.x() - pt2.x()) * (pt1.x() - pt2.x()) +
(pt1.y() - pt2.y()) * (pt1.y() - pt2.y());
return sqrtf(fD);
}
Not much to explain here, the method returns the Euclidian distance between the given two points.
QPointF RoundedPolygon::GetLineStart(int i) const {
QPointF pt;
QPoint pt1 = at(i);
QPoint pt2 = at((i + 1) % count());
float fRat = m_uiRadius / GetDistance(pt1, pt2);
if (fRat > 0.5 f)
fRat = 0.5 f;
pt.setX((1.0 f - fRat) * pt1.x() + fRat * pt2.x());
pt.setY((1.0 f - fRat) * pt1.y() + fRat * pt2.y());
return pt;
}
And now, the main class method:
const QPainterPath & RoundedPolygon::GetPath() {
m_path = QPainterPath();
if (count() < 3) {
qWarning() << "Polygon should have at least 3 points!";
return m_path;
}
QPointF pt1;
QPointF pt2;
for (int i = 0; i < count(); i++) {
pt1 = GetLineStart(i);
if (i == 0)
m_path.moveTo(pt1);
else
m_path.quadTo(at(i), pt1);
pt2 = GetLineEnd(i);
m_path.lineTo(pt2);
}
// close the last corner
pt1 = GetLineStart(0);
m_path.quadTo(at(0), pt1);
return m_path;
}
Now it’s time to see how to use this class in practice.
QPixmap pix1(300, 200);
QPixmap pix2(300, 200);
pix1.fill(Qt::white);
pix2.fill(Qt::white);
QPainter P1( & pix1);
QPainter P2( & pix2);
P1.setRenderHints(QPainter::Antialiasing);
P2.setRenderHints(QPainter::Antialiasing);
P1.setPen(QPen(Qt::blue, 2));
P1.setBrush(Qt::red);
P2.setPen(QPen(Qt::blue, 2));
P2.setBrush(Qt::red);
RoundedPolygon poly;
poly << QPoint(147, 187) << QPoint(95, 187) <<
QPoint(100, 175) << QPoint(145, 165) << QPoint(140, 95) <<
QPoint(5, 85) << QPoint(5, 70) << QPoint(140, 70) << QPoint(135, 45) <<
QPoint(138, 25) << QPoint(145, 5) << QPoint(155, 5) << QPoint(162, 25) <<
QPoint(165, 45) << QPoint(160, 70) << QPoint(295, 70) << QPoint(295, 85) <<
QPoint(160, 95) << QPoint(155, 165) << QPoint(200, 175) <<
QPoint(205, 187) << QPoint(153, 187) << QPoint(150, 199);
P1.drawPolygon(poly);
P2.drawPath(poly.GetPath());
pix1.save("1.png");
pix2.save("2.png");