diff --git a/ffDesign_CounterboreBridges.py b/ffDesign_CounterboreBridges.py index f016252..9cb12ad 100644 --- a/ffDesign_CounterboreBridges.py +++ b/ffDesign_CounterboreBridges.py @@ -106,12 +106,12 @@ def make_upside_down_counterbores(body, hole): if not Utils.hole_has_counterbore_sure(hole): Utils.warning_confirm_proceed("Selected Hole does not seem to have a known counterbore type.") - profile_sketch = Utils.get_hole_profile_sketch(hole) + profile_sketch, profile_partial = Utils.get_hole_profile_sketch(hole) sketch_bridges_y = Utils.make_derived_sketch(body, profile_sketch, "_BridgesY") sketch_bridges_x = Utils.make_derived_sketch(body, profile_sketch, "_BridgesX") - for hole_loc in Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole)): + for hole_loc in Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole), profile_partial): # Create parametric y bridges cutout for this circle make_parametric_y_cutout( sketch_bridges_y, diff --git a/ffDesign_RibThreads.py b/ffDesign_RibThreads.py index cd2cadf..4d08c0e 100644 --- a/ffDesign_RibThreads.py +++ b/ffDesign_RibThreads.py @@ -308,7 +308,7 @@ def make_rib_threads(body, hole, global_template: bool, rib_param: RibParameters Utils.assert_body(body) Utils.assert_hole(hole) - profile_sketch = Utils.get_hole_profile_sketch(hole) + profile_sketch, profile_partial = Utils.get_hole_profile_sketch(hole) template = get_or_create_rib_template(body, hole, global_template, rib_param) @@ -327,7 +327,7 @@ def make_rib_threads(body, hole, global_template: bool, rib_param: RibParameters sketch_entrance = Utils.make_derived_sketch(body, profile_sketch, "_ThreadEntrance") - circles = Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole)) + circles = Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole), profile_partial) if len(circles) == 1 and not global_template: # In the special case of the Hole only having one circle and we have # generated a local template, we can just move this local template in diff --git a/ffDesign_RoofBridge.py b/ffDesign_RoofBridge.py index 1b8f328..476080b 100644 --- a/ffDesign_RoofBridge.py +++ b/ffDesign_RoofBridge.py @@ -99,13 +99,13 @@ def make_roof_bridges( hole.addProperty("App::PropertyLength", "RoofBridgeClearance", group="FusedFilamentDesign") hole.RoofBridgeClearance = bridge_clearance - profile_sketch = Utils.get_hole_profile_sketch(hole) + profile_sketch, profile_partial = Utils.get_hole_profile_sketch(hole) roofbridge_sketch = Utils.make_derived_sketch(body, profile_sketch, "_RoofBridge") roofbridge_sketch_other_side = None if do_doublesided: roofbridge_sketch_other_side = Utils.make_derived_sketch(body, profile_sketch, "_RoofBridge2") - hole_locations = Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole)) + hole_locations = Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole), profile_partial) for hole_loc in hole_locations: make_parametric_roof_bridge( roofbridge_sketch, diff --git a/ffDesign_Teardrop.py b/ffDesign_Teardrop.py index 7076059..9de0e5f 100644 --- a/ffDesign_Teardrop.py +++ b/ffDesign_Teardrop.py @@ -109,10 +109,10 @@ def make_teardrops( hole.addProperty("App::PropertyAngle", "TeardropRotation", group="FusedFilamentDesign") hole.TeardropRotation = rotation - profile_sketch = Utils.get_hole_profile_sketch(hole) + profile_sketch, profile_partial = Utils.get_hole_profile_sketch(hole) teardrop_sketch = Utils.make_derived_sketch(body, profile_sketch, "_Teardrops") - hole_locations = Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole)) + hole_locations = Utils.get_sketch_locations(profile_sketch, Utils.get_hole_profile_type(hole), profile_partial) for hole_loc in hole_locations: make_parametric_teardrop( teardrop_sketch, diff --git a/ffDesign_Tests.py b/ffDesign_Tests.py index e384c0d..0022cbd 100644 --- a/ffDesign_Tests.py +++ b/ffDesign_Tests.py @@ -122,6 +122,24 @@ def test_construction_and_non_defining(self): self.assert_expected_body() +class PartialHoleProfiles(ffDesignTestCase_Fc11): + test_document = "PartialHoleProfile.FCStd" + + @unittest.expectedFailure + def test_plausibility(self): + self.prepare_regression_test() + self.assert_expected_body() + + def test_partial_profile(self): + """ + Test that a PartDesign_Hole using a partial sketch as its profile is + correctly handled by the counterbore bridges tool. + """ + self.prepare_regression_test() + ffDesign_CounterboreBridges.CounterboreBridgesCommand().Activated() + self.assert_expected_body() + + class RibThreads(ffDesignTestCase): test_document = "TapHoles.FCStd" diff --git a/ffDesign_Utils.py b/ffDesign_Utils.py index 703f003..6a26366 100644 --- a/ffDesign_Utils.py +++ b/ffDesign_Utils.py @@ -225,12 +225,14 @@ def get_hole_profile_sketch(hole): if profile_sketch.TypeId != "Sketcher::SketchObject": raise ffDesignError("Hole profile must be a Sketch!") - if hole.Profile[1] != [""]: - raise ffDesignError( - "Only some parts of the sketch are selected as profile for the PartDesign_Hole. This is not supported yet!" - ) - - return profile_sketch + if hole.Profile[1] in ([""], []): + return (profile_sketch, None) + else: + # Only some edges of the Sketch are selected. We prepare identifying + # information for them so we can later filter for only these + # geometries: + profile_partial = [profile_sketch.Shape.getElementMappedName(edge_name) for edge_name in hole.Profile[1]] + return (profile_sketch, profile_partial) def make_derived_sketch(body, original, suffix: str): @@ -277,6 +279,35 @@ def sketch_external_geo_is_defining(sketch, index): return None +def profile_partial_contains_geo(sketch, profile_partial, geo_id) -> bool: + if profile_partial is None: + return True + + geometry_id = sketch.getGeometryId(geo_id) + ident = f"g{geometry_id}" + + for pp in profile_partial: + if ident in pp: + return True + + return False + + +def profile_partial_contains_external_geo(sketch, profile_partial, ext_geo_id) -> bool: + if profile_partial is None: + return True + + ext_geo = sketch.ExternalGeo[ext_geo_id] + geometry_id = Sketcher.ExternalGeometryFacade(ext_geo).Id + ident = f"e{geometry_id}" + + for pp in profile_partial: + if ident in pp: + return True + + return False + + @dataclasses.dataclass class LocationExprSet: vector_expr: str @@ -284,7 +315,7 @@ class LocationExprSet: y_expr: str -def get_sketch_locations(sketch, profile_type): +def get_sketch_locations(sketch, profile_type, profile_partial=None): assert_sketch(sketch) def try_make_loc_expr_set(index, obj, kind): @@ -325,6 +356,10 @@ def try_make_loc_expr_set(index, obj, kind): if sketch.getConstruction(i): continue + # If we are filtering for partial selection, check if this geo is included + if not profile_partial_contains_geo(sketch, profile_partial, i): + continue + loc = try_make_loc_expr_set(i, obj, "Geometry") if loc is not None: locations.append(loc) @@ -334,6 +369,10 @@ def try_make_loc_expr_set(index, obj, kind): if not sketch_external_geo_is_defining(sketch, i): continue + # If we are filtering for partial selection, check if this external geo is included + if not profile_partial_contains_external_geo(sketch, profile_partial, i): + continue + loc = try_make_loc_expr_set(i, obj, "ExternalGeo") if loc is not None: locations.append(loc)