Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. dskeith
    3. Topics
    • Profile
    • Following 0
    • Followers 0
    • Topics 9
    • Posts 22
    • Best 3
    • Controversial 0
    • Groups 0

    Topics created by dskeith

    • dskeithD

      Node Editor API - View Bounds, Node Positions, Node Sizes

      Cinema 4D SDK
      • s26 python • • dskeith
      4
      0
      Votes
      4
      Posts
      745
      Views

      ferdinandF

      Hello @dskeith,

      without any further questions and other postings, we will consider this topic as solved and flag it as such by Friday, 17/06/2022.

      Thank you for your understanding,
      Ferdinand

    • dskeithD

      Node Editor API - Active Node Editor Graph?

      Cinema 4D SDK
      • python s26 • • dskeith
      7
      0
      Votes
      7
      Posts
      1.3k
      Views

      ferdinandF

      Hello @dskeith,

      without any further questions and other postings, we will consider this topic as solved and flag it as such by Friday, 17/06/2022.

      Thank you for your understanding,
      Ferdinand

    • dskeithD

      Polygon Islands Convenience Method?

      Cinema 4D SDK
      • r23 python • • dskeith
      7
      0
      Votes
      7
      Posts
      2.0k
      Views

      E

      Here is something I recently used to count UV Islands as well as get some other info

      import c4d from collections import defaultdict def get_uv_islands(obj, tol=1e-5): """ Compute UV islands for the given polygon object using its UVW tag. This version retrieves the UV dictionary for each polygon using uvTag.GetSlow(polyIndex), then groups connected polygons (sharing at least one UV coordinate) into islands using an iterative flood-fill algorithm. """ uvw_tag = obj.GetTag(c4d.Tuvw) if uvw_tag is None: c4d.gui.MessageDialog("The object does not have a UVW tag.") return None poly_count = obj.GetPolygonCount() # Build maps: # - face_to_uvs: mapping from polygon index to its set of rounded UV keys. # - uv_to_faces: reverse mapping from each UV key to the set of polygon indices using that UV. face_to_uvs = defaultdict(set) uv_to_faces = defaultdict(set) def uv_key(vec): # Round UV vector components to mitigate floating-point precision issues. return (round(vec.x, 5), round(vec.y, 5)) # Build connectivity maps based on each polygon's UV data. for poly_index in range(poly_count): poly = obj.GetPolygon(poly_index) uv_data = obj.GetTag(c4d.Tuvw).GetSlow(poly_index) # If the polygon is a triangle, remove the extraneous 'd' key. if poly.IsTriangle() and 'd' in uv_data: del uv_data['d'] for key in uv_data: uv_vec = uv_data[key] key_tuple = uv_key(uv_vec) face_to_uvs[poly_index].add(key_tuple) uv_to_faces[key_tuple].add(poly_index) # Use an iterative flood-fill to group connected faces. islands = [] faces_left = set(range(poly_count)) while faces_left: island = [] start_face = next(iter(faces_left)) # Pick an arbitrary face from unvisited ones. stack = [start_face] while stack: face_idx = stack.pop() if face_idx not in faces_left: continue faces_left.remove(face_idx) island.append(face_idx) # For every UV key in this face, add all neighboring faces. for uv_val in face_to_uvs[face_idx]: for neighbor in uv_to_faces[uv_val]: if neighbor in faces_left: stack.append(neighbor) islands.append(island) return islands def get_island_uv_center(obj, uvw_tag, island): """ Given a polygon object, its UVW tag, and an island (list of polygon indices), compute the UV bounding box and center. """ # Initialize min/max with infinities. min_u = float('inf') max_u = -float('inf') min_v = float('inf') max_v = -float('inf') # Gather unique UV coordinates from all faces in the island. unique_uvs = {} for face_idx in island: poly = obj.GetPolygon(face_idx) uv_data = uvw_tag.GetSlow(face_idx) if poly.IsTriangle() and 'd' in uv_data: del uv_data['d'] for key in uv_data: uv = uv_data[key] key_tuple = (round(uv.x, 6), round(uv.y, 6)) unique_uvs[key_tuple] = uv # Compute the bounding box. for uv in unique_uvs.values(): if uv.x < min_u: min_u = uv.x if uv.x > max_u: max_u = uv.x if uv.y < min_v: min_v = uv.y if uv.y > max_v: max_v = uv.y center_u = (min_u + max_u) / 2.0 # Invert V axis so the center is correct in UV space. center_v = 1.0 - ((min_v + max_v) / 2.0) return center_u, center_v, min_u, max_u, min_v, max_v def main(): doc = c4d.documents.GetActiveDocument() obj = doc.GetActiveObject() if obj is None: c4d.gui.MessageDialog("Please select a polygon object.") return uvw_tag = obj.GetTag(c4d.Tuvw) if uvw_tag is None: c4d.gui.MessageDialog("The object does not have a UVW tag!") return islands = get_uv_islands(obj) if islands is None: return num_islands = len(islands) print("Number of UV islands:", num_islands) for idx, island in enumerate(islands): center_u, center_v, min_u, max_u, min_v, max_v = get_island_uv_center(obj, uvw_tag, island) print("Island", idx, "has faces:", island) print("Island Num",idx," Bounding Box: U [{:.6f}, {:.6f}], V [{:.6f}, {:.6f}]".format(min_u, max_u, min_v, max_v)) print(" Center: U = {:.6f}, V = {:.6f}".format(center_u, center_v)) if __name__=='__main__': main()

      Output looks something like

      Number of UV islands: 2 Island 0 has faces: [0, 5, 3, 4] Island Num 0 Bounding Box: U [0.004902, 0.495098], V [0.004902, 0.740196] Center: U = 0.250000, V = 0.627451 Island 1 has faces: [1, 2] Island Num 1 Bounding Box: U [0.004902, 0.495098], V [0.750000, 0.995098] Center: U = 0.250000, V = 0.127451

      Hope it is useful!

    • dskeithD

      Left-Hand Local Coordinates to Right-Hand Local Coordinates?

      General Talk
      • python • • dskeith
      4
      0
      Votes
      4
      Posts
      1.1k
      Views

      dskeithbuckD

      Thank you @zipit and @m_magalhaes for your replies! @zipit your response put me on the right track. The issue was that I was trying to do all of the conversions using C4D's left-handed coordinate matrices, and it was auto-fixing my "malformed" matrices.

      I ended up porting the Three.js Matrix4 and Euler classes from JS to python and using them in my script.

      So, the final code (leaving out the ported methods) looked something like:

      mg = self.op.GetMg() parent_mg = self.op.GetUpMg() c4d_to_three = c4d.Matrix( off=c4d.Vector(0), v1=c4d.Vector(1, 0, 0), v2=c4d.Vector(0, 1, 0), v3=c4d.Vector(0, 0, -1) ) # Convert to new coordinate space # http://www.techart3d.com/2016/02/convert-left-handed-to-right-handed-coordinates/ mg_three_coords = c4d_to_three * mg * c4d_to_three parent_mg_three_coords = c4d_to_three * parent_mg * c4d_to_three mg_mat4 = Matrix4(mg_three_coords) parent_mg_mat4 = Matrix4(parent_mg_three_coords) inv_parent_mg_mat4 = parent_mg_mat4.Clone() inv_parent_mg_mat4 = inv_parent_mg_mat4.Inverse() node_local = inv_parent_mg_mat4.Clone() node_local = node_local.MultiplyMatrices(inv_parent_mg_mat4, mg_mat4) position, scale, rotation = node_local.Decompose() if position != c4d.Vector(0): self.props["position"] = position if scale != c4d.Vector(1): self.props["scale"] = scale if rotation != c4d.Vector(0): self.props["rotation"] = rotation
    • dskeithD

      How to Solo a Layer

      Cinema 4D SDK
      • • • dskeith
      3
      0
      Votes
      3
      Posts
      717
      Views

      r_giganteR

      Hi @dskeith thanks for reaching out us.

      As pointed out by @PluginStudent, the NBIT_SOLO_LAYER behavior is explained in the Layer Manuel although it make sense to add a note to the Python API as well.

      With regard to a complete example, I've partially reworked your code to make sure that by executing the script multiple times you can run across all the solo configurations that the scene can handle.

      import c4d # Main function def main(): layer_root = doc.GetLayerObjectRoot() if not layer_root: return # Get the first layer in the scene layer = layer_root.GetDown() if not layer: return soloSet = False while layer is not None: # Set `rawdata` to True flag so to get the original layer values without any additional global changes layer_data = layer.GetLayerData(doc, rawdata=True) # check the current layer solo state: if layer_data["solo"] == False: # if not "solo" then set to True layer_data["solo"] = True # update the layer layer.SetLayerData(doc, layer_data) # update the flag soloSet = True break # deactivate if layer solo was True layer_data["solo"] = False layer.SetLayerData(doc, layer_data) # go to next layer layer = layer.GetNext() # if one (or more) solo was set the set NBIT_SOLO_LAYER otherwise clear if soloSet == True: doc.ChangeNBit(c4d.NBIT_SOLO_LAYER, c4d.NBITCONTROL_SET) else: doc.ChangeNBit(c4d.NBIT_SOLO_LAYER, c4d.NBITCONTROL_CLEAR) # Add an event c4d.EventAdd() # Execute main() if __name__=='__main__': main()

      Cheers

    • dskeithD

      c4dpy.exe and VS Code on Windows?

      Cinema 4D SDK
      • python windows r21 • • dskeith
      9
      1
      Votes
      9
      Posts
      2.9k
      Views

      M

      @rossc1 Thanks for the inputs, I will for sure add a note in the documentation regarding this issue.

      Just as a follow-up from the current status, it is confirmed and while there is a behavior change in our side due to the new licensing mechanism, the issue also comes from Visual Studio Code, reading directly raw string to detect a valid interpreter, and fail if something is not formated as it wants. However, we received the official confirmation from Visual Studio Code that they will not change their implementation, and not support custom interpreters. But we are still on it.

      Regarding your needs for an IDE, you can use Pycharm, the only pre-requires is to rename c4dpy to python, unfortunately, we can't officially recommend doing such things since newer OS (Catalina) prevent user changing content of an application package but @dskeith explained everything cleary in How to use c4dpy for Cinema4D R20 SP1.

      Cheers,
      Maxime.

    • dskeithD

      Updating Tool Settings?

      Cinema 4D SDK
      • • • dskeith
      2
      0
      Votes
      2
      Posts
      451
      Views

      S

      Hello,

      which tool do you actually want to configure?

      best wishes,
      Sebastian

    • dskeithD

      BaseDocument.StartPickSession() Documentation

      Cinema 4D SDK
      • python • • dskeith
      2
      0
      Votes
      2
      Posts
      585
      Views

      M

      Hi, Donovan thanks for reaching us.

      The mystery variable is actually an int flag to retrieve if the user cancels the PickSession or not. def OnPick(flags, active, multi): if flags & c4d.PICKSESSION_FLAG_CANCELED: print "User cancel" print "active: ", active print "multi: ", multi def main(): doc.StartPickSession(OnPick, multi=True) No there is no way. Actually nothing, it's up to you to detect when the user cancels with PICKSESSION_FLAG_CANCELED and do whatever you want. Cinema 4D handle automatically undo. But you can call BaseDocument.GetSelection before and BaseDocument.SetSelection after if the user cancels the pick session)

      Finally, note the C++ version offer to adds userdata ( For more information please read the C++ documentation https://developers.maxon.net/docs/cpp/2023_2/struct_pick_session_data_struct.html)

      If you have any question, please let me know.
      Cheers,
      Maxime.

    • dskeithD

      Forum Index CSS Styling

      General Talk
      • • • dskeith
      5
      0
      Votes
      5
      Posts
      2.3k
      Views

      a_blockA

      Hi Donovan,

      thanks for your feedback. Currently we are bound mostly by DevKitchen preparations. But we promise to get back to this issue and think about other options as soon as we find the time.

      Until then: If you click on the "unread message queue", there in the top right you have commands to mark messages as read, either all or just for certain categories. Maybe this can at least ease your pain, until we find the time to address the issue.

      Bye,
      Andreas