File indexing completed on 2024-04-28 03:51:19

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"