Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Viewer3D] Input bounding box (Meshing) & manual transformation (SfMTransform) thanks to a new 3D Gizmo #978

Merged
merged 51 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
98936b9
[ui] Viewer3D: add a Transform Gizmo Component
julien-hdev Jul 3, 2020
30004fe
[ui] Viewer3D: TransformGizmo - add needed mathematics functions
julien-hdev Jul 6, 2020
45fae98
[ui] Viewer3D: TransformGizmo - add picking and transformations
julien-hdev Jul 6, 2020
6a9f229
[ui] Viewer3D: TransformGizmo - individual color picking
julien-hdev Jul 6, 2020
29c4be6
[ui] Viewer3D: TransformGizmo - use one global FrameAction
julien-hdev Jul 7, 2020
fb3b541
[ui] Viewer3D: TransformGizmo - new mouse handling translation
julien-hdev Jul 8, 2020
cd63041
[ui] Viewer3D: TransformGizmo - new mouse handling scale
julien-hdev Jul 8, 2020
ad2f287
[ui] Viewer3D: TransformGizmo - clean mouse handling translation
julien-hdev Jul 8, 2020
414a865
[ui] Viewer3D: TransformGizmo - clean parameter names
julien-hdev Jul 8, 2020
d04eaac
[ui] Viewer3D: TransformGizmo - new mouse handling rotation
julien-hdev Jul 8, 2020
bdf9d74
[ui] Viewer3D: TransformGizmo - cleaning and renaming
julien-hdev Jul 8, 2020
85ebbba
[ui] Viewer3D: TransformGizmo - drawing gizmo on top of the object
julien-hdev Jul 9, 2020
f9d57ca
[ui] Viewer3D: TransformGizmo - add reset transform option
julien-hdev Jul 9, 2020
b234a76
[ui] Viewer3D: TransformGizmo - removing FrameAction
julien-hdev Jul 9, 2020
cb306ff
[ui] Viewer3D: DefaultCameraController - removing FrameAction
julien-hdev Jul 10, 2020
da765a5
[ui] Viewer3D: TransformGizmo - moving transform operations to Python
julien-hdev Jul 13, 2020
d0a78d9
[ui] Viewer3D: TransformGizmo - ready to be set with absolute values
julien-hdev Jul 15, 2020
7663a6a
[ui] Viewer3D: TransformGizmo - preparing for the bounding box
julien-hdev Jul 20, 2020
f4e7311
[ui] Viewer3D: add a BoundingBox entity
julien-hdev Jul 20, 2020
94b2ff3
[nodes] meshing: add bounding box options
julien-hdev Jul 20, 2020
2081146
[ui] Viewer3D: bounding box linked to meshing node
julien-hdev Jul 20, 2020
6103a49
[ui] Viewer3D: TransformGizmo - fix reset transformations
julien-hdev Jul 20, 2020
4ab412d
[ui] Viewer3D: move bounding box display to Inspector3D
julien-hdev Jul 20, 2020
f2a7586
[ui] Viewer3D: better bounding box appearance
julien-hdev Jul 21, 2020
c45d299
[ui] Viewer3D: specific bounding box options in the Inspector3D
julien-hdev Jul 21, 2020
f746f0b
[ui] Viewer3D: TransformGizmo - better mouse control
julien-hdev Jul 22, 2020
e96aae5
[ui] Viewer3D: TransformGizmo - better right click menu
julien-hdev Jul 22, 2020
266ac61
[nodes] SfMTransform : add gizmo transformation
julien-hdev Jul 23, 2020
180b492
[ui] Viewer3D: add a SfMTransformGizmo
julien-hdev Jul 23, 2020
9fd9074
[ui] Viewer3D: add SfMTransformGizmo to MediaLibrary and Inspector3D
julien-hdev Jul 23, 2020
97fd076
[ui] Viewer3D: SfMTransformGizmo - real-time transformed input rendering
julien-hdev Jul 24, 2020
9a7e4fe
[nodes] Meshing: add enabled parameter to bounding box
julien-hdev Jul 30, 2020
1c14159
[nodes] SfMTransform: change name from gizmo to manual + enabled param
julien-hdev Jul 30, 2020
e9151b2
[ui] Viewer3D: fix bbox disappearing when moving after computed
julien-hdev Aug 3, 2020
cd89a49
[core] GroupAttribute: add new ways of setting value
julien-hdev Aug 6, 2020
0c9dc81
[core] fix validateValue and setValue for Attributes
julien-hdev Aug 7, 2020
c979ed3
[ui] Components: fix typo in Scene3D
julien-hdev Aug 7, 2020
7486f3b
[ui] Viewer3D: update TransformGizmo signal with transformation type
julien-hdev Aug 7, 2020
ceb927b
[ui] Viewer3D: avoid unwanted changes on other transformation types
julien-hdev Aug 7, 2020
f614e63
[doc] add a lot of developer's information
julien-hdev Aug 7, 2020
06372bc
[core] fix elif after raise
julien-hdev Aug 7, 2020
887b334
[core] avoid direct includes to PySide2
julien-hdev Aug 10, 2020
79e96e7
[ui] fix typos
julien-hdev Aug 17, 2020
b79795a
[ui] fix binding errors
julien-hdev Aug 17, 2020
fe91d07
[core] Node: add alive property for QML
julien-hdev Aug 17, 2020
8020cf5
[ui] Reconstruction: add clear methods
julien-hdev Aug 17, 2020
b9e68b7
[ui] Graph: change setGraph and clear
julien-hdev Aug 17, 2020
424f7e5
[ui] MediaLibrary: add alive property and fix issue
julien-hdev Aug 17, 2020
bd5f515
[ui] MediaLibrary: fix SfMTransform loading issue
julien-hdev Aug 17, 2020
12de900
[ui] fix binding warnings when closing Meshroom
julien-hdev Aug 17, 2020
688027a
[ui] MediaLibrary: fix dependency binding
julien-hdev Aug 21, 2020
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
5 changes: 3 additions & 2 deletions meshroom/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
BaseObject = None
Variant = None
VariantList = None
JSValue = None

