diff --git a/src/classes/atom.cpp b/src/classes/atom.cpp index 14ecb6644e..b0f0901091 100644 --- a/src/classes/atom.cpp +++ b/src/classes/atom.cpp @@ -150,3 +150,9 @@ void AtomBase::deserialise(const SerialisedValue &node) set(Deserialisable::deser(node.at("z")), Deserialisable::deser(node.at("r")), Deserialisable::deser_or(node, "q", 0)); } + +// Set cell in which the atom exists +void AtomBase::setCell(Cell *cell) { cell_ = cell; } + +// Return cell in which the atom exists +Cell *AtomBase::cell() const { return cell_; } diff --git a/src/classes/atom.h b/src/classes/atom.h index 4357241f5e..4acca28732 100644 --- a/src/classes/atom.h +++ b/src/classes/atom.h @@ -9,6 +9,8 @@ #include "data/elements.h" #include "math/vector3.h" +class Cell; + // Atom Base class AtomBase { @@ -26,6 +28,8 @@ class AtomBase int index_{-1}; // Atom type index in parent object int atomTypeIndex_{AtomConstants::TypeIndex::Ignore}; + // Cell in which the atom exists + Cell *cell_{nullptr}; public: // Set basic properties @@ -52,6 +56,10 @@ class AtomBase void setAtomTypeIndex(int id); // Return associated atom type index int atomTypeIndex() const; + // Set cell in which the atom exists + void setCell(Cell *cell); + // Return cell in which the atom exists + Cell *cell() const; /* * General Connectivity diff --git a/src/classes/cell.cpp b/src/classes/cell.cpp index 9b7a392252..91e1da615e 100644 --- a/src/classes/cell.cpp +++ b/src/classes/cell.cpp @@ -35,14 +35,14 @@ const Vector3 &Cell::centre() const { return centre_; } */ // Return vector of contained Atoms -std::vector &Cell::atoms() { return atoms_; } -const std::vector &Cell::atoms() const { return atoms_; } +std::vector &Cell::atoms() { return atoms_; } +const std::vector &Cell::atoms() const { return atoms_; } // Return number of Atoms in list int Cell::nAtoms() const { return atoms_.size(); } // Add atom to Cell -void Cell::addAtom(ConfigurationAtom *atom) +void Cell::addAtom(AtomBase *atom) { assert(atom); assert(!atom->cell()); @@ -52,7 +52,7 @@ void Cell::addAtom(ConfigurationAtom *atom) } // Remove Atom from Cell -void Cell::removeAtom(ConfigurationAtom *atom) +void Cell::removeAtom(AtomBase *atom) { auto it = std::find(atoms_.begin(), atoms_.end(), atom); assert(it != atoms_.end()); diff --git a/src/classes/cell.h b/src/classes/cell.h index a0bd8e78ef..dbd65dbdb0 100644 --- a/src/classes/cell.h +++ b/src/classes/cell.h @@ -47,18 +47,18 @@ class Cell */ private: // Vector of Atoms contained in this Cell - std::vector atoms_; + std::vector atoms_; public: // Return array of contained Atoms - std::vector &atoms(); - const std::vector &atoms() const; + std::vector &atoms(); + const std::vector &atoms() const; // Return number of Atoms in array int nAtoms() const; // Add atom to Cell - void addAtom(ConfigurationAtom *atom); + void addAtom(AtomBase *atom); // Remove Atom from Cell - void removeAtom(ConfigurationAtom *atom); + void removeAtom(AtomBase *atom); // Clear all atoms from cell void clearAtoms(); }; diff --git a/src/classes/cellArray.cpp b/src/classes/cellArray.cpp index ad9f9fef7e..6d85850467 100644 --- a/src/classes/cellArray.cpp +++ b/src/classes/cellArray.cpp @@ -165,6 +165,16 @@ const std::vector &CellArray::neighbours(const Cell &cell) const // Return vector of all unique cell neighbour pairs const std::vector &CellArray::getCellNeighbourPairs() const { return neighbourPairs_; } +// Return vector of all unique cell neighbour pairs with self cell pairs +const std::vector CellArray::getCellNeighbourPairsWithSelf() const +{ + auto result = neighbourPairs_; + for (auto &cell : cells_) + result.emplace_back(cell, cell, false); + + return result; +} + // Return whether minimum image calculation is required between the supplied cells bool CellArray::minimumImageRequired(const Cell &a, const Cell &b) const { diff --git a/src/classes/cellArray.h b/src/classes/cellArray.h index 9879dff8a9..40b8d0da93 100644 --- a/src/classes/cellArray.h +++ b/src/classes/cellArray.h @@ -110,6 +110,8 @@ class CellArray const std::vector &neighbours(const Cell &cell) const; // Return vector of all unique cell neighbour pairs const std::vector &getCellNeighbourPairs() const; + // Return vector of all unique cell neighbour pairs with self cell pairs + const std::vector getCellNeighbourPairsWithSelf() const; // Return whether minimum image calculation is required between the supplied cells bool minimumImageRequired(const Cell &a, const Cell &b) const; diff --git a/src/classes/configurationAtom.cpp b/src/classes/configurationAtom.cpp index af4af168ca..f5d050bb20 100644 --- a/src/classes/configurationAtom.cpp +++ b/src/classes/configurationAtom.cpp @@ -23,12 +23,6 @@ void ConfigurationAtom::setMolecule(std::shared_ptr mol) { molecule_ = // Return Molecule in which this Atom exists const std::shared_ptr &ConfigurationAtom::molecule() const { return molecule_; } -// Set cell in which the atom exists -void ConfigurationAtom::setCell(Cell *cell) { cell_ = cell; } - -// Return cell in which the atom exists -Cell *ConfigurationAtom::cell() const { return cell_; } - /* * Intramolecular Information */ diff --git a/src/classes/configurationAtom.h b/src/classes/configurationAtom.h index 07598e34d1..6b4126548c 100644 --- a/src/classes/configurationAtom.h +++ b/src/classes/configurationAtom.h @@ -24,8 +24,6 @@ class ConfigurationAtom : public Atom const SpeciesAtom *speciesAtom_{nullptr}; // Molecule in which this Atom exists std::shared_ptr molecule_{nullptr}; - // Cell in which the atom exists - Cell *cell_{nullptr}; public: // Set SpeciesAtom that this Atom represents @@ -36,10 +34,6 @@ class ConfigurationAtom : public Atom void setMolecule(std::shared_ptr mol); // Return Molecule in which this Atom exists const std::shared_ptr &molecule() const; - // Set cell in which the atom exists - void setCell(Cell *cell); - // Return cell in which the atom exists - Cell *cell() const; /* * Intramolecular Information diff --git a/src/gui2/qml/DissolveMain.qml b/src/gui2/qml/DissolveMain.qml index 3a8b8239bd..7848c47ff3 100644 --- a/src/gui2/qml/DissolveMain.qml +++ b/src/gui2/qml/DissolveMain.qml @@ -19,20 +19,13 @@ import "../../Dissolve" ApplicationWindow { id: dissolveWindow + property Dialog quickRunDialog: null property vector3d scale: Qt.vector3d(Math.min(graphView.width / 2.5, graphView.height / 2.5), Math.min(graphView.width / 2.5, graphView.height / 2.5), 200) height: Screen.height - width: Screen.width title: "Dissolve" visible: true - - property Dialog quickRunDialog: null - Component { - id: quickRunDialogComponent - - QuickRunDialog { - } - } + width: Screen.width /* * Dissolve2 Main Menu @@ -50,51 +43,45 @@ ApplicationWindow { title: "&File" MenuItem { - text: "&New project" - - ToolTip.visible: hovered ToolTip.delay: Application.styleHints.mousePressAndHoldInterval ToolTip.text: "Create a new Dissolve project" + ToolTip.visible: hovered + text: "&New project" } MenuItem { + ToolTip.delay: Application.styleHints.mousePressAndHoldInterval + ToolTip.text: "Load an existing Dissolve graph from a TOML file" + ToolTip.visible: hovered //shortcut: "Ctrl+O" text: "&Load project from TOML" onTriggered: openDialog.open() - - ToolTip.visible: hovered - ToolTip.delay: Application.styleHints.mousePressAndHoldInterval - ToolTip.text: "Load an existing Dissolve graph from a TOML file" } MenuItem { + ToolTip.delay: Application.styleHints.mousePressAndHoldInterval + ToolTip.text: "Save the current Dissolve graph to a TOML file" + ToolTip.visible: hovered //shortcut: "Ctrl+S" text: "&Save project to TOML" onTriggered: saveDialog.open() - - ToolTip.visible: hovered - ToolTip.delay: Application.styleHints.mousePressAndHoldInterval - ToolTip.text: "Save the current Dissolve graph to a TOML file" } - - MenuSeparator {} - + MenuSeparator { + } MenuItem { text: "Load Restart Point..." } MenuItem { text: "Save Restart Point..." } - - MenuSeparator {} - + MenuSeparator { + } MenuItem { //shortcut: "Ctrl+X" text: "Close project" } - - MenuSeparator {} - + MenuSeparator { + } MenuItem { //shortcut: "Ctrl+Q" text: "&Quit Dissolve" @@ -112,35 +99,33 @@ ApplicationWindow { title: "&Graph" DissolveMenuItem { + iconPath: "qrc:/DissolveIconsModule/play.svg" + dissolveAction: Action { - text: "&Run" shortcut: "Ctrl+Enter" + text: "&Run" + onTriggered: dissolveWindow.quickRunDialog.open() } - iconPath: "qrc:/DissolveIconsModule/play.svg" } - - MenuSeparator{} - + MenuSeparator { + } MenuItem { - //shortcut: "Ctrl+A" - text: "&Add Node" - - ToolTip.visible: hovered ToolTip.delay: Application.styleHints.mousePressAndHoldInterval ToolTip.text: "Search the Node registry by node name, and add the selection to the graph" //onTriggered: dissolveWindow.nodeSearchDialog.open() + ToolTip.visible: hovered + //shortcut: "Ctrl+A" + text: "&Add Node" + } + MenuSeparator { } - - MenuSeparator{} - MenuItem { - text: "&EPSR" - - ToolTip.visible: hovered ToolTip.delay: Application.styleHints.mousePressAndHoldInterval ToolTip.text: "Open the EPSR Manager" + ToolTip.visible: hovered + text: "&EPSR" } } @@ -153,24 +138,30 @@ ApplicationWindow { title: "&View" MenuItem { - //shortcut: "Ctrl+N" - text: "&Nodes" - - ToolTip.visible: hovered ToolTip.delay: Application.styleHints.mousePressAndHoldInterval ToolTip.text: "View all available Nodes" + ToolTip.visible: hovered + //shortcut: "Ctrl+N" + text: "&Nodes" } } } + Component { + id: quickRunDialogComponent + + QuickRunDialog { + } + } + /* * TabBar */ TabBar { id: tabBar - width: parent.width currentIndex: 2 + width: parent.width // DEFAULT TABS TabButton { @@ -193,6 +184,7 @@ ApplicationWindow { */ StackLayout { id: applicationTabStack + anchors.bottom: parent.bottom anchors.top: tabBar.bottom currentIndex: tabBar.currentIndex @@ -226,7 +218,10 @@ ApplicationWindow { id: graphModel graph: dissolve.graph - Component.onCompleted: dissolveWindow.quickRunDialog = quickRunDialogComponent.createObject(dissolveWindow, {graphModel : graphModel}) + + Component.onCompleted: dissolveWindow.quickRunDialog = quickRunDialogComponent.createObject(dissolveWindow, { + graphModel: graphModel + }) } Pane { id: toolBar @@ -291,8 +286,13 @@ ApplicationWindow { parameterEndPointsModel: graphModel.parameterEndPoints rootGraphModel: graphModel + Component.onCompleted: { + graphModel.canvasDimensions = Qt.size(graph.width, graph.height); + } + Repeater { id: graphDelegateRepeater + model: graph.nodeModel delegate: Component { @@ -308,10 +308,6 @@ ApplicationWindow { } } } - - Component.onCompleted: { - graphModel.canvasDimensions = Qt.size(graph.width, graph.height) - } } } } diff --git a/src/gui2/qml/controls/DissolveMenuItem.qml b/src/gui2/qml/controls/DissolveMenuItem.qml index d21dac2759..5f08a0800e 100644 --- a/src/gui2/qml/controls/DissolveMenuItem.qml +++ b/src/gui2/qml/controls/DissolveMenuItem.qml @@ -15,20 +15,17 @@ MenuItem { Rectangle { id: iconFrame - visible: root.iconPath ? true : false readonly property var sideLength: menuItemRowLayout.height - Layout.preferredWidth: sideLength Layout.preferredHeight: sideLength - - radius: 3 - color: "transparent" - - border.width: 1 + Layout.preferredWidth: sideLength border.color: "grey" - + border.width: 1 clip: true + color: "transparent" + radius: 3 + visible: root.iconPath ? true : false Image { clip: true @@ -40,10 +37,11 @@ MenuItem { } Text { id: titleText - text: root.dissolveAction.text.replace("&", "") - Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true elide: Text.ElideRight + text: root.dissolveAction.text.replace("&", "") } } -} \ No newline at end of file +} diff --git a/src/gui2/qml/dialogs/QuickRunDialog.qml b/src/gui2/qml/dialogs/QuickRunDialog.qml index b745fd070f..ed075879ef 100644 --- a/src/gui2/qml/dialogs/QuickRunDialog.qml +++ b/src/gui2/qml/dialogs/QuickRunDialog.qml @@ -10,34 +10,32 @@ Dialog { required property GraphModel graphModel property string startNode: input.text - x: dissolveWindow.width / 2 - y: dissolveWindow.height / 2 - height: implicitHeight - width: implicitWidth - standardButtons: Dialog.Ok | Dialog.Cancel + width: implicitWidth + x: dissolveWindow.width / 2 + y: dissolveWindow.height / 2 contentItem: Item { anchors.fill: parent focus: true - Keys.onReturnPressed: accept() Keys.onEnterPressed: accept() + Keys.onReturnPressed: accept() TextField { id: input + anchors.fill: parent font.pixelSize: 14 placeholderText: "Enter a node name to run graph from..." - } + } } onAccepted: { if (graphModel.isValidNode(startNode)) - graphModel.run(startNode) - - root.close() + graphModel.run(startNode); + root.close(); } onRejected: root.close() -} \ No newline at end of file +} diff --git a/src/gui2/qml/nodeGraph/GraphDelegate.qml b/src/gui2/qml/nodeGraph/GraphDelegate.qml index 68825d2b63..8d0da46bc7 100644 --- a/src/gui2/qml/nodeGraph/GraphDelegate.qml +++ b/src/gui2/qml/nodeGraph/GraphDelegate.qml @@ -14,75 +14,83 @@ NodeBox { id: root property double endX: x + property bool hasErrors: false + property string hint: "" + property NodeMessages messageStore: nodeMessages property double midY: y + height / 2 property variant rootGraphModel property double startX: x + width - property string hint: "" - property NodeMessages messageStore: nodeMessages - property bool hasErrors: false signal descended(int idx) signal edgeCreated(string srcNode, string srcOutput, string tgtNode, string tgtInput) - image: icon - nodeName: name - coords: Qt.point(posX, posY) + function closeMessages() { + messages.close(); + warnings.close(); + errors.close(); + } + coords: Qt.point(posX, posY) height: nodeColumnLayout.implicitHeight + label.height + padding - width: 250 + image: icon + nodeName: name // Ensure that we only display this box for valid node items visible: (icon != null) && !(isRootNode && (nodeName == "Outputs" || nodeName == "Inputs")) + width: 250 x: coords.x y: coords.y + onDeleted: rootGraphModel.deleteNode(index) + NodeMessages { id: nodeMessages + graphModel: root.rootGraphModel nodeName: root.nodeName parent: root } Rectangle { id: errorIndicator - width: 20 - height: width - radius: width / 2 - - opacity: 0.8 - color: "red" - border.width: 2 - border.color: "grey" - visible: root.hasErrors - anchors.top: parent.top + ToolTip.text: "There are errors associated with this node. Check the logs." + ToolTip.visible: errorIndicatorHover.hovered anchors.right: parent.right - anchors.topMargin: -50 anchors.rightMargin: -15 + anchors.top: parent.top + anchors.topMargin: -50 + border.color: "grey" + border.width: 2 + color: "red" + height: width + opacity: 0.8 + radius: width / 2 + visible: root.hasErrors + width: 20 HoverHandler { id: errorIndicatorHover - } - - ToolTip.visible: errorIndicatorHover.hovered - ToolTip.text: "There are errors associated with this node. Check the logs." + } } - onDeleted: rootGraphModel.deleteNode(index) - Menu { id: nodePopupMenu DissolveMenuItem { + iconPath: "qrc:/DissolveIconsModule/play.svg" + dissolveAction: Action { - text: "&Run" shortcut: "Ctrl+Enter" + text: "&Run" + onTriggered: rootGraphModel.run(nodeName) } - iconPath: "qrc:/DissolveIconsModule/play.svg" } - MenuSeparator {} + MenuSeparator { + } MenuItem { id: messagesMenuItem + text: "Messages" HoverHandler { @@ -90,26 +98,20 @@ NodeBox { onHoveredChanged: { if (hovered) { - root.closeMessages() - root.messageStore.updateMessages() - - const p = messagesMenuItem.mapToItem( - Overlay.overlay, - messagesMenuItem.width, - 0 - ) - - messages.x = p.x - messages.y = p.y - - root.messageStore.updateMessages() - messages.open() + root.closeMessages(); + root.messageStore.updateMessages(); + const p = messagesMenuItem.mapToItem(Overlay.overlay, messagesMenuItem.width, 0); + messages.x = p.x; + messages.y = p.y; + root.messageStore.updateMessages(); + messages.open(); } } } } MenuItem { id: warningsMenuItem + text: "Warnings" HoverHandler { @@ -117,26 +119,20 @@ NodeBox { onHoveredChanged: { if (hovered) { - root.closeMessages() - root.messageStore.updateMessages() - - const p = warningsMenuItem.mapToItem( - Overlay.overlay, - warningsMenuItem.width, - 0 - ) - - warnings.x = p.x - warnings.y = p.y - - root.messageStore.updateMessages() - warnings.open() + root.closeMessages(); + root.messageStore.updateMessages(); + const p = warningsMenuItem.mapToItem(Overlay.overlay, warningsMenuItem.width, 0); + warnings.x = p.x; + warnings.y = p.y; + root.messageStore.updateMessages(); + warnings.open(); } } } } MenuItem { id: errorsMenuItem + text: "Errors" HoverHandler { @@ -144,45 +140,30 @@ NodeBox { onHoveredChanged: { if (hovered) { - root.closeMessages() - root.messageStore.updateMessages() - - const p = errorsMenuItem.mapToItem( - Overlay.overlay, - errorsMenuItem.width, - 0 - ) - - errors.x = p.x - errors.y = p.y - - root.messageStore.updateMessages() - errors.open() + root.closeMessages(); + root.messageStore.updateMessages(); + const p = errorsMenuItem.mapToItem(Overlay.overlay, errorsMenuItem.width, 0); + errors.x = p.x; + errors.y = p.y; + root.messageStore.updateMessages(); + errors.open(); } } } } } - function closeMessages() - { - messages.close() - warnings.close() - errors.close() - } Popup { id: messages - width: 300 height: 250 - - parent: Overlay.overlay - padding: 4 + parent: Overlay.overlay + width: 300 HoverHandler { onHoveredChanged: { if (!hovered) - messages.close() + messages.close(); } } ScrollView { @@ -195,20 +176,20 @@ NodeBox { delegate: ItemDelegate { id: messageDelegate - width: messagesScrollView.width hoverEnabled: true + width: messagesScrollView.width + background: Rectangle { + color: messageDelegate.hovered ? "#444444" : "transparent" + } contentItem: Text { - text: message color: messageDelegate.hovered ? "white" : "grey" - font.bold: messageDelegate.hovered elide: Text.ElideRight + font.bold: messageDelegate.hovered + text: message verticalAlignment: Text.AlignVCenter } - background: Rectangle { - color: messageDelegate.hovered ? "#444444" : "transparent" - } } } } @@ -216,17 +197,15 @@ NodeBox { Popup { id: warnings - width: 300 height: 250 - - parent: Overlay.overlay - padding: 4 + parent: Overlay.overlay + width: 300 HoverHandler { onHoveredChanged: { if (!hovered) - warnings.close() + warnings.close(); } } ScrollView { @@ -239,20 +218,20 @@ NodeBox { delegate: ItemDelegate { id: messageDelegate - width: warningsScrollView.width hoverEnabled: true + width: warningsScrollView.width + background: Rectangle { + color: messageDelegate.hovered ? "#444444" : "transparent" + } contentItem: Text { - text: message color: "orange" - font.bold: messageDelegate.hovered elide: Text.ElideRight + font.bold: messageDelegate.hovered + text: message verticalAlignment: Text.AlignVCenter } - background: Rectangle { - color: messageDelegate.hovered ? "#444444" : "transparent" - } } } } @@ -260,17 +239,15 @@ NodeBox { Popup { id: errors - width: 300 height: 250 - + padding: 4 parent: Overlay.overlay + width: 300 - padding: 4 - HoverHandler { onHoveredChanged: { if (!hovered) - errors.close() + errors.close(); } } ScrollView { @@ -283,37 +260,37 @@ NodeBox { delegate: ItemDelegate { id: messageDelegate - width: errorsScrollView.width hoverEnabled: true + width: errorsScrollView.width + background: Rectangle { + color: messageDelegate.hovered ? "#444444" : "transparent" + } contentItem: Text { - text: message color: "red" - font.bold: messageDelegate.hovered elide: Text.ElideRight + font.bold: messageDelegate.hovered + text: message verticalAlignment: Text.AlignVCenter } - background: Rectangle { - color: messageDelegate.hovered ? "#444444" : "transparent" - } } } } } TapHandler { id: tapHandler + acceptedButtons: Qt.RightButton onTapped: nodePopupMenu.popup(point.position.x, point.position.y) } - ColumnLayout { id: nodeColumnLayout anchors.left: header.left - anchors.right: header.right anchors.leftMargin: 8 + anchors.right: header.right anchors.rightMargin: 8 spacing: 8 @@ -334,6 +311,7 @@ NodeBox { Shape { id: inputShape + property string nodeName: root.nodeName property string title: name @@ -363,11 +341,12 @@ NodeBox { } DropArea { id: inputDropArea + readonly property var parentNodeBox: root + anchors.fill: parent Component.onCompleted: root.rootGraphModel.initialiseInputEndPoints(parent.nodeName, parent.title, inputDropArea) - onDropped: function (event) { edgeCreated(event.source.parent.nodeName, event.source.parent.title, parent.nodeName, parent.title); } @@ -386,7 +365,9 @@ NodeBox { Text { id: inputText + property string info: description + Layout.alignment: Qt.AlignLeft Layout.column: 1 Layout.row: index @@ -397,11 +378,12 @@ NodeBox { MouseArea { id: inputMouseArea - anchors.fill: parent - hoverEnabled: true + + ToolTip.delay: 500 ToolTip.text: "Input:
" + inputText.info ToolTip.visible: containsMouse - ToolTip.delay: 500 + anchors.fill: parent + hoverEnabled: true } } } @@ -428,6 +410,7 @@ NodeBox { Shape { id: outputShape + property string nodeName: root.nodeName property string title: name @@ -457,11 +440,12 @@ NodeBox { } DropArea { id: outputDropArea + readonly property var parentNodeBox: root - anchors.fill: parent - Component.onCompleted: root.rootGraphModel.initialiseOutputEndPoints(parent.nodeName, parent.title, outputDropArea); + anchors.fill: parent + Component.onCompleted: root.rootGraphModel.initialiseOutputEndPoints(parent.nodeName, parent.title, outputDropArea) onDropped: function (event) { edgeCreated(parent.nodeName, parent.title, event.source.parent.nodeName, event.source.parent.title); } @@ -480,7 +464,9 @@ NodeBox { Text { id: outputText + property string info: description + Layout.alignment: Qt.AlignRight Layout.column: 3 Layout.row: index @@ -491,11 +477,12 @@ NodeBox { MouseArea { id: outputMouseArea - anchors.fill: parent - hoverEnabled: true + + ToolTip.delay: 500 ToolTip.text: "Output:
" + outputText.info ToolTip.visible: containsMouse - ToolTip.delay: 500 + anchors.fill: parent + hoverEnabled: true } } } @@ -520,7 +507,9 @@ NodeBox { Text { id: optionText + property string info: description + Layout.alignment: Qt.AlignLeft Layout.column: 0 Layout.row: index @@ -528,11 +517,12 @@ NodeBox { MouseArea { id: optionMouseArea - anchors.fill: parent - hoverEnabled: true + + ToolTip.delay: 500 ToolTip.text: "Option:
" + optionText.info ToolTip.visible: containsMouse - ToolTip.delay: 500 + anchors.fill: parent + hoverEnabled: true } } } diff --git a/src/gui2/qml/nodeGraph/GraphView.qml b/src/gui2/qml/nodeGraph/GraphView.qml index 7057fc0d56..427cde78d7 100644 --- a/src/gui2/qml/nodeGraph/GraphView.qml +++ b/src/gui2/qml/nodeGraph/GraphView.qml @@ -13,18 +13,18 @@ Pane { property variant parameterEndPointsModel property variant rootGraphModel - Component.onCompleted: nodeRegistry.setGraphModel(rootGraphModel); + Component.onCompleted: nodeRegistry.setGraphModel(rootGraphModel) MouseArea { id: ctxMenuCatcher + property point mousePos: Qt.point(ctxMenuCatcher.mouseX, ctxMenuCatcher.mouseY) + acceptedButtons: Qt.RightButton anchors.fill: parent onClicked: contextMenu.popup() - property point mousePos: Qt.point(ctxMenuCatcher.mouseX, ctxMenuCatcher.mouseY) - Menu { id: contextMenu @@ -32,38 +32,42 @@ Pane { model: nodeRegistry.categories() delegate: Item { - id: menuDelegateItem - required property var display - property Menu innerMenu: innerMenuComponent.createObject(parent) + id: menuDelegateItem + + required property var display + property Menu innerMenu: innerMenuComponent.createObject(parent) + + Component { + id: innerMenuComponent + + Menu { + id: nodeCategoryMenu - Component { - id: innerMenuComponent + title: menuDelegateItem.display - Menu { - id: nodeCategoryMenu + Repeater { + model: nodeRegistry.nodeNames(menuDelegateItem.display) - title: menuDelegateItem.display + delegate: MenuItem { + required property var modelData - Repeater { - model: nodeRegistry.nodeNames(menuDelegateItem.display) + ToolTip.delay: 500 + ToolTip.text: modelData.description + ToolTip.visible: hovered + text: modelData.name - delegate: MenuItem { - required property var modelData - text: modelData.name - onClicked: graphRoot.rootGraphModel.emplace_back(ctxMenuCatcher.mousePos.x, ctxMenuCatcher.mousePos.y, modelData.name, nodeRegistry.uniqueNodeName(modelData.name), false) - ToolTip.text: modelData.description - ToolTip.visible: hovered - ToolTip.delay: 500 - } + onClicked: graphRoot.rootGraphModel.emplace_back(ctxMenuCatcher.mousePos.x, ctxMenuCatcher.mousePos.y, modelData.name, nodeRegistry.uniqueNodeName(modelData.name), false) } } } + } } + onItemAdded: (index, item) => { - contextMenu.addMenu(item.innerMenu) + contextMenu.addMenu(item.innerMenu); } onItemRemoved: (index, item) => { - contextMenu.removeMenu(item.innerMenu) + contextMenu.removeMenu(item.innerMenu); } } } @@ -76,20 +80,16 @@ Pane { id: edgeShape required property Item sourceDropArea - required property Item targetDropArea - property point sourcePos: { - sourceDropArea.parentNodeBox.x - sourceDropArea.parentNodeBox.y - - return sourceDropArea.mapToItem(graphRoot, Qt.point(0, 0)) + sourceDropArea.parentNodeBox.x; + sourceDropArea.parentNodeBox.y; + return sourceDropArea.mapToItem(graphRoot, Qt.point(0, 0)); } - + required property Item targetDropArea property point targetPos: { - targetDropArea.parentNodeBox.x - targetDropArea.parentNodeBox.y - - return targetDropArea.mapToItem(graphRoot, Qt.point(0, 0)) + targetDropArea.parentNodeBox.x; + targetDropArea.parentNodeBox.y; + return targetDropArea.mapToItem(graphRoot, Qt.point(0, 0)); } z: -10 diff --git a/src/gui2/qml/nodeGraph/NodeBox.qml b/src/gui2/qml/nodeGraph/NodeBox.qml index 635f6e8114..340c91299d 100644 --- a/src/gui2/qml/nodeGraph/NodeBox.qml +++ b/src/gui2/qml/nodeGraph/NodeBox.qml @@ -6,98 +6,72 @@ import Qt.labs.qmlmodels GroupBox { id: root - default property alias content: contentArea.data - property bool isMoving: dragHandler.active property double baseY: header.height - property string image - property string nodeName + default property alias content: contentArea.data property point coords property bool dragActive: false - property bool headerHovered: false property int headerHeight: 32 - - topPadding: headerHeight + padding - padding: 8 + property bool headerHovered: false + property string image + property bool isMoving: dragHandler.active + property string nodeName signal deleted implicitWidth: 250 - + padding: 8 scale: headerHovered ? 1.05 : 1.0 - - Behavior on scale { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } - } + topPadding: headerHeight + padding background: Rectangle { id: creationPulsedBorder anchors.fill: parent - color: "transparent" border.color: "black" border.width: 1 + color: "transparent" radius: 4 z: -1 Component.onCompleted: creationPulsedAnimation.start() } + contentItem: Item { + id: contentArea - SequentialAnimation { - id: creationPulsedAnimation - - NumberAnimation { - target: creationPulsedBorder - property: "border.width" - from: 1 - to: 5 - duration: 300 - easing.type: Easing.OutQuad - } - NumberAnimation { - target: creationPulsedBorder - property: "border.width" - from: 5 - to: 1 - duration: 500 - easing.type: Easing.OutQuad + anchors { + bottom: parent.bottom + left: parent.left + margins: 8 + right: parent.right + top: header.bottom } } - label: Rectangle { id: header - implicitHeight: root.headerHeight - width: root.width - + border.color: root.dragActive ? "dodgerblue" : "grey" + border.width: root.dragActive ? 3 : 1 color: '#a9f0f4ff' + implicitHeight: root.headerHeight radius: 4 - - border.width: root.dragActive ? 3 : 1 - border.color: root.dragActive ? "dodgerblue" : "grey" + width: root.width - RowLayout{ + RowLayout { anchors.fill: parent anchors.leftMargin: 6 anchors.rightMargin: 6 - spacing: 6 Rectangle { id: iconFrame - Layout.preferredWidth: titleLabel.height Layout.preferredHeight: titleLabel.height - - radius: 3 - color: "transparent" - - border.width: 1 + Layout.preferredWidth: titleLabel.height border.color: "grey" - + border.width: 1 clip: true + color: "transparent" + radius: 3 Image { clip: true @@ -124,31 +98,24 @@ GroupBox { id: deleteNodeButtonRectangle anchors.fill: parent + border.color: deleteNodeButton.pressed ? "dodgerblue" : "grey" + border.width: deleteNodeButton.hovered ? 2 : 1 + color: deleteNodeButton.hovered ? "white" : "transparent" radius: 3 - color: deleteNodeButton.hovered - ? "white" - : "transparent" - - border.width: deleteNodeButton.hovered - ? 2 - : 1 - - border.color: deleteNodeButton.pressed - ? "dodgerblue" - : "grey" } contentItem: Text { id: cross - text: "x" - color: parent.pressed ? "white": "red" - font.pointSize: 11 + color: parent.pressed ? "white" : "red" font.bold: false + font.pointSize: 11 + horizontalAlignment: Text.AlignHCenter style: Text.Outline styleColor: "black" - horizontalAlignment: Text.AlignHCenter + text: "x" verticalAlignment: Text.AlignVCenter } + onClicked: root.deleted() } } @@ -163,30 +130,46 @@ GroupBox { */ HoverHandler { id: hoverHandler + target: root onHoveredChanged: root.headerHovered = hovered } DragHandler { id: dragHandler + target: root + onActiveChanged: root.dragActive = active xAxis.onActiveValueChanged: delta => x += delta yAxis.onActiveValueChanged: delta => y += delta - - onActiveChanged: root.dragActive = active + } + } + Behavior on scale { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad } } - contentItem: Item { - id: contentArea + SequentialAnimation { + id: creationPulsedAnimation - anchors { - left: parent.left - right: parent.right - top: header.bottom - bottom: parent.bottom - margins: 8 + NumberAnimation { + duration: 300 + easing.type: Easing.OutQuad + from: 1 + property: "border.width" + target: creationPulsedBorder + to: 5 + } + NumberAnimation { + duration: 500 + easing.type: Easing.OutQuad + from: 5 + property: "border.width" + target: creationPulsedBorder + to: 1 } } } diff --git a/src/kernels/energy.cpp b/src/kernels/energy.cpp index b3fcd21a00..f3b3172df1 100644 --- a/src/kernels/energy.cpp +++ b/src/kernels/energy.cpp @@ -5,6 +5,7 @@ #include "classes/box.h" #include "classes/cell.h" #include "classes/configuration.h" +#include "classes/configurationAtom.h" #include "classes/molecule.h" #include "classes/potentialMap.h" #include "classes/species.h" @@ -46,7 +47,7 @@ Kernel::PairPotentialEnergyValue EnergyKernel::cellEnergy(const Cell &cell, bool for (auto i = 0; i < atoms.size(); ++i) { - auto &ii = atoms[i]; + auto ii = static_cast(atoms[i]); auto molI = ii->molecule(); auto &rI = ii->r(); @@ -54,7 +55,7 @@ Kernel::PairPotentialEnergyValue EnergyKernel::cellEnergy(const Cell &cell, bool for (auto j = i + 1; j < atoms.size(); ++j) { // Calculate rSquared distance between atoms, and check it against the stored cutoff distance - auto &jj = atoms[j]; + auto jj = static_cast(atoms[j]); auto rSq = (rI - jj->r()).magnitudeSq(); if (rSq > cutoffDistanceSquared_) @@ -90,13 +91,13 @@ Kernel::PairPotentialEnergyValue EnergyKernel::cellToCellEnergy(const Cell ¢ // Loop over central cell atoms if (applyMim) { - for (auto &ii : centralAtoms) + for (auto ii : centralAtoms | castView()) { auto molI = ii->molecule(); auto &rI = ii->r(); // Straight loop over other cell atoms - for (const auto &jj : otherAtoms) + for (const auto jj : otherAtoms | castView()) { // Calculate rSquared distance between atoms, and check it against the stored cutoff distance auto rSq = box_.minimumDistanceSquared(rI, jj->r()); @@ -122,13 +123,13 @@ Kernel::PairPotentialEnergyValue EnergyKernel::cellToCellEnergy(const Cell ¢ } else { - for (auto &ii : centralAtoms) + for (auto ii : centralAtoms | castView()) { auto &molI = ii->molecule(); auto &rI = ii->r(); // Straight loop over other cell atoms - for (const auto &jj : otherAtoms) + for (const auto jj : otherAtoms | castView()) { // Calculate rSquared distance between atoms, and check it against the stored cutoff distance auto rSq = (rI - jj->r()).magnitudeSq(); @@ -172,7 +173,7 @@ double EnergyKernel::pairPotentialEnergy(const ConfigurationAtom &i) const return std::accumulate(nbrCellAtoms.begin(), nbrCellAtoms.end(), 0.0, [&i, mimRequired, this](const auto innerAcc, const auto *j) { - auto &jj = *j; + auto &jj = *static_cast(j); // Calculate rSquared distance between atoms, and check it // against the stored cutoff distance @@ -230,7 +231,7 @@ Kernel::PairPotentialEnergyValue EnergyKernel::pairPotentialEnergy(const Molecul return acc + std::accumulate(nbrCellAtoms.begin(), nbrCellAtoms.end(), 0.0, [&ii, mimRequired, this](const auto innerAcc, const auto *j) { - auto &jj = *j; + auto &jj = *static_cast(j); // Don't consider atoms within the target molecule here - add // it on afterwards diff --git a/src/kernels/force.cpp b/src/kernels/force.cpp index d66f63a0b4..0217f6ee25 100644 --- a/src/kernels/force.cpp +++ b/src/kernels/force.cpp @@ -96,24 +96,24 @@ void ForceKernel::cellToCellPairPotentialForces(const Cell *centralCell, const C // Loop over all atom pairs excluding any within the same molecule if (applyMim) { - for (const auto &i : centralAtoms) + for (const auto &i : centralAtoms | castView()) { molI = i->molecule(); auto indexI = molI->globalAtomIndex(i); - for (auto *j : otherAtoms) + for (auto *j : otherAtoms | castView()) if (molI != j->molecule()) forcesWithMim(*i, indexI, *j, j->molecule()->globalAtomIndex(j), f); } } else { - for (const auto &i : centralAtoms) + for (const auto &i : centralAtoms | castView()) { molI = i->molecule(); auto indexI = molI->globalAtomIndex(i); - for (auto *j : otherAtoms) + for (auto *j : otherAtoms | castView()) if (molI != j->molecule()) forcesWithoutMim(*i, indexI, *j, j->molecule()->globalAtomIndex(j), f); } @@ -160,7 +160,7 @@ void ForceKernel::totalForces(std::vector &ppForceVector, std::vectoratoms(), + dissolve::for_each_pair(ParallelPolicies::seq, cellI->atoms() | castView(), [&](int indexI, const auto &i, int indexJ, const auto &j) { if (indexI == indexJ) diff --git a/src/nodes/calculateBonding.cpp b/src/nodes/calculateBonding.cpp index 05aea2ddb1..4f7031d280 100644 --- a/src/nodes/calculateBonding.cpp +++ b/src/nodes/calculateBonding.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2026 Team Dissolve and contributors #include "nodes/calculateBonding.h" +#include "classes/cellArray.h" #include "data/atomicRadii.h" #include "main/dissolve.h" #include "templates/parallelDefs.h" @@ -52,9 +53,13 @@ void CalculateBondingNode::calculate(Structure &structure, double tolerance, boo structure.clearBonds(); auto box = structure.box(); - auto nAtoms = PairIterator(structure.nAtoms()); - PairIterator pairs(structure.nAtoms()); + CellArray cells; + cells.generate(box, 6.0); + + // Populate cells + for (auto &atom : structure.atoms()) + cells.cell(atom->r())->addAtom(atom.get()); // Look at two indices and check to see if it would be a vaild // bond. If so, return a list with that bond, otherwise an empty @@ -62,47 +67,53 @@ void CalculateBondingNode::calculate(Structure &structure, double tolerance, boo // one element is identical to a std::optional, but the lists can // be trivially combined during the reduce part of // transform_reduce - auto validBond = [&structure, &box, tolerance, preventMetallic, - clearBefore](std::tuple pair) -> std::vector> + auto validBond = [&box, tolerance, preventMetallic](const CellNeighbourPair idx) -> std::set> { - auto [indexI, indexJ] = pair; - if (indexI == indexJ) - return {}; - auto i = structure.atom(indexI); - // Get StructureAtom 'i' and its radius - auto radiusI = AtomicRadii::radius(i->Z()); - // Get StructureAtom 'j' - auto j = structure.atom(indexJ); - - // If the two atoms are both metal ions and prevent metallic bonds = true, continue - if (preventMetallic && Elements::isMetallic(i->Z()) && Elements::isMetallic(j->Z())) - return {}; - - // Calculate distance between atoms - auto r = box.minimumDistance(j->r(), i->r()); - - // Compare distance to sum of atomic radii (multiplied by tolerance factor) - if (r > (radiusI + AtomicRadii::radius(j->Z())) * tolerance) - return {}; - - if (structure.getBond(i, j)) - return {}; - - return {{i, j}}; + auto cellI = idx.cell; + auto cellJ = idx.neighbour; + + std::set> result; + + for (auto i : cellI.atoms() | castView()) + for (auto j : cellJ.atoms() | castView()) + { + // Don't bond atoms to themselves + if (i == j) + continue; + + // Get StructureAtom 'i' and its radius + auto radiusI = AtomicRadii::radius(i->Z()); + // Get StructureAtom 'j' + + // If the two atoms are both metal ions and prevent metallic bonds = true, continue + if (preventMetallic && Elements::isMetallic(i->Z()) && Elements::isMetallic(j->Z())) + continue; + + // Calculate distance between atoms + auto r = box.minimumDistance(j->r(), i->r()); + + // Compare distance to sum of atomic radii (multiplied by tolerance factor) + if (r > (radiusI + AtomicRadii::radius(j->Z())) * tolerance) + continue; + + result.insert({std::min(i->index(), j->index()), std::max(i->index(), j->index())}); + } + + return result; }; // Combine two lists of bonds into a single list auto joinBonds = [](auto a, auto b) { auto ab = a; - ab.insert(ab.end(), b.begin(), b.end()); + ab.merge(b); return ab; }; - // Create an empty vector of the correct shape - std::vector> empty; // In parallel, construct the list of the bonds that need to be added - auto results = std::transform_reduce(ParallelPolicies::par_unseq, pairs.begin(), pairs.end(), empty, joinBonds, validBond); + auto pairs = cells.getCellNeighbourPairsWithSelf(); + auto results = std::transform_reduce(ParallelPolicies::par_unseq, pairs.begin(), pairs.end(), + std::set>(), joinBonds, validBond); // Add the bonds serially for (auto [i, j] : results) diff --git a/src/templates/algorithms.h b/src/templates/algorithms.h index c66e2492c6..9c0c27580e 100644 --- a/src/templates/algorithms.h +++ b/src/templates/algorithms.h @@ -304,3 +304,9 @@ template class U> inline constexpr bool is_instanc template