Skip to content

Commit 07b406a

Browse files
Merge pull request #61 from neurobionics/30-problem-with-composite-parts
Add support for composite parts
2 parents 12166e9 + 1b317c1 commit 07b406a

File tree

5 files changed

+38
-173
lines changed

5 files changed

+38
-173
lines changed

examples/export/formats_example.py

Lines changed: 0 additions & 160 deletions
This file was deleted.

examples/export/main.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from onshape_robotics_toolkit.config import ORTConfig
44
from onshape_robotics_toolkit.connect import Client
5-
from onshape_robotics_toolkit.formats import MJCFSerializer
5+
from onshape_robotics_toolkit.formats.urdf import URDFSerializer
66
from onshape_robotics_toolkit.graph import KinematicGraph
77
from onshape_robotics_toolkit.parse import CAD
88
from onshape_robotics_toolkit.robot import Robot
@@ -11,11 +11,10 @@
1111
# Basic parameters a user would normally hard-code or source from their own scripts.
1212
ENV_PATH = ".env"
1313
DOCUMENT_URL = (
14-
"https://cad.onshape.com/documents/a1c1addf75444f54b504f25c/"
15-
"w/0d17b8ebb2a4c76be9fff3c7/e/d8f8f1d9dbf9634a39aa7f5b"
14+
"https://cad.onshape.com/documents/1291b9c12b545eeed2d7b739/w/97037c8205b8249da5568aaf/e/c7cf82667c856bf201b3cbde"
1615
)
1716
MAX_DEPTH = 2
18-
EXPORT_PATH = Path("output/robot.xml")
17+
EXPORT_PATH = Path("output/robot.urdf")
1918
MESH_DIR = "custom_meshes"
2019
CONFIG_OUTPUT = Path("ORT.yaml")
2120

@@ -39,8 +38,8 @@ def main() -> None:
3938
graph = KinematicGraph.from_cad(cad, use_user_defined_root=use_root)
4039
robot = Robot.from_graph(kinematic_graph=graph, client=client, name=robot_name)
4140

