Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
699fc71
[desc] Node Internal Attributes: Added Attribute factory
waaake Dec 18, 2024
b988a7c
[core] Backdrop Node: Added Notion of Backdrop as a Node
waaake Dec 18, 2024
41c24b1
[desc] Node Attribute Traits: Added Resizable Trait and attribute def…
waaake Dec 18, 2024
0b79a29
[desc] Added Notion of Builtin Backdrop
waaake Dec 18, 2024
23574c4
[ui] Geom2d: Added a way to detect full intersection between two rects
waaake Dec 18, 2024
dc5d7dd
[ui][qml] Added Backdrop Node component in QML
waaake Dec 18, 2024
fce662b
[ui] Graph: Added Functionality to modify node position and dimensions
waaake Dec 18, 2024
ce25d00
[ui] GraphEditor: Added Loader for Node Delegate
waaake Dec 18, 2024
8cc6c84
[core] Builtins: Added registration mechanism for Builtin plugins
waaake Dec 18, 2024
dd62a36
[core] Node: Added Constructor Preference to allow defining the Node …
waaake Dec 18, 2024
da7212b
[core] Graph: Updated Node Construction to rely on using preferred co…
waaake Dec 18, 2024
71843e6
[ui][qml] GraphEditor: Added helper for nodeRepeater to get the item …
waaake Jan 2, 2025
e653213
[ui] GraphEditor: Updated bounding box to look at the actual node ite…
waaake Jan 2, 2025
0adf04f
[ui] DelegateSelectionBox: Updated selection to refer to the actual i…
waaake Jan 2, 2025
302753e
[ui] GraphEditor: Grouped the visual resize of a Node and reposition
waaake Jan 7, 2025
3a2384f
[ui] Backdrop: Updated the signal emission from QML to be invoked onl…
waaake Jan 7, 2025
d72d29e
[ui] GraphEditor: Backdrop Resizing updates
waaake Jan 7, 2025
8050a6e
[ui] GraphEditor: Added flag for holding the animation state for the …
waaake Jan 8, 2025
2f29d00
[ui] Commands: Added Grouped UI Modification Command
waaake Jan 8, 2025
5fb7636
[ui] Graph: Using Grouped UI Graph Modification to disable animation …
waaake Jan 8, 2025
483a838
[desc] Node: Updated return hint -> List[Attribute] on getInternalParams
waaake Feb 10, 2025
05c9511
[core] Node: Add method to create a node instance in memory
waaake Feb 10, 2025
e5ec7f0
[ui] Backdrop: Backdrop can be resized from the top as well
waaake Feb 10, 2025
419fdc7
[ui] GraphEditor: Backdrop Node Animations considers reszing on Y and…
waaake Feb 10, 2025
faa7287
[ui] Node: Added property denoting if the node delegate is a backdrop
waaake Feb 11, 2025
236ba1d
[ui] Backdrop: Added getters for children indices from a backdrop
waaake Feb 11, 2025
5a00360
[ui] GraphEditor: Selection of children when a backdrop is selected
waaake Feb 11, 2025
499c994
[ui] DelegateSelectionBox: Fetch children indices when box selecting …
waaake Feb 11, 2025
cca0bb1
[ui] Graph: Remove unused moveNodesBy function
waaake Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions meshroom/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from meshroom.core.submitter import BaseSubmitter
from . import desc
from .desc import builtins

# Setup logging
logging.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=logging.INFO)
Expand All @@ -36,7 +37,7 @@

def hashValue(value):
""" Hash 'value' using sha1. """
hashObject = hashlib.sha1(str(value).encode('utf-8'))

Check failure on line 40 in meshroom/core/__init__.py

View check run for this annotation

codefactor.io / CodeFactor

meshroom/core/__init__.py#L40

Use of weak SHA1 hash for security. Consider usedforsecurity=False (B324)
return hashObject.hexdigest()


Expand Down Expand Up @@ -337,6 +338,9 @@
for f in nodesFolders:
loadAllNodes(folder=f)

# Load all of the builtin Node Plugins
initBuiltinNodePlugins()


def initSubmitters():
meshroomFolder = os.path.dirname(os.path.dirname(__file__))
Expand All @@ -357,3 +361,8 @@
loadPipelineTemplates(f)
else:
logging.error("Pipeline templates folder '{}' does not exist.".format(f))

