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 }