42-
# Export using MJCF serializer - no need to import MJCFConfig!
43-
serializer = MJCFSerializer()
41+
# Export using URDF serializer - no need to import URDFConfig!
42+
serializer = URDFSerializer()
4443
serializer.save(
4544
robot,
4645
export_path,

onshape_robotics_toolkit/connect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ def get_assembly(
629629
query={
630630
"includeMateFeatures": "true",
631631
"includeMateConnectors": "true",
632-
"includeNonSolids": "false",
632+
"includeNonSolids": "true",
633633
"configuration": configuration,
634634
},
635635
log_response=log_response,

onshape_robotics_toolkit/models/assembly.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ class PartMateConnector(BaseModel):
357357
featureId: str = Field(..., description="The unique identifier of the mate connector feature.")
358358

359359

360+
class BodyType(str, Enum):
361+
SOLID = "solid"
362+
SHEET = "sheet"
363+
COMPOSITE = "composite"
364+
365+
360366
class Part(IDBase):
361367
"""
362368
Represents a part within an assembly, including its properties and configuration.
@@ -378,7 +384,7 @@ class Part(IDBase):
378384
Attributes:
379385
isStandardContent (bool): Indicates if the part is standard content.
380386
partId (str): The unique identifier of the part.
381-
bodyType (str): The type of the body (e.g., solid, surface).
387+
bodyType (BodyType): The type of the body (e.g., solid, surface).
382388
383389
Custom Attributes:
384390
MassProperty (Union[MassProperties, None]): The mass properties of the part, if available.
@@ -400,7 +406,7 @@ class Part(IDBase):
400406

401407
isStandardContent: bool = Field(..., description="Indicates if the part is standard content.")
402408
partId: str = Field(..., description="The unique identifier of the part.")
403-
bodyType: str = Field(..., description="The type of the body (e.g., solid, surface).")
409+
bodyType: BodyType = Field(..., description="The type of the body (e.g., solid, surface).")
404410
mateConnectors: list[Union[PartMateConnector, None]] = Field(
405411
default_factory=list,
406412
description="The mate connectors that belong to the part.",

onshape_robotics_toolkit/parse.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
AssemblyFeature,
3030
AssemblyFeatureType,
3131
AssemblyInstance,
32+
BodyType,
3233
MatedCS,
3334
MateFeatureData,
3435
MateGroupFeatureData,
@@ -539,10 +540,17 @@ def estimate_api_calls(
539540
Estimated remaining API calls: 24
540541
"""
541542
num_kinematic_parts = sum(
542-
1 for key, part in self.parts.items() if part.rigidAssemblyToPartTF is None and not part.isRigidAssembly
543+
1
544+
for key, part in self.parts.items()
545+
if part.rigidAssemblyToPartTF is None and not part.isRigidAssembly and not self.instances[key].suppressed
543546
)
544547

545-
num_rigid_subassemblies = sum(1 for key, sub in self.subassemblies.items() if sub.isRigid)
548+
num_rigid_subassemblies = sum(
549+
1 for key, sub in self.subassemblies.items() if sub.isRigid and not self.instances[key].suppressed
550+
)
551+
num_flexible_subassemblies = sum(
552+
1 for key, sub in self.subassemblies.items() if not self.instances[key].suppressed
553+
)
546554

547555
# Each rigid subassembly needs get_root_assembly to fetch occurrence data
548556
subassembly_calls = num_rigid_subassemblies
@@ -553,7 +561,7 @@ def estimate_api_calls(
553561

554562
mate_property_calls = 0
555563
if fetch_mate_properties:
556-
mate_property_calls = 1 + len(self.subassemblies) - num_rigid_subassemblies
564+
mate_property_calls = 1 + num_flexible_subassemblies - num_rigid_subassemblies
557565

558566
mesh_download_calls = 0
559567
if download_meshes:
@@ -1285,7 +1293,7 @@ def _populate_parts(self, assembly: Assembly) -> None:
12851293
self.parts[key] = Part(
12861294
isStandardContent=False,
12871295
partId=subassembly_instance.elementId, # Use element ID as part ID
1288-
bodyType="assembly",
1296+
bodyType=BodyType.SOLID,
12891297
documentId=subassembly_instance.documentId,
12901298
elementId=subassembly_instance.elementId,
12911299
documentMicroversion=subassembly_instance.documentMicroversion,
@@ -1329,6 +1337,12 @@ async def _fetch_mass_properties(key: PathKey, part: Part, client: Client) -> No
13291337

13301338
tasks = []
13311339
for key, part in self.parts.items():
1340+
if part.bodyType == BodyType.SHEET.value:
1341+
continue # skip surface bodies
1342+
1343+
if self.instances[key].suppressed:
1344+
continue
1345+
13321346
if part.MassProperty is not None:
13331347
logger.debug(f"Part {key} already has mass properties, skipping")
13341348
continue
@@ -1387,6 +1401,9 @@ async def _fetch_rootassembly(key: PathKey, subassembly: SubAssembly, client: Cl
13871401

13881402
tasks = []
13891403
for key, subassembly in self.subassemblies.items():
1404+
if self.instances[key].suppressed:
1405+
continue
1406+
13901407
if subassembly.RootOccurrences is not None:
13911408
logger.debug(f"Subassembly {key} already has RootOccurrences, skipping")
13921409
continue
@@ -1430,6 +1447,9 @@ def fetch_mate_limits(self, client: Optional[Client]) -> None:
14301447

14311448
# Add flexible subassemblies
14321449
for sub_key, subassembly in self.subassemblies.items():
1450+
if self.instances[sub_key].suppressed:
1451+
continue
1452+
14331453
if not subassembly.isRigid:
14341454
assemblies_to_fetch.append((
14351455
sub_key,

0 commit comments

Comments
 (0)