def initBuiltinNodePlugins():
""" Registers the Builtin plugins for Meshroom.
"""
registerNodeType(builtins.Backdrop)
Comment on lines +365 to +368
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove that if we move it to the standard folder.

12 changes: 12 additions & 0 deletions meshroom/core/desc/builtins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
""" Defines the Built in Plugins.
Copy link
Member

@fabiencastan fabiencastan Jan 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect the embedded nodes to be in a folder loaded automatically by Meshroom, in the same way than we are doing for external nodes:
meshroom/nodes/utils/Backdrop.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussing with @yann-lty initially to have a way to register builtin plugins.
So have added this accordingly.
We can discuss and simplify this if needed 🙂

"""
from .node import InputNode, AttributeFactory, Traits


class Backdrop(InputNode):
""" A Backdrop for other nodes.
"""

internalInputs = AttributeFactory.getInternalParameters(Traits.RESIZABLE)

category = "Utils"
113 changes: 92 additions & 21 deletions meshroom/core/desc/node.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
import enum
import os
import psutil
import shlex
from typing import List

from .computation import Level, StaticNodeSize
from .attribute import StringParam, ColorParam
from .attribute import Attribute, StringParam, ColorParam, IntParam, FloatParam

from meshroom.core import cgroup


class Node(object):
"""
class Traits(enum.IntEnum):
""" Describes the characteristics of the Attribute Groups.
"""
internalFolder = '{cache}/{nodeType}/{uid}/'
cpu = Level.NORMAL
gpu = Level.NONE
ram = Level.NORMAL
packageName = ''
packageVersion = ''
internalInputs = [
StringParam(
name="invalidation",
label="Invalidation Message",
description="A message that will invalidate the node's output folder.\n"
"This is useful for development, we can invalidate the output of the node when we modify the code.\n"
"It is displayed in bold font in the invalidation/comment messages tooltip.",
value="",
semantic="multiline",
advanced=True,
uidIgnoreValue="", # If the invalidation string is empty, it does not participate to the node's UID
),

# Computable: Characterisics that holds invalidation attribute
COMPUTABLE = 1

# Incomputable: Characterisics that does not need processing
INCOMPUTABLE = 2

# Resizable: Characterisics that allows node's dimensions to be adjusted
RESIZABLE = 3


class AttributeFactory:

# Attribute Defines
BASIC = [
StringParam(
name="comment",
label="Comments",
Expand All @@ -53,6 +52,72 @@
invalidate=False,
)
]

INVALIDATION = [
StringParam(
name="invalidation",
label="Invalidation Message",
description="A message that will invalidate the node's output folder.\n"
"This is useful for development, we can invalidate the output of the node when we modify the code.\n"
"It is displayed in bold font in the invalidation/comment messages tooltip.",
value="",
semantic="multiline",
advanced=True,
uidIgnoreValue="", # If the invalidation string is empty, it does not participate to the node's UID
)
]

RESIZABLE = [
IntParam(
name="fontSize",
label="Font Size",
description="The Font size for the User Comment.",
value=12,
range=(6, 100, 1),
),
FloatParam(
name="nodeWidth",
label="Node Width",
description="The Node's Width.",
value=600,
range=None,
enabled=False # Hidden always
),
FloatParam(
name="nodeHeight",
label="Node Height",
description="The Node's Height.",
value=400,
range=None,
enabled=False # Hidden always
),
]

@classmethod
def getInternalParameters(cls, traits: Traits) -> List[Attribute]:
""" Returns an array of Attributes characterized by a given trait.
"""
paramMap = {
Traits.COMPUTABLE: cls.INVALIDATION + cls.BASIC,
Traits.INCOMPUTABLE: cls.BASIC,
Traits.RESIZABLE: cls.BASIC + cls.RESIZABLE
}

return paramMap.get(traits)


class Node(object):
"""
"""
internalFolder = '{cache}/{nodeType}/{uid}/'
cpu = Level.NORMAL
gpu = Level.NONE
ram = Level.NORMAL
packageName = ''
packageVersion = ''

internalInputs = AttributeFactory.getInternalParameters(Traits.COMPUTABLE)

inputs = []
outputs = []
size = StaticNodeSize(1)
Expand Down Expand Up @@ -116,12 +181,18 @@
"""
Node that does not need to be processed, it is just a placeholder for inputs.
"""

internalInputs = AttributeFactory.getInternalParameters(Traits.INCOMPUTABLE)

def __init__(self):
super(InputNode, self).__init__()

def processChunk(self, chunk):
pass

def stopProcess(self, chunk):
pass

Check warning on line 194 in meshroom/core/desc/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/desc/node.py#L194

Added line #L194 was not covered by tests


class CommandLineNode(Node):
"""
Expand Down
4 changes: 2 additions & 2 deletions meshroom/core/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from meshroom.core.exception import GraphCompatibilityError, StopGraphVisit, StopBranchVisit
from meshroom.core.graphIO import GraphIO, GraphSerializer, TemplateGraphSerializer, PartialGraphSerializer
from meshroom.core.node import BaseNode, Status, Node, CompatibilityNode
from meshroom.core.nodeFactory import nodeFactory
from meshroom.core.nodeFactory import createNode, nodeFactory
from meshroom.core.typing import PathLike

