Hi guys,
so as the title suggests I observed some strange behaviour of the SMC Weld command in conjunction with BaseObject.GetModelingAxis().
My test setup is as follows:
I have a simple plane objects made editable. Then I select some vertices I want to weld together. I then move the Modeling Axis to the random position and execute the script down below.
The first question I have is why can't I weld the points directly together at the position vector BaseObject.GetModelingAxis() provides me with?
To circumvent this issue I came up with the workaround to first weld the point selection to its 'center' while afterwards move that point to the position of BaseObject.GetModelingAxis().
The second question possibly arises from this workaround because while it works it seems that this, way the undo system gets screwed up. To undo the weld operation I have to hit undo twice. Why is that?
I hope someone can help me here and I could explain my problem well enough. If not just let me know.
Cheers,
Sebastian
from typing import Optional, Generator, Union
import c4d
doc: c4d.documents.BaseDocument # The active document
op: Optional[c4d.BaseObject] # The active object, None if unselected
def is_mesh(node: c4d.BaseObject) -> bool:
"""Return whether *node* is of type *c4d.PointObject*."""
return isinstance(node, c4d.PointObject)
def iter_selected_points(obj: c4d.BaseObject) -> Generator:
"""Yield selected points of *obj* along with their index."""
points = obj.GetAllPoints()
baseselect = obj.GetPointS()
sel = baseselect.GetAll(obj.GetPointCount())
for (index, selected), point in zip(enumerate(sel), points):
if not selected:
continue
yield index, point
def smc_weld(obj: c4d.PointObject, doc: c4d.documents.BaseDocument) -> bool:
"""Perform modeling comannd 'Weld' for selected points of *obj*."""
settings = c4d.BaseContainer()
# Explicitly say we will not use point, so the center will be used
settings[c4d.MDATA_WELD_TOPOINT] = False
result = c4d.utils.SendModelingCommand(
command=c4d.ID_MODELING_WELD_TOOL,
list=[obj],
mode=c4d.MODELINGCOMMANDMODE_POINTSELECTION,
bc=settings,
doc=doc
)
return result
def smc_optimize(obj: c4d.PointObject, doc: c4d.documents.BaseDocument, tolerance: float = 0.001) -> bool:
"""Perform modeling comannd 'Optimze' for all points of *obj*."""
settings = c4d.BaseContainer()
settings[c4d.MDATA_OPTIMIZE_TOLERANCE] = tolerance
settings[c4d.MDATA_OPTIMIZE_POINTS] = True
settings[c4d.MDATA_OPTIMIZE_POLYGONS] = True
settings[c4d.MDATA_OPTIMIZE_UNUSEDPOINTS] = True
result = c4d.utils.SendModelingCommand(
command=c4d.MCOMMAND_OPTIMIZE,
list=[obj],
mode=c4d.MODELINGCOMMANDMODE_ALL,
bc=settings,
doc=doc
)
return result
def main() -> None:
# Called when the plugin is selected by the user. Similar to CommandData.Execute.
c4d.StopAllThreads()
doc = c4d.documents.GetActiveDocument()
doc.StartUndo()
obj = doc.GetActiveObject()
if not obj:
return
doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj)
position = obj.GetModelingAxis(doc).off
if not smc_weld(obj, doc):
msg = f"Could not execute Modeling Command 'Weld'."
raise RuntimeError(msg)
index = list(dict(iter_selected_points(obj)).keys())[0]
obj.SetPoint(index, position)
obj.Message(c4d.MSG_UPDATE)
if not smc_optimize(obj, doc):
msg = f"Could not execute Modeling Command 'Optimize'."
raise RuntimeError(msg)
doc.EndUndo()
c4d.EventAdd()
def state():
# Defines the state of the command in a menu. Similar to CommandData.GetState.
doc = c4d.documents.GetActiveDocument()
obj = doc.GetActiveObject()
if not obj:
return False
return c4d.CMD_ENABLED
if __name__ == '__main__':
main()