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()