File indexing completed on 2024-05-05 07:51:43
0001 /*. 0002 SPDX-FileCopyrightText: 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com> 0003 SPDX-FileCopyrightText: 2014 Inge Wallin <inge@lysator.liu.se> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "softbodygraphics.h" 0009 0010 #include <stepcore/softbody.h> 0011 #include <stepcore/types.h> 0012 0013 #include "ui_create_softbody_items.h" 0014 0015 #include <float.h> 0016 0017 #include "worldmodel.h" 0018 #include "worldfactory.h" 0019 0020 #include <QDialog> 0021 #include <QDialogButtonBox> 0022 #include <QEvent> 0023 #include <QGraphicsScene> 0024 #include <QGraphicsSceneMouseEvent> 0025 #include <QItemSelectionModel> 0026 #include <QPainter> 0027 #include <QPushButton> 0028 #include <QRegularExpressionValidator> 0029 #include <QVBoxLayout> 0030 0031 #include <KLocalizedString> 0032 0033 0034 void SoftBodyCreator::start() 0035 { 0036 showMessage(MessageFrame::Information, 0037 i18n("Click on the scene to create a %1", classNameTr())); 0038 } 0039 0040 bool SoftBodyCreator::sceneEvent(QEvent* event) 0041 { 0042 QGraphicsSceneMouseEvent* mouseEvent = static_cast<QGraphicsSceneMouseEvent*>(event); 0043 0044 if (event->type() == QEvent::GraphicsSceneMousePress && 0045 mouseEvent->button() == Qt::LeftButton) { 0046 QPointF pos = mouseEvent->scenePos(); 0047 StepCore::Vector2d position = StepGraphicsItem::pointToVector(pos); 0048 0049 _worldModel->simulationPause(); 0050 _worldModel->beginMacro(i18n("Create %1", 0051 _worldModel->newItemName(_className))); 0052 0053 showMessage(MessageFrame::Information, 0054 i18n("Please fill in the parameters for %1", classNameTr())); 0055 0056 _item = _worldModel->createItem(_className); Q_ASSERT(_item != nullptr); 0057 0058 SoftBodyMenuHandler* menuHandler = 0059 new SoftBodyMenuHandler(_item, _worldModel, nullptr); 0060 menuHandler->createSoftBodyItems(position); 0061 menuHandler->deleteLater(); 0062 0063 _worldModel->endMacro(); 0064 0065 if (menuHandler->applied()) { 0066 showMessage(MessageFrame::Information, 0067 i18n("%1 named '%2' created", classNameTr(), _item->name()), 0068 MessageFrame::CloseButton | MessageFrame::CloseTimer); 0069 } 0070 else { 0071 delete _item; 0072 _item = nullptr; 0073 } 0074 0075 setFinished(); 0076 0077 return true; 0078 } 0079 0080 return false; 0081 } 0082 0083 void SoftBodyMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) 0084 { 0085 _createSoftBodyItemsUi = nullptr; 0086 _createSoftBodyItemsDialog = nullptr; 0087 //_confChanged = false; 0088 0089 // XXX: better icon 0090 // TODO: This will never work the way SoftBody is right now... 0091 //menu->addAction(QIcon::fromTheme("step_object_GasParticle"), i18n("Create items..."), this, SLOT(createSoftBodyItems())); 0092 //menu->addAction(QIcon::fromTheme("edit-clear"), i18n("Clear gas"), this, SLOT(clearGas())); 0093 menu->addSeparator(); 0094 ItemMenuHandler::populateMenu(menu, actions); 0095 } 0096 0097 inline StepCore::SoftBody* SoftBodyMenuHandler::softBody() const 0098 { 0099 return static_cast<StepCore::SoftBody*>(_object); 0100 } 0101 0102 void SoftBodyMenuHandler::clearSoftBody() 0103 { 0104 // _worldModel->simulationPause(); 0105 } 0106 0107 void SoftBodyMenuHandler::createSoftBodyItems(const StepCore::Vector2d& pos) 0108 { 0109 if(_worldModel->isSimulationActive()) 0110 _worldModel->simulationStop(); 0111 0112 _createSoftBodyItemsDialog = new QDialog(); // XXX: parent? 0113 0114 _createSoftBodyItemsDialog->setWindowTitle(i18nc("@title:window", "Create Soft Body Items")); 0115 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0116 QWidget *mainWidget = new QWidget(); 0117 QVBoxLayout *mainLayout = new QVBoxLayout; 0118 _createSoftBodyItemsDialog->setLayout(mainLayout); 0119 mainLayout->addWidget(mainWidget); 0120 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); 0121 okButton->setDefault(true); 0122 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0123 _createSoftBodyItemsDialog->connect(buttonBox, &QDialogButtonBox::accepted, 0124 _createSoftBodyItemsDialog, &QDialog::accept); 0125 _createSoftBodyItemsDialog->connect(buttonBox, &QDialogButtonBox::rejected, 0126 _createSoftBodyItemsDialog, &QDialog::reject); 0127 mainLayout->addWidget(buttonBox); 0128 0129 _createSoftBodyItemsUi = new Ui::WidgetCreateSoftBodyItems; 0130 _createSoftBodyItemsUi->setupUi(mainWidget); 0131 0132 _createSoftBodyItemsUi->lineEditPosition->setValidator( 0133 new QRegularExpressionValidator(QRegularExpression("^\\([+-]?\\d+(\\.\\d*)?([eE]\\d*)?,[+-]?\\d+(\\.\\d*)?([eE]\\d*)?\\)$"), 0134 _createSoftBodyItemsUi->lineEditPosition)); 0135 _createSoftBodyItemsUi->lineEditPosition->setText(StepCore::typeToString(pos)); 0136 _createSoftBodyItemsUi->lineEditSize->setValidator( 0137 new QRegularExpressionValidator(QRegularExpression("^\\([+-]?\\d+(\\.\\d*)?([eE]\\d*)?,[+-]?\\d+(\\.\\d*)?([eE]\\d*)?\\)$"), 0138 _createSoftBodyItemsUi->lineEditSize)); 0139 _createSoftBodyItemsUi->lineEditSplit->setValidator( 0140 new QRegularExpressionValidator(QRegularExpression("^\\(\\d+,\\d+\\)$"), 0141 _createSoftBodyItemsUi->lineEditSplit)); 0142 _createSoftBodyItemsUi->lineEditBodyMass->setValidator( 0143 new QDoubleValidator(0, HUGE_VAL, DBL_DIG, _createSoftBodyItemsUi->lineEditBodyMass)); 0144 _createSoftBodyItemsUi->lineEditBodyDamping->setValidator( 0145 new QDoubleValidator(0, HUGE_VAL, DBL_DIG, _createSoftBodyItemsUi->lineEditBodyDamping)); 0146 _createSoftBodyItemsUi->lineEditYoungModulus->setValidator( 0147 new QDoubleValidator(0, HUGE_VAL, DBL_DIG, _createSoftBodyItemsUi->lineEditYoungModulus)); 0148 0149 int retval = _createSoftBodyItemsDialog->exec(); 0150 if (retval == QDialog::Accepted) { 0151 createSoftBodyItemsApply(); 0152 } 0153 0154 delete _createSoftBodyItemsDialog; _createSoftBodyItemsDialog = nullptr; 0155 delete _createSoftBodyItemsUi; _createSoftBodyItemsUi = nullptr; 0156 } 0157 0158 void SoftBodyMenuHandler::createSoftBodyItemsApply() 0159 { 0160 Q_ASSERT(_createSoftBodyItemsUi && _createSoftBodyItemsDialog); 0161 0162 bool ok; 0163 StepCore::Vector2d position = StepCore::stringToType<StepCore::Vector2d>( 0164 _createSoftBodyItemsUi->lineEditPosition->text(), &ok); 0165 StepCore::Vector2d size = StepCore::stringToType<StepCore::Vector2d>( 0166 _createSoftBodyItemsUi->lineEditSize->text(), &ok); 0167 StepCore::Vector2i split = StepCore::stringToType<StepCore::Vector2i>( 0168 _createSoftBodyItemsUi->lineEditSplit->text(), &ok); 0169 0170 double bodyMass = _createSoftBodyItemsUi->lineEditBodyMass->text().toDouble(); 0171 double youngModulus = _createSoftBodyItemsUi->lineEditYoungModulus->text().toDouble(); 0172 double bodyDamping = _createSoftBodyItemsUi->lineEditBodyDamping->text().toDouble(); 0173 0174 0175 _worldModel->beginMacro(i18n("Create items for %1", softBody()->name())); 0176 0177 StepCore::ItemList items = 0178 softBody()->createSoftBodyItems(position, size, split, bodyMass, youngModulus, bodyDamping); 0179 0180 _worldModel->addItem(softBody()); 0181 _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(softBody()), 0182 QItemSelectionModel::ClearAndSelect); 0183 0184 const StepCore::ItemList::const_iterator end = items.end(); 0185 for(StepCore::ItemList::const_iterator it = items.begin(); it != end; ++it) { 0186 (*it)->setName(_worldModel->getUniqueName((*it)->metaObject()->className())); 0187 _worldModel->addItem(*it, softBody()); 0188 } 0189 0190 _worldModel->endMacro(); 0191 0192 _applied = true; 0193 } 0194 0195 ///////////////////////////////////////////////// 0196 0197 SoftBodyGraphicsItem::SoftBodyGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0198 : StepGraphicsItem(item, worldModel) 0199 { 0200 Q_ASSERT(dynamic_cast<StepCore::SoftBody*>(_item) != nullptr); 0201 setFlag(QGraphicsItem::ItemIsSelectable); 0202 setFlag(QGraphicsItem::ItemIsMovable); 0203 setAcceptHoverEvents(true); 0204 setZValue(BODY_ZVALUE-1); 0205 _velocityHandler = new ArrowHandlerGraphicsItem(item, worldModel, this, 0206 _item->metaObject()->property(QStringLiteral("velocity")), 0207 _item->metaObject()->property(QStringLiteral("position"))); 0208 _velocityHandler->setVisible(false); 0209 } 0210 0211 inline StepCore::SoftBody* SoftBodyGraphicsItem::softBody() const 0212 { 0213 return static_cast<StepCore::SoftBody*>(_item); 0214 } 0215 0216 QPainterPath SoftBodyGraphicsItem::shape() const 0217 { 0218 return _painterPath; 0219 } 0220 0221 void SoftBodyGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) 0222 { 0223 int renderHints = painter->renderHints(); 0224 painter->setRenderHint(QPainter::Antialiasing, true); 0225 painter->setPen(Qt::NoPen); 0226 painter->setBrush(QBrush(QColor::fromRgba(softBody()->color()))); 0227 painter->drawPath(_painterPath); 0228 0229 if(_isSelected) { 0230 double s = currentViewScale(); 0231 QRectF rect = _painterPath.boundingRect(); 0232 rect.adjust(-SELECTION_MARGIN/s, -SELECTION_MARGIN/s, SELECTION_MARGIN/s, SELECTION_MARGIN/s); 0233 painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine)); 0234 painter->setBrush(QBrush()); 0235 painter->drawRect(rect); 0236 } 0237 0238 painter->setRenderHint(QPainter::Antialiasing, renderHints & QPainter::Antialiasing); 0239 if(_isSelected || _isMouseOverItem) { 0240 painter->setPen(QPen(Qt::blue, 0)); 0241 drawArrow(painter, softBody()->position(), softBody()->velocity()); 0242 painter->setPen(QPen(Qt::red, 0)); 0243 drawArrow(painter, softBody()->position(), softBody()->acceleration()); 0244 } 0245 } 0246 0247 void SoftBodyGraphicsItem::viewScaleChanged() 0248 { 0249 prepareGeometryChange(); 0250 0251 _painterPath = QPainterPath(); 0252 _painterPath.setFillRule(Qt::WindingFill); 0253 0254 if(softBody()->borderParticles().size() > 0) { 0255 _painterPath.moveTo(vectorToPoint( 0256 dynamic_cast<StepCore::Particle*>(softBody()->borderParticles()[0])->position() )); 0257 for(unsigned int i=1; i<softBody()->borderParticles().size(); ++i) { 0258 _painterPath.lineTo(vectorToPoint( 0259 dynamic_cast<StepCore::Particle*>(softBody()->borderParticles()[i])->position() )); 0260 } 0261 _painterPath.closeSubpath(); 0262 } 0263 0264 double s = currentViewScale(); 0265 0266 _boundingRect = _painterPath.boundingRect(); 0267 0268 if(_isSelected || _isMouseOverItem) { 0269 const StepCore::Vector2d r = softBody()->position(); 0270 const StepCore::Vector2d v = softBody()->velocity(); 0271 const StepCore::Vector2d a = softBody()->acceleration(); 0272 _boundingRect |= QRectF(r[0], r[1], v[0], v[1]).normalized() 0273 | QRectF(r[0], r[1], a[0], a[1]).normalized(); 0274 } 0275 0276 double adjust = (ARROW_STROKE+SELECTION_MARGIN)/s; 0277 _boundingRect.adjust(-adjust,-adjust, adjust, adjust); 0278 } 0279 0280 void SoftBodyGraphicsItem::worldDataChanged(bool dynamicOnly) 0281 { 0282 Q_UNUSED(dynamicOnly); 0283 //setPos(vectorToPoint(softBody()->position())); 0284 viewScaleChanged(); 0285 update(); 0286 } 0287 0288 void SoftBodyGraphicsItem::stateChanged() 0289 { 0290 if(_isSelected) _velocityHandler->setVisible(true); 0291 else _velocityHandler->setVisible(false); 0292 viewScaleChanged(); 0293 update(); 0294 } 0295 0296 void SoftBodyGraphicsItem::mouseSetPos(const QPointF& /*pos*/, const QPointF& diff, MovingState) 0297 { 0298 _worldModel->simulationPause(); 0299 _worldModel->setProperty(_item, QStringLiteral("position"), 0300 QVariant::fromValue((softBody()->position() + pointToVector(diff)).eval())); 0301 } 0302 0303 void SoftBodyParticleGraphicsItem::worldDataChanged(bool dynamicOnly) 0304 { 0305 if(!dynamicOnly) { 0306 StepCore::SoftBody* sb = dynamic_cast<StepCore::SoftBody*>(particle()->group()); 0307 if(sb) { 0308 if(sb->showInternalItems() && !isVisible()) show(); 0309 else if(!sb->showInternalItems() && isVisible()) hide(); 0310 } 0311 } 0312 if(isVisible()) { 0313 ParticleGraphicsItem::worldDataChanged(dynamicOnly); 0314 } 0315 } 0316 0317 void SoftBodySpringGraphicsItem::worldDataChanged(bool dynamicOnly) 0318 { 0319 if(!dynamicOnly) { 0320 StepCore::SoftBody* sb = dynamic_cast<StepCore::SoftBody*>(spring()->group()); 0321 if(sb) { 0322 if(sb->showInternalItems() && !isVisible()) show(); 0323 else if(!sb->showInternalItems() && isVisible()) hide(); 0324 } 0325 } 0326 if(isVisible()) { 0327 SpringGraphicsItem::worldDataChanged(dynamicOnly); 0328 } 0329 } 0330 0331 #include "moc_softbodygraphics.cpp"