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

0001 /*.
0002     SPDX-FileCopyrightText: 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "worldmodel.h"
0008 
0009 #include "clipboard.h"
0010 #include "simulationthread.h"
0011 #include "worldgraphics.h"
0012 
0013 #include "settings.h"
0014 
0015 #include "worldfactory.h"
0016 #include <stepcore/world.h>
0017 #include <stepcore/xmlfile.h>
0018 #include <stepcore/eulersolver.h>
0019 #include <stepcore/collisionsolver.h>
0020 #include <stepcore/constraintsolver.h>
0021 #include <stepcore/types.h>
0022 
0023 #include <QApplication>
0024 #include <QItemSelectionModel>
0025 #include <QMenu>
0026 #include <QTimer>
0027 #include <QUndoStack>
0028 
0029 #include <KLocalizedString>
0030 
0031 class CommandEditProperty: public QUndoCommand
0032 {
0033 public:
0034     CommandEditProperty(WorldModel* worldModel, StepCore::Object* object,
0035                 const StepCore::MetaProperty* property, const QVariant& newValue, bool merge);
0036 
0037     int id() const override { return _merge ? 1 : -1; }
0038     bool mergeWith(const QUndoCommand* command) override;
0039 
0040     void redo() override;
0041     void undo() override;
0042 
0043 protected:
0044     /* It's important to properly compress commands
0045      *  or the stack becomes too big and slow */
0046     struct EditProperty {
0047         StepCore::Object* object;
0048         const StepCore::MetaProperty* property;
0049         QVariant oldValue;
0050         QVariant newValue;
0051     };
0052 
0053     bool _merge;
0054     WorldModel* _worldModel;
0055     QList<EditProperty> _commands;
0056     //QList<StepCore::Object*> _objects;
0057 };
0058 
0059 CommandEditProperty::CommandEditProperty(WorldModel* worldModel, StepCore::Object* object,
0060             const StepCore::MetaProperty* property, const QVariant& newValue, bool merge)
0061         : _merge(merge), _worldModel(worldModel)
0062 {
0063     //qDebug() << "CommandEditProperty: " << object->name() << " " << property->name() << " " << newValue.toString() << endl;
0064     EditProperty p = { object, property, property->readVariant(object), newValue };
0065     _commands << p;// _objects << object;
0066 }
0067 
0068 void CommandEditProperty::redo()
0069 {
0070     bool fullUpdate = false;
0071     foreach(const EditProperty& p, _commands) {
0072         if(p.newValue.type() != QVariant::String) p.property->writeVariant(p.object, p.newValue);
0073         else p.property->writeString(p.object, p.newValue.value<QString>());
0074         if(!p.property->isDynamic() || p.property->hasSideEffects()) fullUpdate = true;
0075     }
0076     //_worldModel->objectChanged(NULL);
0077     _worldModel->emitChanged(fullUpdate, true);
0078     //foreach(StepCore::Object* object, _objects) _worldModel->objectChanged(object);
0079 }
0080 
0081 void CommandEditProperty::undo()
0082 {
0083     bool fullUpdate = false;
0084     foreach(const EditProperty& p, _commands) {
0085         p.property->writeVariant(p.object, p.oldValue);
0086         if(!p.property->isDynamic() || p.property->hasSideEffects()) fullUpdate = true;
0087     }
0088     //_worldModel->objectChanged(NULL);
0089     _worldModel->emitChanged(fullUpdate, true);
0090     //foreach(StepCore::Object* object, _objects) _worldModel->objectChanged(object);
0091 }
0092 
0093 bool CommandEditProperty::mergeWith(const QUndoCommand* command)
0094 {
0095     const CommandEditProperty* cmd = dynamic_cast<const CommandEditProperty*>(command);
0096     Q_ASSERT(cmd != nullptr);
0097     if(cmd->_commands.count() != 1) return false;
0098 
0099     const EditProperty& p1 = cmd->_commands[0];
0100     for(int i=0; i < _commands.count(); ++i) {
0101         if(_commands[i].object == p1.object && _commands[i].property == p1.property) {
0102             _commands[i].newValue = p1.newValue;
0103             //if(!_objects.contains(p1.object)) _objects << p1.object;
0104             return true;
0105         }
0106     }
0107     _commands << p1;
0108     //if(!_objects.contains(p1.object)) _objects << p1.object;
0109     return true;
0110 }
0111 
0112 class CommandNewItem: public QUndoCommand
0113 {
0114 public:
0115     CommandNewItem(WorldModel* worldModel, StepCore::Item* item, StepCore::ItemGroup* parent, bool create)
0116             : _worldModel(worldModel), _item(item), _parent(parent), _create(create), _shouldDelete(create) {
0117         if(!create) findLinks(item, static_cast<StepCore::ItemGroup*>(_worldModel->world()));
0118     }
0119     ~CommandNewItem() { if(_shouldDelete) delete _item; }
0120 
0121     void redo() override;
0122     void undo() override;
0123 
0124 protected:
0125     void findLinks(StepCore::Item* itemToMatch, StepCore::ItemGroup* groupToSearch);
0126     void removeItem();
0127     void readdItem();
0128 
0129     WorldModel* _worldModel;
0130     StepCore::Item* _item;
0131     StepCore::ItemGroup* _parent;
0132     bool _create;
0133     bool _shouldDelete;
0134 
0135     typedef QPair<StepCore::Object*, const StepCore::MetaProperty*> Link;
0136     QList<Link> _links;
0137 };
0138 
0139 void CommandNewItem::findLinks(StepCore::Item* itemToMatch, StepCore::ItemGroup* groupToSearch)
0140 {
0141     StepCore::ItemGroup* itemToDeleteGroup = dynamic_cast<StepCore::ItemGroup*>(_item);
0142 
0143     for (StepCore::Item *item : groupToSearch->items()) {
0144         if (item == itemToMatch) // no need to find links in itself
0145             continue;
0146         if (itemToDeleteGroup && itemToDeleteGroup->contains(item)) // no need to find links if the item is a child of the item that is being deleted
0147             continue;
0148         const StepCore::MetaObject* mobj = item->metaObject();
0149         for(int i=0; i<mobj->propertyCount(); ++i) {
0150             if(mobj->property(i)->userTypeId() != qMetaTypeId<StepCore::Object*>()) continue;
0151             if(itemToMatch == mobj->property(i)->readVariant(item).value<StepCore::Object*>())
0152                 _links << qMakePair(static_cast<StepCore::Object*>(item), mobj->property(i));
0153         }
0154         if(mobj->inherits<StepCore::ItemGroup>())
0155             findLinks(itemToMatch, static_cast<StepCore::ItemGroup*>(item));
0156     }
0157 
0158     // If the item being deleted is a group, be sure we also find links for all its children
0159     StepCore::ItemGroup* itemToMatchGroup = dynamic_cast<StepCore::ItemGroup*>(itemToMatch);
0160     if (itemToMatchGroup) {
0161         for (StepCore::Item *child : itemToMatchGroup->items()) {
0162             findLinks(child, groupToSearch);
0163         }
0164     }
0165 }
0166 
0167 void CommandNewItem::removeItem()
0168 {
0169     foreach(const Link& link, _links)
0170         link.second->writeVariant(link.first, QVariant::fromValue<StepCore::Object*>(NULL));
0171     //qDebug("%d links removed", _links.count());
0172     _worldModel->removeCreatedItem(_item);
0173 }
0174 
0175 void CommandNewItem::readdItem()
0176 {
0177     _worldModel->addCreatedItem(_item, _parent);
0178     foreach(const Link& link, _links)
0179         link.second->writeVariant(link.first, QVariant::fromValue<StepCore::Object*>(_item));
0180     //qDebug("%d links restored", _links.count());
0181 }
0182 
0183 void CommandNewItem::redo()
0184 {
0185     if(_create) readdItem();
0186     else removeItem();
0187     _shouldDelete = !_create;
0188 }
0189 
0190 void CommandNewItem::undo()
0191 {
0192     if(_create) removeItem();
0193     else readdItem();
0194     _shouldDelete = _create;
0195 }
0196 
0197 class CommandSetSolver: public QUndoCommand
0198 {
0199 public:
0200     CommandSetSolver(WorldModel* worldModel, StepCore::Solver* solver)
0201             : _worldModel(worldModel), _solver(solver) {}
0202     ~CommandSetSolver() { delete _solver; }
0203     void redo() override { _solver = _worldModel->swapSolver(_solver); }
0204     void undo() override { _solver = _worldModel->swapSolver(_solver); }
0205 
0206 protected:
0207     WorldModel* _worldModel;
0208     StepCore::Solver* _solver;
0209 };
0210 
0211 class CommandSimulate: public QUndoCommand
0212 {
0213 public:
0214     CommandSimulate(WorldModel* worldModel);
0215     ~CommandSimulate() { delete _worldCopy; }
0216 
0217     void done();
0218 
0219     void redo() override;
0220     void undo() override;
0221 
0222     const QString& startTime() const { return _startTime; }
0223     const QString& endTime() const { return _endTime; }
0224 
0225 protected:
0226     typedef QPair<int, int> PairInt;
0227     PairInt indexToPair(const QModelIndex &index);
0228     QModelIndex pairToIndex(PairInt pair);
0229 
0230     WorldModel* _worldModel;
0231     StepCore::World* _worldCopy;
0232     StepCore::World* _oldWorld;
0233     StepCore::World* _newWorld;
0234 
0235     QString _startTime;
0236     QString _endTime;
0237 };
0238 
0239 CommandSimulate::PairInt CommandSimulate::indexToPair(const QModelIndex &index)
0240 {
0241     if(index.parent().isValid()) return PairInt(1, index.row());
0242     else return PairInt(0, index.row());
0243 }
0244 
0245 QModelIndex CommandSimulate::pairToIndex(CommandSimulate::PairInt pair)
0246 {
0247     if(pair.first == 0) {
0248         if(pair.second == 0) return _worldModel->worldIndex();
0249         else if(pair.second == 1) return _worldModel->solverIndex();
0250         else if(pair.second == 2) return _worldModel->collisionSolverIndex();
0251         else if(pair.second == 3) return _worldModel->constraintSolverIndex();
0252         else return QModelIndex();
0253     } else return _worldModel->childItemIndex(pair.second);
0254 }
0255 
0256 CommandSimulate::CommandSimulate(WorldModel* worldModel)
0257 {
0258     _worldModel = worldModel;
0259     _oldWorld = _worldCopy = _worldModel->_world;
0260 
0261     /*
0262     QList<PairInt> selection;
0263     foreach(QModelIndex index, _worldModel->_selectionModel->selection().indexes())
0264         selection << indexToPair(index);
0265     PairInt current = indexToPair(_worldModel->_selectionModel->currentIndex());
0266     */
0267 
0268     _worldModel->beginResetModel();
0269     _newWorld = _worldModel->_world = new StepCore::World(*_oldWorld);
0270     _worldModel->endResetModel();
0271 
0272     _startTime = _worldModel->formatProperty(_worldModel->_world, nullptr,
0273                             _worldModel->_world->metaObject()->property(QStringLiteral("time")),
0274                             WorldModel::FormatEditable);
0275 
0276     /*
0277     foreach(PairInt pair, selection)
0278         _worldModel->_selectionModel->select(pairToIndex(pair), QItemSelectionModel::Select);
0279     _worldModel->_selectionModel->setCurrentIndex(pairToIndex(current), QItemSelectionModel::Current);
0280     */
0281 }
0282 
0283 void CommandSimulate::done()
0284 {
0285     _endTime = _worldModel->formatProperty(_worldModel->_world, nullptr,
0286                             _worldModel->_world->metaObject()->property(QStringLiteral("time")),
0287                             WorldModel::FormatEditable);
0288 }
0289 
0290 void CommandSimulate::redo()
0291 {
0292     if(_newWorld != _worldModel->_world) {
0293         _worldModel->beginResetModel();
0294         _worldModel->_world = _newWorld;
0295         _worldCopy = _oldWorld;
0296 
0297         _worldModel->endResetModel();
0298         _worldModel->_selectionModel->setCurrentIndex(
0299                 _worldModel->worldIndex(), QItemSelectionModel::SelectCurrent);
0300     }
0301 }
0302 
0303 void CommandSimulate::undo()
0304 {
0305     _worldModel->beginResetModel();
0306     _worldModel->_world = _oldWorld;
0307     _worldCopy = _newWorld;
0308 
0309     _worldModel->endResetModel();
0310     _worldModel->_selectionModel->setCurrentIndex(
0311             _worldModel->worldIndex(), QItemSelectionModel::SelectCurrent);
0312 }
0313 
0314 WorldModel::WorldModel(QObject* parent)
0315     : QAbstractItemModel(parent), _actions(nullptr)
0316 {
0317     _selectionModel = new QItemSelectionModel(this, this);
0318     _undoStack = new QUndoStack(this);
0319     _clipboard = new Clipboard(this);
0320     _worldFactory = new WorldFactory();
0321     _world = new StepCore::World();
0322 
0323     _simulationCommand = nullptr;
0324     _simulationTimer = new QTimer(this);
0325     _simulationTimer0 = new QTimer(this);
0326     _simulationTimer0->setSingleShot(true);
0327     _simulationTimer0->setInterval(0);
0328     setSimulationFps(25); // XXX KConfig ?
0329 
0330     _simulationThread = new SimulationThread(&_world);
0331     _simulationThread->start();
0332 
0333     connect(_simulationTimer, &QTimer::timeout,
0334                 this, &WorldModel::simulationFrameBegin);
0335     connect(_simulationTimer0, &QTimer::timeout,
0336                 this, &WorldModel::simulationFrameBegin);
0337     connect(_simulationThread, &SimulationThread::worldEvolveDone,
0338                 this, &WorldModel::simulationFrameEnd, Qt::QueuedConnection);
0339 
0340     _updatingTimer = new QTimer(this);
0341     _updatingTimer->setSingleShot(true);
0342     _updatingTimer->setInterval(0);
0343     _updatingFullUpdate = false;
0344     _updatingRecalcFn = false;
0345 
0346     connect(_updatingTimer, &QTimer::timeout,
0347                 this, &WorldModel::doEmitChanged);
0348 
0349     resetWorld();
0350 
0351     _simulationFrameWaiting = false;
0352     _simulationFrameSkipped = false;
0353     _simulationStopping = false;
0354     _simulationPaused = false;
0355 
0356 }
0357 
0358 WorldModel::~WorldModel()
0359 {
0360     delete _simulationThread;
0361     delete _worldFactory;
0362     delete _world;
0363 }
0364 
0365 void WorldModel::resetWorld()
0366 {
0367     Q_ASSERT(!isSimulationActive());
0368     beginResetModel();
0369     if(_world->name().isEmpty()) {
0370         // XXX: check that loaded items has unique names !
0371         _world->setName(getUniqueName(QStringLiteral("world")));
0372     }
0373     if(nullptr == _world->solver()) {
0374         _world->setSolver(new StepCore::AdaptiveEulerSolver());
0375         _world->solver()->setName(getUniqueName(QStringLiteral("solver")));
0376     }
0377     if(nullptr == _world->collisionSolver()) {
0378         _world->setCollisionSolver(new StepCore::GJKCollisionSolver());
0379         _world->collisionSolver()->setName(getUniqueName(QStringLiteral("collisionSolver")));
0380     }
0381     if(nullptr == _world->constraintSolver()) {
0382         _world->setConstraintSolver(new StepCore::CGConstraintSolver());
0383         _world->constraintSolver()->setName(getUniqueName(QStringLiteral("constraintSolver")));
0384     }
0385     _undoStack->clear();
0386 
0387     _world->doCalcFn();
0388     endResetModel();
0389 
0390     _selectionModel->setCurrentIndex(worldIndex(), QItemSelectionModel::SelectCurrent);
0391 
0392     emitChanged(true, false);
0393 }
0394 
0395 void WorldModel::emitChanged(bool fullUpdate, bool recalcFn)
0396 {
0397     //qDebug() << "emitChanged(): " << world()->time() << endl;
0398     if(fullUpdate) _updatingFullUpdate = true;
0399     if(recalcFn) _updatingRecalcFn = true;
0400     if(!_updatingTimer->isActive()) _updatingTimer->start(0);
0401 }
0402 
0403 void WorldModel::doEmitChanged()
0404 {
0405     if(_updatingRecalcFn) _world->doCalcFn();
0406     emit worldDataChanged(!_updatingFullUpdate);
0407     if(_updatingFullUpdate) {
0408         emit dataChanged(worldIndex(), constraintSolverIndex());
0409     }
0410 }
0411 
0412 QModelIndex WorldModel::worldIndex() const
0413 {
0414     return createIndex(0, 0, _world);
0415 }
0416 
0417 QModelIndex WorldModel::solverIndex() const
0418 {
0419     return createIndex(1, 0, _world->solver());
0420 }
0421 
0422 QModelIndex WorldModel::collisionSolverIndex() const
0423 {
0424     return createIndex(2, 0, _world->collisionSolver());
0425 }
0426 
0427 QModelIndex WorldModel::constraintSolverIndex() const
0428 {
0429     return createIndex(3, 0, _world->constraintSolver());
0430 }
0431 
0432 QModelIndex WorldModel::childItemIndex(int n, StepCore::ItemGroup* group) const
0433 {
0434     if(!group) group = _world;
0435     return createIndex(n, 0, group->childItem(n));
0436 }
0437 
0438 QModelIndex WorldModel::objectIndex(StepCore::Object* obj) const
0439 {
0440     if(obj == _world) return worldIndex();
0441     else if(obj == _world->solver()) return solverIndex();
0442     else if(obj == _world->collisionSolver()) return collisionSolverIndex();
0443     else if(obj == _world->constraintSolver()) return constraintSolverIndex();
0444     else {
0445         StepCore::Item* item = dynamic_cast<StepCore::Item*>(obj);
0446         for(StepCore::Item* it = item; it != _world; it = it->group()) {
0447             if(it == nullptr) return QModelIndex();
0448         }
0449         STEPCORE_ASSERT_NOABORT(item && item->group());
0450         return createIndex(item->group()->childItemIndex(item), 0, item);
0451         //return itemIndex(_world->childItemIndex(dynamic_cast<const StepCore::Item*>(obj)));
0452     }
0453 }
0454 
0455 StepCore::Object* WorldModel::object(const QModelIndex& index) const
0456 {
0457     if(index.isValid()) return static_cast<StepCore::Object*>(index.internalPointer());
0458     else return nullptr;
0459 }
0460 
0461 #if 0
0462 void WorldModel::objectChanged(const StepCore::Object* /*object*/)
0463 {
0464     if(!_updating) {
0465         _world->doCalcFn();
0466         emitChanged(false);
0467     }
0468 }
0469 #endif
0470 
0471 StepCore::Item* WorldModel::item(const QModelIndex& index) const
0472 {
0473     return dynamic_cast<StepCore::Item*>(object(index));
0474 }
0475 
0476 QVariant WorldModel::data(const QModelIndex &index, int role) const
0477 {
0478     if(!index.isValid()) return QVariant();
0479     StepCore::Object* obj = static_cast<StepCore::Object*>(index.internalPointer());
0480 
0481     if(role == Qt::DisplayRole) {
0482         return formatNameFull(obj);
0483     } else if(role == Qt::ToolTipRole) {
0484         const_cast<WorldModel*>(this)->simulationPause();
0485         return createToolTip(index); // XXX
0486     } else if(role == Qt::DecorationRole) {
0487         if(_worldFactory->hasObjectIcon(obj->metaObject())) 
0488             return _worldFactory->objectIcon(obj->metaObject());
0489         return QVariant();
0490     } else if(role == FormattedNameRole) {
0491         return formatName(obj);
0492     } else if(role == ClassNameRole) {
0493         return obj->metaObject()->className();
0494     }
0495 
0496     return QVariant();
0497 }
0498 
0499 QModelIndex WorldModel::index(int row, int /*column*/, const QModelIndex &parent) const
0500 {
0501     if(!parent.isValid()) {
0502         if(row == 0) return worldIndex();
0503         else if(row == 1) return solverIndex();
0504         else if(row == 2) return collisionSolverIndex();
0505         else if(row == 3) return constraintSolverIndex();
0506     } else {
0507         StepCore::ItemGroup* group = dynamic_cast<StepCore::ItemGroup*>(object(parent));
0508         if(group) return createIndex(row, 0, group->childItem(row));
0509         //if(parent.internalPointer() == _world) return itemIndex(row);
0510     }
0511     return QModelIndex();
0512 }
0513 
0514 QModelIndex WorldModel::parent(const QModelIndex &index) const
0515 {
0516     if(!index.isValid()) return QModelIndex();
0517     else if(index.internalPointer() == _world) return QModelIndex();
0518     else if(index.internalPointer() == _world->solver()) return QModelIndex();
0519     else if(index.internalPointer() == _world->collisionSolver()) return QModelIndex();
0520     else if(index.internalPointer() == _world->constraintSolver()) return QModelIndex();
0521     else {
0522         StepCore::Item* item = dynamic_cast<StepCore::Item*>(object(index));
0523         if(item && item->group()) return objectIndex(item->group());
0524         else return QModelIndex();
0525     }
0526 }
0527 
0528 int WorldModel::rowCount(const QModelIndex &parent) const
0529 {
0530     if(!parent.isValid()) {
0531         Q_ASSERT(_world->solver());
0532         Q_ASSERT(_world->collisionSolver());
0533         Q_ASSERT(_world->constraintSolver());
0534         return 4;
0535         /*
0536         int count = 1;
0537         if(_world->solver()) ++count;
0538         if(_world->collisionSolver()) ++count;
0539         return count;
0540         */
0541     }
0542     else if(dynamic_cast<StepCore::ItemGroup*>(object(parent)))
0543         return dynamic_cast<StepCore::ItemGroup*>(object(parent))->childItemCount();
0544     else return 0;
0545 }
0546 
0547 int WorldModel::columnCount(const QModelIndex& /*parent*/) const
0548 {
0549     return 1;
0550 }
0551 
0552 QString WorldModel::newItemName(const QString& className)
0553 {
0554     return getUniqueName(className);
0555 }
0556 
0557 StepCore::Item* WorldModel::createItem(const QString& className, StepCore::ItemGroup* parent)
0558 {
0559     Q_UNUSED(parent)
0560     StepCore::Item* item = _worldFactory->newItem(className);
0561     if(item == nullptr) return nullptr;
0562     item->setName(getUniqueName(className));
0563     return item;
0564 }
0565 
0566 StepCore::Item* WorldModel::newItem(const QString& className, StepCore::ItemGroup* parent)
0567 {
0568     StepCore::Item* item = _worldFactory->newItem(className);
0569     if(item == nullptr) return nullptr;
0570     item->setName(getUniqueName(className));
0571     pushCommand(new CommandNewItem(this, item, parent, true));
0572     return item;
0573 }
0574 
0575 void WorldModel::addItem(StepCore::Item* item, StepCore::ItemGroup* parent)
0576 {
0577     //if(item->name().isEmpty()) item->setName(getUniqueName(item->metaObject()->className()));
0578     pushCommand(new CommandNewItem(this, item, parent, true));
0579 }
0580 
0581 StepCore::Solver* WorldModel::newSolver(const QString& className)
0582 {
0583     StepCore::Solver* solver = _worldFactory->newSolver(className);
0584     if(solver == nullptr) return nullptr;
0585 
0586     // Copy similarly named properties
0587     // XXX: is it right ?
0588     StepCore::Solver* oldSolver = _world->solver();
0589     for(int i=0; i<oldSolver->metaObject()->propertyCount(); ++i) {
0590         const StepCore::MetaProperty* oldProperty = oldSolver->metaObject()->property(i);
0591         const StepCore::MetaProperty* newProperty = solver->metaObject()->property(oldProperty->name());
0592         if(newProperty && newProperty->isWritable()) {
0593             newProperty->writeVariant(solver, oldProperty->readVariant(oldSolver));
0594         }
0595     }
0596 
0597     //solver->setName(_world->solver()->name());
0598     //solver->setName(getUniqueName(name)); // XXX: is it better ?
0599 
0600     pushCommand(new CommandSetSolver(this, solver));
0601     return solver;
0602 }
0603 
0604 void WorldModel::deleteItem(StepCore::Item* item)
0605 {
0606     pushCommand(new CommandNewItem(this, item, item->group(), false));
0607 }
0608 
0609 void WorldModel::deleteSelectedItems()
0610 {
0611     simulationPause();
0612 
0613     QList<StepCore::Item*> items;
0614     foreach(QModelIndex index, selectionModel()->selectedIndexes()) {
0615         // Do not delete world item
0616         if (index == worldIndex()) continue;
0617         
0618         StepCore::Item* it = item(index);
0619         if (it) items << it;
0620     }
0621 
0622     foreach(StepCore::Item* it, items) {
0623         for(StepCore::Item* it1 = it->group(); it1 != nullptr && it1 != _world; it1 = it1->group()) {
0624             if(items.contains(it1)) { items.removeOne(it); break; }
0625         }
0626     }
0627 
0628     if(!items.isEmpty()) {
0629         beginMacro(items.count()==1 ? i18n("Delete %1", items[0]->name())
0630                                     : i18n("Delete several items"));
0631         foreach(StepCore::Item* it, items) deleteItem(it);
0632         endMacro();
0633     }
0634 }
0635 
0636 void WorldModel::addCreatedItem(StepCore::Item* item, StepCore::ItemGroup* parent)
0637 {
0638     if(!parent) parent = _world;
0639     beginInsertRows(objectIndex(parent), parent->childItemCount(), parent->childItemCount());
0640     parent->addItem(item);
0641     endInsertRows();
0642     emitChanged(true, true);
0643 }
0644 
0645 void WorldModel::removeCreatedItem(StepCore::Item* item)
0646 {
0647     STEPCORE_ASSERT_NOABORT(item->group());
0648     QModelIndex index = objectIndex(item);
0649     beginRemoveRows(index.parent(), index.row(), index.row());
0650     item->group()->removeItem(item);
0651     endRemoveRows();
0652     emitChanged(true, true);
0653 }
0654 
0655 StepCore::Solver* WorldModel::swapSolver(StepCore::Solver* solver)
0656 {
0657     bool selected = selectionModel()->isSelected(solverIndex());
0658     bool current = selectionModel()->currentIndex() == solverIndex();
0659     StepCore::Solver* oldSolver = _world->removeSolver();
0660     _world->setSolver(solver);
0661     if(selected) selectionModel()->select(solverIndex(), QItemSelectionModel::Select);
0662     if(current) selectionModel()->setCurrentIndex(solverIndex(), QItemSelectionModel::Current);
0663     emitChanged(true, true);
0664     return oldSolver;
0665 }
0666 
0667 void WorldModel::pushCommand(QUndoCommand* command)
0668 {
0669     Q_ASSERT(!_simulationFrameWaiting || _simulationPaused);
0670     if(!isSimulationActive()) {
0671         _undoStack->push(command);
0672     } else {
0673         command->redo();
0674         delete command;
0675     }
0676 }
0677 
0678 void WorldModel::beginMacro(const QString& text)
0679 {
0680     // XXX: simulation could be started (by hotkey) during macro creation
0681     if(!isSimulationActive())
0682         _undoStack->beginMacro(text);
0683 }
0684 
0685 void WorldModel::endMacro()
0686 {
0687     // XXX: simulation could be stopped during macro creation
0688     if(!isSimulationActive())
0689         _undoStack->endMacro();
0690 }
0691 
0692 void WorldModel::setProperty(StepCore::Object* object,
0693             const StepCore::MetaProperty* property, const QVariant& value, UndoFlags flags)
0694 {
0695     Q_ASSERT(object != nullptr); Q_ASSERT(property != nullptr);
0696     pushCommand(new CommandEditProperty(this, object, property, value, !flags.testFlag(UndoNoMerge)));
0697 }
0698 
0699 QString WorldModel::formatName(const StepCore::Object* object) const
0700 {
0701     if(!object) return i18n("<no object>");
0702     else if(!object->name().isEmpty()) return object->name();
0703     return i18n("<unnamed>");
0704 }
0705 
0706 QString WorldModel::formatNameFull(const StepCore::Object* object) const
0707 {
0708     if(!object) return i18n("<no object>");
0709     return i18n("%1: %2", formatName(object), object->metaObject()->classNameTr());
0710 }
0711 
0712 QString WorldModel::formatProperty(const StepCore::Object* object,
0713                                    const StepCore::Object* objectErrors,
0714                                    const StepCore::MetaProperty* property,
0715                                    FormatFlags flags) const
0716 {
0717     int pr = Settings::floatDisplayPrecision();
0718 
0719     QString units;
0720     if(!flags.testFlag(FormatHideUnits)) {
0721         if(!flags.testFlag(FormatEditable) && !property->units().isEmpty()) 
0722             units.append(" [").append(property->units()).append("]");
0723 #ifdef STEP_WITH_UNITSCALC
0724         else if(flags.testFlag(FormatEditable) && !property->units().isEmpty()
0725                     && property->userTypeId() == QMetaType::Double) 
0726             units.append(" ").append(property->units());
0727 #endif
0728     }
0729 
0730     const StepCore::MetaProperty* pv = objectErrors ?
0731             objectErrors->metaObject()->property(property->name() + "Variance") : nullptr;
0732 
0733     // Common property types
0734     if(property->userTypeId() == QMetaType::Double) {
0735         QString error;
0736         if(pv) error = QStringLiteral(" ± %1")
0737                         .arg(sqrt(pv->readVariant(objectErrors).toDouble()), 0, 'g', pr)
0738                         .append(units);
0739         return QString::number(property->readVariant(object).toDouble(), 'g', pr).append(units).append(error);
0740     } else if(property->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) {
0741         QString error;
0742         if(pv) {
0743             StepCore::Vector2d vv = pv->readVariant(objectErrors).value<StepCore::Vector2d>();
0744             error = QStringLiteral(" ± (%1,%2)")
0745                         .arg(sqrt(vv[0]), 0, 'g', pr).arg(sqrt(vv[1]), 0, 'g', pr).append(units);
0746         }
0747         StepCore::Vector2d v = property->readVariant(object).value<StepCore::Vector2d>();
0748         return QStringLiteral("(%1,%2)").arg(v[0], 0, 'g', pr).arg(v[1], 0, 'g', pr).append(units).append(error);
0749     } else if(property->userTypeId() == qMetaTypeId<StepCore::Vector2dList >() ) {
0750         // XXX: add error information
0751 //         if(pv) qDebug() << "Unhandled property variance type" << endl;
0752         StepCore::Vector2dList list =
0753                 property->readVariant(object).value<StepCore::Vector2dList >();
0754         QString string;
0755         unsigned int end = flags.testFlag(FormatEditable) ? list.size() : qMin<unsigned int>(10, list.size());
0756         for(unsigned int i=0; i<end; ++i) {
0757             if(!string.isEmpty()) string += ',';
0758             string += QStringLiteral("(%1,%2)").arg(list[i][0], 0, 'g', pr)
0759                                             .arg(list[i][1], 0, 'g', pr);
0760         }
0761         if(!flags.testFlag(FormatEditable) && end != 0 && end < list.size()) string += QLatin1String(",...");
0762         string.append(units);
0763         return string;
0764     } else {
0765         // default type
0766         // XXX: add error information
0767         //if(pe) error = QString::fromUtf8(" ± ").append(pe->readString(_objectErrors)).append(units);
0768         //if(pv) qDebug() << "Unhandled property variance type" << endl;
0769         Q_ASSERT(!pv);
0770         QString str = property->readString(object);
0771         if(!flags.testFlag(FormatEditable) && str.length() > 50)
0772             str = str.left(50).append("...");
0773         return str.append(units);
0774     }
0775 }
0776 
0777 QString WorldModel::createToolTip(const QModelIndex& index) const
0778 {
0779     //Q_ASSERT(object != NULL);
0780     Q_ASSERT(index.isValid());
0781     QString toolTip = i18n("<nobr><h4><u>%1</u></h4></nobr>", index.data(Qt::DisplayRole).toString());
0782     toolTip += QLatin1String("<table>");
0783 
0784     StepCore::Object* object = this->object(index);
0785     StepCore::Item* item = dynamic_cast<StepCore::Item*>(object);
0786     const StepCore::Object* objectErrors = nullptr;
0787     if(item) {
0788         if(world()->errorsCalculation()) objectErrors = item->objectErrors();
0789         else objectErrors = item->tryGetObjectErrors();
0790     }
0791 
0792     for(int i=0; i<object->metaObject()->propertyCount(); ++i) {
0793         const StepCore::MetaProperty* p = object->metaObject()->property(i);
0794         /*QString units;
0795         if(!p->units().isEmpty())
0796             units.append(" [").append(p->units()).append("]");
0797         QString value = p->readString(object);
0798         if(p->userTypeId() == qMetaTypeId<StepCore::Vector2dList >()) {
0799             // XXX: don't use readString in this case !
0800             value.replace("),(", QString(")%1<br />(").arg(units));
0801             value.append(units);
0802             if(value.count("<br />") > 10) {
0803                 value = value.section("<br />", 0, 9);
0804                 value.append("<br />...");
0805             }
0806         } else {
0807             value.append(units);
0808         }*/
0809         toolTip += i18n("<tr><td>%1&nbsp;&nbsp;</td><td>%2</td></tr>", p->nameTr(),
0810                             formatProperty(object, objectErrors, p));
0811     }
0812     toolTip += QLatin1String("</table>");
0813     //qDebug("%s", toolTip.toAscii().constData());
0814     return toolTip;
0815 }
0816 
0817 QMenu* WorldModel::createContextMenu(const QModelIndex& index)
0818 {
0819     //Q_ASSERT(object != NULL);
0820     Q_ASSERT(index.isValid());
0821     QMenu* menu = new QMenu();
0822     menu->addSection(index.data(Qt::DisplayRole).toString());
0823     ItemMenuHandler* handler = _worldFactory->newItemMenuHandler(object(index), this, menu);
0824     handler->populateMenu(menu, _actions);
0825     return menu;
0826 }
0827 
0828 void WorldModel::clearWorld()
0829 {
0830     _world->clear();
0831     resetWorld();
0832 }
0833 
0834 bool WorldModel::saveXml(QIODevice* device)
0835 {
0836     StepCore::XmlFile file(device);
0837 
0838     if(file.save(_world)) {
0839         return true;
0840     } else {
0841         _errorString = file.errorString();
0842         return false;
0843     }
0844 }
0845 
0846 bool WorldModel::loadXml(QIODevice* device)
0847 {
0848     _world->clear();
0849     StepCore::XmlFile file(device);
0850     bool ret = file.load(_world, _worldFactory);
0851     if(!ret) { _world->clear(); _errorString = file.errorString(); }
0852     resetWorld();
0853     return ret;
0854 }
0855 
0856 bool WorldModel::checkUniqueName(const QString& name) const
0857 {
0858     if(name.isEmpty()) return false;
0859     if(_world->object(name) != nullptr) return false;
0860     return true;
0861 }
0862 
0863 QString WorldModel::getUniqueName(const QString& className) const
0864 {
0865     QString classNameCopy = className;
0866     classNameCopy[0] = classNameCopy[0].toLower();
0867     for(int n=1; ; ++n) {
0868         QString name = classNameCopy + QString::number(n);
0869         if(checkUniqueName(name)) return name;
0870     }
0871     return QString();
0872 }
0873 
0874 void WorldModel::simulationPause()
0875 {
0876     if(!_simulationFrameWaiting || _simulationPaused) return;
0877 
0878     // Try to lock but do not wait mode than one frame
0879     if(_simulationThread->mutex()->tryLock(1000/_simulationFps)) {
0880         // We still need to setAbortEvolve(true) since
0881         // _simulationThread->doWorldEvolve() could be called before
0882         _world->setEvolveAbort(true);
0883     } else {
0884         //qDebug() << "simulationPause: simulation aborted" << endl;
0885         Q_ASSERT(isSimulationActive());
0886         Q_ASSERT(_simulationCommand);
0887         Q_ASSERT(_simulationFrameWaiting);
0888         //Q_ASSERT(!_simulationStopping);
0889 
0890         _world->setEvolveAbort(true);
0891 
0892         // Mutex will be locked as soon as current frame is aborted
0893         _simulationThread->mutex()->lock();
0894     }
0895     // We can release mutex just now since new simulation frame can
0896     // only be started from this thread when control returns to event loop
0897     _simulationThread->mutex()->unlock();
0898     _simulationPaused = true;
0899 
0900     //qDebug() << "simulationPause!" << endl;
0901     // XXX: do we need to reset evolveAbort here (and add threadAbort var) ?
0902     // XXX: do we need to call emitChanged here ?
0903 }
0904 
0905 void WorldModel::setSimulationFps(int simulationFps)
0906 {
0907     _simulationFps = simulationFps;
0908     _simulationTimer->setInterval(1000/simulationFps);
0909 }
0910 
0911 bool WorldModel::isSimulationActive()
0912 {
0913     return _simulationTimer->isActive();
0914 }
0915 
0916 void WorldModel::simulationStart()
0917 {
0918     Q_ASSERT(!isSimulationActive());
0919     Q_ASSERT(!_simulationCommand);
0920 
0921     //_undoStack->beginMacro(i18n("Simulate"));
0922     _simulationCommand = new CommandSimulate(this);
0923     _world->setEvolveAbort(false);
0924     _simulationFrameWaiting = false;
0925     _simulationFrameSkipped = false;
0926     _simulationStopping = false;
0927     _simulationPaused = false;
0928 
0929     _simulationFrames = 0;
0930     _simulationStartTime = QTime::currentTime();
0931     _simulationTimer->start();
0932 
0933     //emitChanged();
0934 }
0935 
0936 void WorldModel::simulationStop()
0937 {
0938     Q_ASSERT(isSimulationActive());
0939     Q_ASSERT(_simulationCommand);
0940     _simulationStopping = true;
0941     if(_simulationFrameWaiting) {
0942         simulationPause();
0943     } else {
0944         simulationFrameEnd(0);
0945     }
0946 }
0947 
0948 void WorldModel::simulationFrameBegin()
0949 {
0950     Q_ASSERT(isSimulationActive());
0951     Q_ASSERT(_simulationCommand);
0952     Q_ASSERT(!_simulationStopping);
0953 
0954     //qDebug() << "simulationFrameBegin(): " << world()->time() << endl;
0955 
0956     if(_simulationFrameWaiting) { // TODO: warn user
0957         _simulationFrameSkipped = true;
0958         return;
0959     }
0960 
0961     if(_updatingTimer->isActive()) {
0962         // Wait for updating
0963         _simulationTimer0->start();
0964         return;
0965     }
0966 
0967     //qDebug("emit simulationDoFrame() t=%#x", int(QThread::currentThread()));
0968     //qDebug() << "simulationFrameBegin" << endl;
0969     _simulationFrameWaiting = true;
0970     _simulationPaused = false;
0971     _simulationThread->doWorldEvolve(1.0/_simulationFps);
0972     //qDebug("emitted simulationDoFrame()");
0973 }
0974 
0975 void WorldModel::simulationFrameEnd(int result)
0976 {
0977     Q_ASSERT(isSimulationActive());
0978     Q_ASSERT(_simulationCommand);
0979     Q_ASSERT(_simulationFrameWaiting || _simulationStopping);
0980 
0981     ++_simulationFrames;
0982     if(_simulationFrames == 50) {
0983         qDebug("FPS: %f", double(_simulationFrames) /
0984                     _simulationStartTime.msecsTo(QTime::currentTime()) * 1000);
0985         _simulationStartTime = QTime::currentTime();
0986         _simulationFrames = 0;
0987     }
0988 
0989     //qDebug() << "simulationFrameEnd" << endl;
0990 
0991     // It's OK to be aborted
0992     if(result == StepCore::Solver::Aborted) {
0993         //qDebug("simulation frame aborted!");
0994         result = StepCore::Solver::OK;
0995     }
0996 
0997     // If current frame was aborted we can resume it now
0998     _world->setEvolveAbort(false);
0999 
1000     // Update GUI
1001     _simulationFrameWaiting = false;
1002     emitChanged(false, false);
1003 
1004     // Stop if requested or simulation error occurred
1005     if(_simulationStopping || result != StepCore::Solver::OK) {
1006         _simulationCommand->done();
1007         _undoStack->beginMacro(i18n("Simulate %1 → %2",
1008                 _simulationCommand->startTime(), _simulationCommand->endTime()));
1009         _undoStack->push(_simulationCommand);
1010         _undoStack->endMacro();
1011         _simulationCommand = nullptr;
1012 
1013         _simulationTimer->stop();
1014         _simulationTimer0->stop();
1015         emit simulationStopped(result);
1016         return;
1017     }
1018 
1019     if(_simulationFrameSkipped) {
1020         _simulationFrameSkipped = false;
1021         _simulationTimer0->start();
1022         //QApplication::processEvents(); // XXX
1023         //if(isSimulationActive() && !_simulationFrameWaiting)
1024         //    simulationFrameBegin();
1025     }
1026 }
1027 
1028 QList<StepCore::Item*> WorldModel::selectedItems()
1029 {
1030     QList<StepCore::Item*> items;
1031     foreach (QModelIndex index, selectionModel()->selectedIndexes()) {
1032         // Do not delete world item
1033         if (index == worldIndex()) continue;
1034         
1035         StepCore::Item* it = item(index);
1036         if (it) items << it;
1037     }
1038     
1039     foreach (StepCore::Item* it, items) {
1040         for (StepCore::Item* it1 = it->group(); it1 != nullptr && it1 != _world; it1 = it1->group()) {
1041             if (items.contains(it1)) {
1042                 items.removeOne(it);
1043                 break;
1044             }
1045         }
1046     }
1047     
1048     return items;
1049 }
1050 
1051 void WorldModel::cutSelectedItems()
1052 {
1053     simulationPause();
1054     
1055     QList<StepCore::Item*> items = selectedItems();
1056     
1057     _clipboard->copy(items);
1058     
1059     if (!items.isEmpty()) {
1060         beginMacro(items.count() == 1 ? i18n("Cut %1", items[0]->name()) :
1061                    i18n("Cut several items"));
1062         foreach (StepCore::Item* it, items) deleteItem(it);
1063         endMacro();
1064     }
1065 }
1066 
1067 void WorldModel::copySelectedItems()
1068 {
1069     simulationPause();
1070     
1071     QList<StepCore::Item*> items = selectedItems();
1072     
1073     _clipboard->copy(items);
1074 }
1075 
1076 void WorldModel::pasteItems()
1077 {
1078     simulationPause();
1079     
1080     QList<StepCore::Item*> items = _clipboard->paste(_worldFactory);
1081     if (items.isEmpty()) return;
1082     
1083     beginMacro(items.count() == 1 ? i18n("Pasted %1", items[0]->name()) :
1084                i18n("Pasted several items"));
1085     QItemSelection selection;
1086     foreach (StepCore::Item* item, items) {
1087         QString className = item->metaObject()->className();
1088         item->setName(getUniqueName(className));
1089         addItem(item, _world);
1090         QModelIndex index = objectIndex(item);
1091         selection.select(index, index);
1092     }
1093     if (!selection.isEmpty()) {
1094         selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
1095     }
1096     endMacro();
1097 }
1098 
1099 #include "moc_worldmodel.cpp"