File indexing completed on 2024-04-28 05:38:12

0001 /***************************************************************************
0002  *      Copyright (C) 2010 by Renaud Guezennec                             *
0003  *                                                                         *
0004  *                                                                         *
0005  *   rolisteam is free software; you can redistribute it and/or modify     *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 #include "pathitem.h"
0021 #include <QDebug>
0022 #include <QMenu>
0023 #include <QPainter>
0024 #include <QPainterPath>
0025 #include <QStyleOptionGraphicsItem>
0026 
0027 #include "controller/item_controllers/pathcontroller.h"
0028 #include "controller/item_controllers/visualitemcontroller.h"
0029 #include "controller/view_controller/vectorialmapcontroller.h"
0030 
0031 QPainterPath vectorToFullPath(const std::vector<QPointF>& points, qreal penWidth= 10., bool closeUp= false)
0032 {
0033     QPainterPath path;
0034     if(points.size() == 1)
0035         return path;
0036 
0037     bool first= true;
0038     std::vector<QPointF> topPoints;
0039     std::vector<QPointF> bottomPoints;
0040 
0041     int i= 0;
0042     for(auto current : points)
0043     {
0044         // QPointF current= points[i];
0045         QPointF lastPoint;
0046         QPointF nextPoint;
0047         if(points.size() > i + 1)
0048             nextPoint= points[i + 1];
0049         if(i > 0)
0050             lastPoint= points[i - 1];
0051 
0052         QLineF line(current, lastPoint);
0053         QLineF line2(current, nextPoint);
0054 
0055         if(nextPoint.isNull())
0056         {
0057             auto normal1= line.normalVector();
0058             normal1.setLength(-normal1.length());
0059             topPoints.push_back(normal1.pointAt(penWidth / (normal1.length() * 2)));
0060             bottomPoints.push_back(normal1.pointAt(-penWidth / (normal1.length() * 2)));
0061         }
0062         else if(lastPoint.isNull())
0063         {
0064             auto normal2= line2.normalVector();
0065 
0066             topPoints.push_back(normal2.pointAt(penWidth / (normal2.length() * 2)));
0067             bottomPoints.push_back(normal2.pointAt(-penWidth / (normal2.length() * 2)));
0068         }
0069         else
0070         {
0071             auto normal1= line.normalVector();
0072             normal1.setLength(-normal1.length());
0073             auto normal2= line2.normalVector();
0074 
0075             topPoints.push_back((normal1.pointAt(penWidth / (normal1.length() * 2))
0076                                  + normal2.pointAt(penWidth / (normal2.length() * 2)))
0077                                 / 2);
0078             bottomPoints.push_back((normal1.pointAt(-penWidth / (normal1.length() * 2))
0079                                     + normal2.pointAt(-penWidth / (normal2.length() * 2)))
0080                                    / 2);
0081         }
0082     }
0083 
0084     for(auto i= bottomPoints.rbegin(); i != bottomPoints.rend(); ++i)
0085     {
0086         topPoints.push_back(*i);
0087     }
0088 
0089     if(closeUp)
0090         topPoints.push_back(topPoints.at(0));
0091 
0092     for(const auto& point : topPoints)
0093     {
0094         if(first)
0095         {
0096             path.moveTo(point);
0097             first= false;
0098         }
0099         else
0100             path.lineTo(point);
0101     }
0102 
0103     return path;
0104 }
0105 
0106 PathItem::PathItem(vmap::PathController* ctrl) : VisualItem(ctrl), m_pathCtrl(ctrl)
0107 {
0108     if(!m_pathCtrl)
0109         return;
0110 
0111     connect(m_pathCtrl, &vmap::PathController::positionChanged, this,
0112             [this](int corner, QPointF pos)
0113             {
0114                 if(m_children.empty())
0115                     return;
0116 
0117                 if(corner == qBound(0, corner, m_children.size() - 1))
0118                     m_children[corner]->setPos(pos);
0119                 update();
0120             });
0121     connect(m_pathCtrl, &vmap::PathController::pointAdded, this, &PathItem::addChild);
0122 
0123     int i= 0;
0124     if(!m_pathCtrl->penLine())
0125     {
0126         for(auto point : m_pathCtrl->points())
0127             addChild(point, i);
0128     }
0129 
0130     m_closeAct= new QAction(tr("Close Path"), this);
0131     m_closeAct->setCheckable(true);
0132     connect(m_closeAct, &QAction::triggered, m_pathCtrl, &vmap::PathController::setClosed);
0133 
0134     m_fillAct= new QAction(tr("Fill Path"), this);
0135     m_fillAct->setCheckable(true);
0136     connect(m_fillAct, &QAction::triggered, m_pathCtrl, &vmap::PathController::setFilled);
0137 
0138     update();
0139 }
0140 
0141 QRectF PathItem::boundingRect() const
0142 {
0143     if(!m_pathCtrl)
0144         return {};
0145     return m_pathCtrl->rect();
0146 }
0147 
0148 QPainterPath PathItem::shape() const
0149 {
0150     if(!m_pathCtrl)
0151         return {};
0152     return vectorToFullPath(m_pathCtrl->points(), 10);
0153 }
0154 
0155 void PathItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
0156 {
0157     if(!m_pathCtrl)
0158         return;
0159     Q_UNUSED(option)
0160     Q_UNUSED(widget)
0161 
0162     setChildrenVisible(hasFocusOrChild());
0163 
0164     QPainterPath path= m_pathCtrl->path(); // vectorToPath(m_pathCtrl->points(), m_pathCtrl->closed());
0165 
0166     painter->save();
0167     auto pen= painter->pen();
0168     pen.setColor(m_pathCtrl->color());
0169     pen.setWidth(m_pathCtrl->penWidth());
0170     pen.setCapStyle(Qt::RoundCap);
0171     pen.setJoinStyle(Qt::RoundJoin);
0172     painter->setPen(pen);
0173     if(m_pathCtrl->filled())
0174     {
0175         path.setFillRule(Qt::OddEvenFill);
0176         painter->setBrush(pen.brush());
0177     }
0178     painter->setPen(pen);
0179     painter->drawPath(path);
0180     painter->restore();
0181 
0182     if(canBeMoved() && (option->state & QStyle::State_MouseOver || isSelected()))
0183     {
0184         painter->save();
0185         QPen pen= painter->pen();
0186         pen.setColor(isSelected() ? m_selectedColor : m_highlightColor);
0187         pen.setWidth(m_highlightWidth);
0188         painter->setPen(pen);
0189         QTransform scale= QTransform().scale(1.01, 1.01);
0190         painter->drawPath(scale.map(path));
0191         painter->restore();
0192     }
0193 }
0194 
0195 void PathItem::setNewEnd(const QPointF& p)
0196 {
0197     if(!m_pathCtrl)
0198         return;
0199 
0200     if(m_pathCtrl->penLine())
0201     {
0202         auto p0= m_pathCtrl->pointAt(m_pathCtrl->pointCount() - 1);
0203         m_pathCtrl->addPoint(p0 + p);
0204     }
0205     else
0206         m_pathCtrl->setCorner(p, m_pathCtrl->pointCount() - 1);
0207 }
0208 
0209 /*void PathItem::createActions()
0210 {
0211 
0212 }*/
0213 
0214 void PathItem::addActionContextMenu(QMenu& menu)
0215 {
0216     if(!m_pathCtrl)
0217         return;
0218     menu.addAction(m_closeAct);
0219     menu.addAction(m_fillAct);
0220     VisualItem::addActionContextMenu(menu);
0221 }
0222 void PathItem::addPoint(const QPointF& point)
0223 {
0224     if(!m_pathCtrl)
0225         return;
0226     m_pathCtrl->addPoint(point);
0227 }
0228 
0229 void PathItem::addChild(const QPointF& point, int i)
0230 {
0231     if(!m_pathCtrl)
0232         return;
0233     if(m_pathCtrl->penLine())
0234         return;
0235 
0236     ChildPointItem* tmp= new ChildPointItem(m_ctrl, i, this);
0237     tmp->setMotion(ChildPointItem::MOUSE);
0238     m_children.push_back(tmp);
0239     tmp->setPos(point);
0240     tmp->setPlacement(ChildPointItem::Center);
0241 }