# Replace default encoder to support Enums
Expand Down Expand Up @@ -637,7 +637,7 @@ def addNewNode(self, nodeType, name=None, position=None, **kwargs):
if name and name in self._nodes.keys():
name = self._createUniqueNodeName(name)

n = self.addNode(Node(nodeType, position=position, **kwargs), uniqueName=name)
n = self.addNode(createNode(nodeType, position=position, **kwargs), uniqueName=name)
n.updateInternals()
return n

Expand Down
93 changes: 92 additions & 1 deletion meshroom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import uuid
from collections import namedtuple
from enum import Enum
from typing import Callable, Optional
from typing import Callable, Optional, Type

import meshroom
from meshroom.common import Signal, Variant, Property, BaseObject, Slot, ListModel, DictModel
Expand Down Expand Up @@ -574,6 +574,42 @@
return self.internalAttribute("comment").value
return ""

def getFontSize(self):
""" Gets the Font Size of the node comment.

Returns:
int: The font size from the node if it exists, else 12 as default.
"""
if self.hasInternalAttribute("fontSize"):
return self.internalAttribute("fontSize").value

Check warning on line 584 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L583-L584

Added lines #L583 - L584 were not covered by tests

# Default to 12
return 12

Check warning on line 587 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L587

Added line #L587 was not covered by tests

def getNodeWidth(self):
""" Gets the Width of the node from the internal attribute.

Returns:
int: The Width from the node if the attribute exists, else 160 as default.
"""
if self.hasInternalAttribute("nodeWidth"):
return self.internalAttribute("nodeWidth").value

Check warning on line 596 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L595-L596

Added lines #L595 - L596 were not covered by tests

# Default to 160
return 160

Check warning on line 599 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L599

Added line #L599 was not covered by tests

def getNodeHeight(self):
""" Gets the Height of the node from the internal attribute.

Returns:
int: The Height from the node if the attribute exists, else 120 as default.
"""
if self.hasInternalAttribute("nodeHeight"):
return self.internalAttribute("nodeHeight").value

Check warning on line 608 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L607-L608

Added lines #L607 - L608 were not covered by tests

# Default to 120
return 120

Check warning on line 611 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L611

Added line #L611 was not covered by tests

