Skip to content

Commit

Permalink
Added undo features for all crystal operations
Browse files Browse the repository at this point in the history
Added undo and redo commands for all crystal operations in RWMolecule.
They are also now all called so that the undo stack gets updated with
the changes.
  • Loading branch information
psavery committed Jun 25, 2016
1 parent ddf16f2 commit 85e9bdd
Show file tree
Hide file tree
Showing 8 changed files with 410 additions and 58 deletions.
1 change: 0 additions & 1 deletion avogadro/core/crystaltools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,6 @@ bool CrystalTools::setCellMatrix(Molecule &molecule,
const Matrix3 &newCellColMatrix,
Options opt)
{

if (opt & TransformAtoms && molecule.unitCell()) {
const Matrix3 xform(newCellColMatrix
* molecule.unitCell()->cellMatrix().inverse());
Expand Down
301 changes: 301 additions & 0 deletions avogadro/qtgui/rwmolecule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
#include <algorithm>
#include <cassert>

#include <avogadro/core/avospglib.h>

namespace Avogadro {
namespace QtGui {

using Core::Array;
using Core::AtomHybridization;
using Core::CrystalTools;
using Core::UnitCell;

// Base class for all undo commands used by this class.
// Used to expose molecule internals without needing to add explicit friendships
Expand Down Expand Up @@ -907,6 +911,303 @@ bool RWMolecule::setBondPair(Index bondId, const std::pair<Index, Index> &pair)
return true;
}

namespace {
class AddUnitCellCommand : public RWMolecule::UndoCommand
{
UnitCell m_newUnitCell;
public:
AddUnitCellCommand(RWMolecule &m, const UnitCell &newUnitCell)
: UndoCommand(m), m_newUnitCell(newUnitCell)
{
}

void redo() AVO_OVERRIDE
{
m_mol.molecule().setUnitCell(new UnitCell(m_newUnitCell));
}

void undo() AVO_OVERRIDE
{
m_mol.molecule().setUnitCell(NULL);
}
};
} // end anon namespace

void RWMolecule::addUnitCell()
{
// If there is already a unit cell, there is nothing to do
if (m_molecule.unitCell())
return;

UnitCell *cell = new UnitCell;
cell->setCellParameters(static_cast<Real>(3.0),
static_cast<Real>(3.0),
static_cast<Real>(3.0),
static_cast<Real>(90.0) * DEG_TO_RAD,
static_cast<Real>(90.0) * DEG_TO_RAD,
static_cast<Real>(90.0) * DEG_TO_RAD);
m_molecule.setUnitCell(cell);

AddUnitCellCommand *comm = new AddUnitCellCommand(*this,
*m_molecule.unitCell());
comm->setText(tr("Add Unit Cell"));
m_undoStack.push(comm);
emitChanged(Molecule::UnitCell | Molecule::Added);
}

namespace {
class RemoveUnitCellCommand : public RWMolecule::UndoCommand
{
UnitCell m_oldUnitCell;
public:
RemoveUnitCellCommand(RWMolecule &m, const UnitCell &oldUnitCell)
: UndoCommand(m), m_oldUnitCell(oldUnitCell)
{
}

void redo() AVO_OVERRIDE
{
m_mol.molecule().setUnitCell(NULL);
}

void undo() AVO_OVERRIDE
{
m_mol.molecule().setUnitCell(new UnitCell(m_oldUnitCell));
}
};
} // end anon namespace

void RWMolecule::removeUnitCell()
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

RemoveUnitCellCommand *comm = new RemoveUnitCellCommand(
*this,
*m_molecule.unitCell());
comm->setText(tr("Remove Unit Cell"));
m_undoStack.push(comm);

m_molecule.setUnitCell(NULL);
emitChanged(Molecule::UnitCell | Molecule::Removed);
}

namespace {
class ModifyMoleculeCommand : public RWMolecule::UndoCommand
{
Molecule m_oldMolecule;
Molecule m_newMolecule;
public:
ModifyMoleculeCommand(RWMolecule &m,
const Molecule &oldMolecule,
const Molecule &newMolecule)
: UndoCommand(m), m_oldMolecule(oldMolecule), m_newMolecule(newMolecule)
{
}

void redo() AVO_OVERRIDE
{
m_mol.molecule() = m_newMolecule;
}

void undo() AVO_OVERRIDE
{
m_mol.molecule() = m_oldMolecule;
}
};
} // end anon namespace

void RWMolecule::modifyMolecule(const Molecule &newMolecule,
Molecule::MoleculeChanges changes,
const QString &undoText)
{
ModifyMoleculeCommand *comm = new ModifyMoleculeCommand(*this,
m_molecule,
newMolecule);

comm->setText(undoText);
m_undoStack.push(comm);

m_molecule = newMolecule;
emitChanged(changes);
}

void RWMolecule::editUnitCell(Matrix3 cellMatrix,
CrystalTools::Options options)
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

// Make a copy of the molecule to edit so we can store the old one
// If the user has "TransformAtoms" set in the options, then
// the atom positions will move as well.
Molecule newMolecule = m_molecule;
CrystalTools::setCellMatrix(newMolecule, cellMatrix, options);

// We will just modify the whole molecule since there may be many changes
Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Modified;
// If TransformAtoms is set in the options, then the atoms may be modified
// as well.
if (options & CrystalTools::TransformAtoms)
changes |= Molecule::Atoms | Molecule::Modified;
QString undoText = tr("Edit Unit Cell");

modifyMolecule(newMolecule, changes, undoText);
}

void RWMolecule::wrapAtomsToCell()
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

Core::Array<Vector3> oldPos = m_molecule.atomPositions3d();
CrystalTools::wrapAtomsToUnitCell(m_molecule);
Core::Array<Vector3> newPos = m_molecule.atomPositions3d();

SetPositions3dCommand *comm = new SetPositions3dCommand(*this, oldPos,
newPos);
comm->setText(tr("Wrap Atoms to Cell"));
m_undoStack.push(comm);

Molecule::MoleculeChanges changes = Molecule::Atoms | Molecule::Modified;
emitChanged(changes);
}