if meshroom.backend == meshroom.Backend.PYSIDE:
# PySide types
from .qt import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList
from .qt import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue
elif meshroom.backend == meshroom.Backend.STANDALONE:
# Core types
from .core import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList
from .core import DictModel, ListModel, Slot, Signal, Property, BaseObject, Variant, VariantList, JSValue


class _BaseModel:
Expand Down
1 change: 1 addition & 0 deletions meshroom/common/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,4 @@ def parent(self):
BaseObject = CoreObject
Variant = object
VariantList = object
JSValue = None
3 changes: 2 additions & 1 deletion meshroom/common/qt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PySide2 import QtCore
from PySide2 import QtCore, QtQml


class QObjectListModel(QtCore.QAbstractListModel):
Expand Down Expand Up @@ -374,3 +374,4 @@ def sort(self):
BaseObject = QtCore.QObject
Variant = "QVariant"
VariantList = "QVariantList"
JSValue = QtQml.QJSValue
19 changes: 13 additions & 6 deletions meshroom/core/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ def updateInternals(self):
isLink = Property(bool, isLink.fget, notify=isLinkChanged)
isDefault = Property(bool, _isDefault, notify=valueChanged)
linkParam = Property(BaseObject, getLinkParam, notify=isLinkChanged)
rootLinkParam = Property(BaseObject, lambda self: self.getLinkParam(recursive=True), notify=isLinkChanged)
node = Property(BaseObject, node.fget, constant=True)
enabledChanged = Signal()
enabled = Property(bool, getEnabled, setEnabled, notify=enabledChanged)
Expand Down Expand Up @@ -300,8 +301,8 @@ def _set_value(self, value):
self._value = value
# New value
else:
self.desc.validateValue(value)
self.extend(value)
newValue = self.desc.validateValue(value)
self.extend(newValue)
self.requestGraphUpdate()

@raiseIfLink
Expand Down Expand Up @@ -410,10 +411,16 @@ def __getattr__(self, key):
raise AttributeError(key)

def _set_value(self, exportedValue):
self.desc.validateValue(exportedValue)
# set individual child attribute values
for key, value in exportedValue.items():
self._value.get(key).value = value
value = self.desc.validateValue(exportedValue)
if isinstance(value, dict):
# set individual child attribute values
for key, v in value.items():
self._value.get(key).value = v
elif isinstance(value, (list, tuple)):
for attrDesc, v in zip(self.desc._groupDesc, value):
self._value.get(attrDesc.name).value = v
else:
raise AttributeError("Failed to set on GroupAttribute: {}".format(str(value)))