@Slot(str, result=str)
def nameToLabel(self, name):
"""
Expand Down Expand Up @@ -1200,6 +1236,11 @@
def _isInputNode(self):
return isinstance(self.nodeDesc, desc.InputNode)

def _isBackdropNode(self) -> bool:
""" Returns True if the node is a Backdrop type.
"""
return False

Check warning on line 1242 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1242

Added line #L1242 was not covered by tests

@property
def globalExecMode(self):
return self._chunks.at(0).execModeName
Expand Down Expand Up @@ -1389,6 +1430,11 @@
color = Property(str, getColor, notify=internalAttributesChanged)
invalidation = Property(str, getInvalidationMessage, notify=internalAttributesChanged)
comment = Property(str, getComment, notify=internalAttributesChanged)
fontSize = Property(int, getFontSize, notify=internalAttributesChanged)

# Node Dimensions
nodeWidth = Property(float, getNodeWidth, notify=internalAttributesChanged)
nodeHeight = Property(float, getNodeHeight, notify=internalAttributesChanged)
internalFolderChanged = Signal()
internalFolder = Property(str, internalFolder.fget, notify=internalFolderChanged)
valuesFile = Property(str, valuesFile.fget, notify=internalFolderChanged)
Expand All @@ -1408,6 +1454,7 @@
# isCompatibilityNode: need lambda to evaluate the virtual function
isCompatibilityNode = Property(bool, lambda self: self._isCompatibilityNode(), constant=True)
isInputNode = Property(bool, lambda self: self._isInputNode(), constant=True)
isBackdrop = Property(bool, lambda self: self._isBackdropNode(), constant=True)

globalExecModeChanged = Signal()
globalExecMode = Property(str, globalExecMode.fget, notify=globalExecModeChanged)
Expand All @@ -1428,6 +1475,50 @@
has3DOutput = Property(bool, has3DOutputAttribute, notify=outputAttrEnabledChanged)


class Backdrop(BaseNode):
""" A Node serving as a Backdrop in the Graph.
"""

def __init__(self, nodeType: str, position=None, parent=None, uid=None, **kwargs):
super().__init__(nodeType, position, parent=parent, uid=uid, **kwargs)

Check warning on line 1483 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1483

Added line #L1483 was not covered by tests

if not self.nodeDesc:
raise UnknownNodeTypeError(nodeType)

Check warning on line 1486 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1485-L1486

Added lines #L1485 - L1486 were not covered by tests

self.packageName = self.nodeDesc.packageName
self.packageVersion = self.nodeDesc.packageVersion
self._internalFolder = self.nodeDesc.internalFolder

Check warning on line 1490 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1488-L1490

Added lines #L1488 - L1490 were not covered by tests

for attrDesc in self.nodeDesc.internalInputs:
self._internalAttributes.add(attributeFactory(attrDesc, kwargs.get(attrDesc.name, None), isOutput=False,

Check warning on line 1493 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1492-L1493

Added lines #L1492 - L1493 were not covered by tests
node=self))

def _isBackdropNode(self) -> bool:
""" Returns True.
"""
return True

Check warning on line 1499 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1499

Added line #L1499 was not covered by tests

def toDict(self) -> dict:
""" Returns the serialised data for the Backdrop.
"""
internalInputs = {k: v.getExportValue() for k, v in self._internalAttributes.objects.items()}

Check warning on line 1504 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1504

Added line #L1504 was not covered by tests

return {

Check warning on line 1506 in meshroom/core/node.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/node.py#L1506

Added line #L1506 was not covered by tests
'nodeType': self.nodeType,
'position': self._position,
'parallelization': {
'blockSize': 0,
'size': 0,
'split': 0
},
'uid': self._uid,
'internalFolder': self._internalFolder,
'inputs': {},
'internalInputs': {k: v for k, v in internalInputs.items() if v is not None},
'outputs': {},
}


class Node(BaseNode):
"""
A standard Graph node based on a node type.
Expand Down
26 changes: 24 additions & 2 deletions meshroom/core/nodeFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import meshroom.core
from meshroom.core import Version, desc
from meshroom.core.node import CompatibilityIssue, CompatibilityNode, Node, Position
from meshroom.core.node import Backdrop, BaseNode, CompatibilityIssue, CompatibilityNode, Node, Position


def nodeFactory(
Expand All @@ -29,6 +29,28 @@ def nodeFactory(
return _NodeCreator(nodeData, name, inTemplate, expectedUid).create()


def createNode(nodeType: str, position: Optional[Position]=None, **kwargs) -> BaseNode:
""" Create a new Node instance based on given node description.
Any other keyword argument will be used to initialize the node's attributes.

Args:
nodeType: Node Plugin/Descriptor name.
position: Node Position.
parent (BaseObject): this Node's parent.
**kwargs: attributes values.

Returns:
The created Node.
"""
constructors = {
"Backdrop": Backdrop,
}
# Node constructor based on NodeType
constructor = constructors.get(nodeType, Node)

return constructor(nodeType, position=position, **kwargs)


class _NodeCreator:

def __init__(
Expand Down Expand Up @@ -168,7 +190,7 @@ def _checkAttributesCompatibility(

def _createNode(self) -> Node:
logging.info(f"Creating node '{self.name}'")
return Node(
return createNode(
self.nodeType,
position=self.position,
uid=self.uid,
Expand Down
Loading