void RWMolecule::setCellVolume(double newVolume,
CrystalTools::Options options)
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

// Make a copy of the molecule to edit so we can store the old one
// The unit cell and atom positions may change
Molecule newMolecule = m_molecule;

CrystalTools::setVolume(newMolecule, newVolume, options);

// We will just modify the whole molecule since there may be many changes
Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Modified;
if (options & CrystalTools::TransformAtoms)
changes |= Molecule::Atoms | Molecule::Modified;
QString undoText = tr("Scale Cell Volume");

modifyMolecule(newMolecule, changes, undoText);
}

void RWMolecule::buildSupercell(unsigned int a, unsigned int b, unsigned int c)
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

// Make a copy of the molecule to edit so we can store the old one
// The unit cell and atom positions may change
Molecule newMolecule = m_molecule;

CrystalTools::buildSupercell(newMolecule, a, b, c);

// We will just modify the whole molecule since there may be many changes
Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Modified |
Molecule::Atoms | Molecule::Added;
QString undoText = tr("Build Super Cell");

modifyMolecule(newMolecule, changes, undoText);
}

void RWMolecule::niggliReduceCell()
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

// Make a copy of the molecule to edit so we can store the old one
// The unit cell and atom positions may change
Molecule newMolecule = m_molecule;

// We need to perform all three of these operations...
CrystalTools::niggliReduce(newMolecule, CrystalTools::TransformAtoms);
CrystalTools::rotateToStandardOrientation(newMolecule,
CrystalTools::TransformAtoms);
CrystalTools::wrapAtomsToUnitCell(newMolecule);

// We will just modify the whole molecule since there may be many changes
Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms |
Molecule::Modified;
QString undoText = tr("Niggli Reduction");

modifyMolecule(newMolecule, changes, undoText);
}

void RWMolecule::rotateCellToStandardOrientation()
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return;

// Store a copy of the old molecule
// The atom positions may move as well.
Molecule newMolecule = m_molecule;

CrystalTools::rotateToStandardOrientation(newMolecule,
CrystalTools::TransformAtoms);

// Since most components of the molecule will be modified (atom positions
// and the unit cell), we will just modify the whole thing...
Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms |
Molecule::Modified;
QString undoText = tr("Rotate to Standard Orientation");

modifyMolecule(newMolecule, changes, undoText);
}

bool RWMolecule::reduceCellToPrimitive(double cartTol)
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return false;

// Make a copy of the molecule to edit so we can store the old one
// The unit cell, atom positions, and numbers of atoms may change
Molecule newMolecule = m_molecule;

if (!Core::AvoSpglib::reduceToPrimitive(newMolecule, cartTol))
return false;

// Since most components of the molecule will be modified,
// we will just modify the whole thing...
Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms |
Molecule::Added;
QString undoText = tr("Reduce to Primitive");

modifyMolecule(newMolecule, changes, undoText);
return true;
}

bool RWMolecule::conventionalizeCell(double cartTol)
{
// If there is no unit cell, there is nothing to do
if (!m_molecule.unitCell())
return false;

// Make a copy of the molecule to edit so we can store the old one
// The unit cell, atom positions, and numbers of atoms may all change
Molecule newMolecule = m_molecule;

if (!Core::AvoSpglib::conventionalizeCell(newMolecule, cartTol))
return false;

Molecule::MoleculeChanges changes = Molecule::UnitCell | Molecule::Atoms |
Molecule::Added;
QString undoText = tr("Conventionalize Cell");

modifyMolecule(newMolecule, changes, undoText);
return true;
}

void RWMolecule::emitChanged(unsigned int change)
{
m_molecule.emitChanged(change);
Expand Down
Loading

0 comments on commit 85e9bdd

Please sign in to comment.