@Slot(str, result=Attribute)
def childAttribute(self, key):
Expand Down
43 changes: 32 additions & 11 deletions meshroom/core/desc.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from meshroom.common import BaseObject, Property, Variant, VariantList
from meshroom.common import BaseObject, Property, Variant, VariantList, JSValue
from meshroom.core import pyCompatibility
from enum import Enum # available by default in python3. For python2: "pip install enum34"
import math
import os
import psutil

import ast

class Attribute(BaseObject):
"""
Expand Down Expand Up @@ -32,12 +32,12 @@ def __init__(self, name, label, description, value, advanced, uid, group, enable
type = Property(str, lambda self: self.__class__.__name__, constant=True)

def validateValue(self, value):
""" Return validated/conformed 'value'.
""" Return validated/conformed 'value'. Need to be implemented in derived classes.

Raises:
ValueError: if value does not have the proper type
"""
return value
raise NotImplementedError("Attribute.validateValue is an abstract function that should be implemented in the derived class.")

def matchDescription(self, value, conform=False):
""" Returns whether the value perfectly match attribute's description.
Expand Down Expand Up @@ -68,6 +68,14 @@ def __init__(self, elementDesc, name, label, description, group='allParams', adv
joinChar = Property(str, lambda self: self._joinChar, constant=True)

def validateValue(self, value):
if JSValue is not None and isinstance(value, JSValue):
# Note: we could use isArray(), property("length").toInt() to retrieve all values
raise ValueError("ListAttribute.validateValue: cannot recognize QJSValue. Please, use JSON.stringify(value) in QML.")
if isinstance(value, pyCompatibility.basestring):
# Alternative solution to set values from QML is to convert values to JSON string
# In this case, it works with all data types
value = ast.literal_eval(value)

if not isinstance(value, (list, tuple)):
raise ValueError('ListAttribute only supports list/tuple input values (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
return value
Expand Down Expand Up @@ -95,12 +103,25 @@ def __init__(self, groupDesc, name, label, description, group='allParams', advan
groupDesc = Property(Variant, lambda self: self._groupDesc, constant=True)

def validateValue(self, value):
""" Ensure value is a dictionary with keys compatible with the group description. """
if not isinstance(value, dict):
raise ValueError('GroupAttribute only supports dict input values (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
invalidKeys = set(value.keys()).difference([attr.name for attr in self._groupDesc])
if invalidKeys:
raise ValueError('Value contains key that does not match group description : {}'.format(invalidKeys))
""" Ensure value is compatible with the group description and convert value if needed. """
if JSValue is not None and isinstance(value, JSValue):
# Note: we could use isArray(), property("length").toInt() to retrieve all values
raise ValueError("GroupAttribute.validateValue: cannot recognize QJSValue. Please, use JSON.stringify(value) in QML.")
if isinstance(value, pyCompatibility.basestring):
# Alternative solution to set values from QML is to convert values to JSON string
# In this case, it works with all data types
value = ast.literal_eval(value)

if isinstance(value, dict):
invalidKeys = set(value.keys()).difference([attr.name for attr in self._groupDesc])
if invalidKeys:
raise ValueError('Value contains key that does not match group description : {}'.format(invalidKeys))
elif isinstance(value, (list, tuple)):
if len(value) != len(self._groupDesc):
raise ValueError('Value contains incoherent number of values: desc size: {}, value size: {}'.format(len(self._groupDesc), len(value)))
else:
raise ValueError('GroupAttribute only supports dict/list/tuple input values (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))

return value

def matchDescription(self, value, conform=False):
Expand Down Expand Up @@ -169,7 +190,7 @@ def __init__(self, name, label, description, value, uid, group='allParams', adva

def validateValue(self, value):
try:
return bool(int(value)) # int cast is useful to handle string values ('0', '1')
return bool(int(value)) # int cast is useful to handle string values ('0', '1')
except:
raise ValueError('BoolParam only supports bool value (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))

Expand Down
6 changes: 5 additions & 1 deletion meshroom/core/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,11 @@ def __init__(self, name, parent=None):
def clear(self):
self.header.clear()
self._compatibilityNodes.clear()
self._nodes.clear()
self._edges.clear()
# Tell QML nodes are going to be deleted
for node in self._nodes:
node.alive = False
self._nodes.clear()

@property
def fileFeatures(self):
Expand Down Expand Up @@ -437,6 +440,7 @@ def removeNode(self, nodeName):
self.removeEdge(edge.dst)
inEdges[edge.dst.getFullName()] = edge.src.getFullName()

node.alive = False
self._nodes.remove(node)
self.update()

Expand Down
14 changes: 14 additions & 0 deletions meshroom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ def __init__(self, nodeType, position=None, parent=None, **kwargs):
self._position = position or Position()
self._attributes = DictModel(keyAttrName='name', parent=self)
self.attributesPerUid = defaultdict(set)
self._alive = True # for QML side to know if the node can be used or is going to be deleted

def __getattr__(self, k):
try:
Expand Down Expand Up @@ -526,6 +527,17 @@ def position(self, value):
self._position = value
self.positionChanged.emit()

@property
def alive(self):
return self._alive

@alive.setter
def alive(self, value):
if self._alive == value:
return
self._alive = value
self.aliveChanged.emit()

@property
def depth(self):
return self.graph.getDepth(self)
Expand Down Expand Up @@ -792,6 +804,8 @@ def __repr__(self):
globalStatusChanged = Signal()
globalStatus = Property(str, lambda self: self.getGlobalStatus().name, notify=globalStatusChanged)
isComputed = Property(bool, _isComputed, notify=globalStatusChanged)
aliveChanged = Signal()
alive = Property(bool, alive.fget, alive.fset, notify=aliveChanged)


class Node(BaseNode):
Expand Down
97 changes: 96 additions & 1 deletion meshroom/nodes/aliceVision/Meshing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "5.0"
__version__ = "6.0"

from meshroom.core import desc

Expand Down Expand Up @@ -35,6 +35,101 @@ class Meshing(desc.CommandLineNode):
value='',
uid=[0],
),
desc.BoolParam(
name='useBoundingBox',
label='Custom Bounding Box',
description='Edit the meshing bounding box. If enabled, it takes priority over the Estimate From SfM option. Parameters can be adjusted in advanced settings.',
value=False,
uid=[0],
group=''
),
desc.GroupAttribute(
name="boundingBox",
label="Bounding Box Settings",
description="Translation, rotation and scale of the bounding box.",
groupDesc=[
desc.GroupAttribute(
name="bboxTranslation",
label="Translation",
description="Position in space.",
groupDesc=[
desc.FloatParam(
name="x", label="x", description="X Offset",
value=0.0,
uid=[0],
range=(-20.0, 20.0, 0.01)
),
desc.FloatParam(
name="y", label="y", description="Y Offset",
value=0.0,
uid=[0],
range=(-20.0, 20.0, 0.01)
),
desc.FloatParam(
name="z", label="z", description="Z Offset",
value=0.0,
uid=[0],
range=(-20.0, 20.0, 0.01)
)
],
joinChar=","
),
desc.GroupAttribute(
name="bboxRotation",
label="Euler Rotation",
description="Rotation in Euler degrees.",
groupDesc=[
desc.FloatParam(
name="x", label="x", description="Euler X Rotation",
value=0.0,
uid=[0],
range=(-90.0, 90.0, 1)
),
desc.FloatParam(
name="y", label="y", description="Euler Y Rotation",
value=0.0,
uid=[0],
range=(-180.0, 180.0, 1)
),
desc.FloatParam(
name="z", label="z", description="Euler Z Rotation",
value=0.0,
uid=[0],
range=(-180.0, 180.0, 1)
)
],
joinChar=","
),
desc.GroupAttribute(
name="bboxScale",
label="Scale",
description="Scale of the bounding box.",
groupDesc=[
desc.FloatParam(
name="x", label="x", description="X Scale",
value=1.0,
uid=[0],
range=(0.0, 20.0, 0.01)
),
desc.FloatParam(
name="y", label="y", description="Y Scale",
value=1.0,
uid=[0],
range=(0.0, 20.0, 0.01)
),
desc.FloatParam(
name="z", label="z", description="Z Scale",
value=1.0,
uid=[0],
range=(0.0, 20.0, 0.01)
)
],
joinChar=","
)
],
joinChar=",",
enabled=lambda node: node.useBoundingBox.value,
),
desc.BoolParam(
name='estimateSpaceFromSfM',
label='Estimate Space From SfM',
Expand Down
Loading