diff --git a/.gitignore b/.gitignore index 7c6a1f9..9bf0e88 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,13 @@ cad-experience-plugin/parser/input/* !cad-experience-plugin/parser/input/.gitkeep cad-experience-plugin/parser/output/* !cad-experience-plugin/parser/output/.gitkeep + +# CAD experience benchmark scratch geometry and private case evidence +cad-experience-plugin/benchmarks/**/generated/ +cad-experience-plugin/benchmarks/**/quarantine-*/ +cad-experience-plugin/benchmarks/**/private-case.json +cad-experience-plugin/benchmarks/**/source-inspect.json +cad-experience-plugin/benchmarks/**/train-semantic-batch.json +cad-experience-plugin/benchmarks/**/train_cases/ +cad-experience-plugin/benchmarks/**/test_cases/ +cad-experience-plugin/benchmarks/**/cases/ diff --git a/cad-experience-library/library.json b/cad-experience-library/library.json index 066f90f..0551839 100644 --- a/cad-experience-library/library.json +++ b/cad-experience-library/library.json @@ -1,9 +1,9 @@ { "schema_version": "2.0", "library_kind": "generalized_cad_experience", - "generated_at": "2026-07-23T06:43:51+00:00", + "generated_at": "2026-07-23T11:56:44+00:00", "induction_mode": "llm_proposals_with_deterministic_evidence_verification", - "status": "collecting_evidence", + "status": "ready", "policy": { "instance_parameters_allowed": false, "absolute_coordinates_allowed": false, @@ -15,25 +15,83 @@ "minimum_confidence": 0.8 }, "corpus_summary": { - "accepted_case_count": 1, + "accepted_case_count": 1000, "duplicate_case_count": 0, "rejected_case_count": 0, - "family_count": 1 + "family_count": 11 }, "experience_summary": { - "llm_proposal_count": 3, - "promoted_experience_count": 0, + "llm_proposal_count": 133, + "promoted_experience_count": 130, "candidate_experience_count": 3, - "by_kind": [], - "by_scope": [], - "candidate_by_kind": [ + "by_kind": [ { "kind": "constraint", - "count": 1 + "count": 30 + }, + { + "kind": "dimensionless_distribution", + "count": 60 }, { "kind": "feature_motif", - "count": 1 + "count": 22 + }, + { + "kind": "reconstruction_grammar", + "count": 9 + }, + { + "kind": "validation_rule", + "count": 9 + } + ], + "by_scope": [ + { + "scope": "flanged_rotational_part", + "count": 20 + }, + { + "scope": "general_mechanical_part", + "count": 4 + }, + { + "scope": "hollow_or_stepped_shaft", + "count": 15 + }, + { + "scope": "multi_axis_manifold", + "count": 12 + }, + { + "scope": "patterned_plate", + "count": 16 + }, + { + "scope": "patterned_prismatic_part", + "count": 18 + }, + { + "scope": "plate_or_bracket", + "count": 7 + }, + { + "scope": "prismatic_block", + "count": 9 + }, + { + "scope": "rotational_part", + "count": 14 + }, + { + "scope": "shaft", + "count": 15 + } + ], + "candidate_by_kind": [ + { + "kind": "reconstruction_grammar", + "count": 2 }, { "kind": "validation_rule", @@ -41,81 +99,4026 @@ } ] }, - "experiences": [], - "candidate_experiences": [ + "experiences": [ { - "id": "constraint.patterned_prismatic_part.repeated_hole_radius_consistency", + "id": "constraint.flanged_rotational_part.coaxial_stack", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 48, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.flanged_rotational_part.common_pattern_radius", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "common_pattern_radius" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 48, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.flanged_rotational_part.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 48, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.flanged_rotational_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 48, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.flanged_rotational_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 48, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.flanged_rotational_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 23, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.hollow_or_stepped_shaft.coaxial_stack", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "central_passage" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 46, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.hollow_or_stepped_shaft.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "central_passage" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 46, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.hollow_or_stepped_shaft.shared_passage_axis", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "central_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 46, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.hollow_or_stepped_shaft.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 35, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.multi_axis_manifold.multiple_axis_system", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "compact_body" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 21, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.multi_axis_manifold.shared_passage_axis", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 21, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_plate.common_pattern_radius", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "common_pattern_radius" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 66, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_plate.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 76, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_plate.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "circular_equal_radius_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 66, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_plate.shared_passage_axis", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 108, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_plate.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 54, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_prismatic_part.common_pattern_radius", "kind": "constraint", "scope": [ "patterned_prismatic_part" ], "when": { "features": [ - "repeated_axial_hole_pattern" + "circular_equal_radius_pattern" + ], + "relations": [ + "common_pattern_radius" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 159, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_prismatic_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 156, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_prismatic_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "conical_transition" ], "relations": [ "repeated_radius_group" ] }, - "guidance": "Treat repeated axial holes as one radius-consistent semantic pattern rather than unrelated cuts.", - "semantic_rationale": "A shared pattern relationship makes count, placement, and hole-role edits coherent across dimensional variants.", - "support": 1, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 87, "confidence": 1.0, - "check": "Confirm that all members intended to belong to the repeated axial hole pattern retain a common hole-radius role.", - "promotion_state": "candidate", - "required_support": 20, - "remaining_support": 19, - "required_confidence": 0.8, - "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + "check": "Verify the participating features retain the intended shared geometric relationship." }, { - "id": "motif.patterned_prismatic_part.passage_with_repeated_axial_holes", + "id": "constraint.patterned_prismatic_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 167, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.patterned_prismatic_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 87, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.rotational_part.coaxial_stack", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 123, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.rotational_part.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 123, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.rotational_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 123, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.rotational_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 59, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.shaft.coaxial_stack", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 40, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.shaft.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 40, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.shaft.shared_passage_axis", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 40, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "constraint.shaft.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Preserve this semantic relationship whenever the participating feature roles are instantiated.", + "semantic_rationale": "The relationship remains meaningful when dimensions and feature counts vary.", + "support": 27, + "confidence": 1.0, + "check": "Verify the participating features retain the intended shared geometric relationship." + }, + { + "id": "distribution.flanged_rotational_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.01, + "p10": 0.174882, + "median": 0.902597, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.flanged_rotational_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.005, + "p10": 0.039066, + "median": 0.275, + "p90": 0.647113, + "max": 0.886752 + } + }, + { + "id": "distribution.flanged_rotational_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.024972, + "p10": 0.098071, + "median": 0.315931, + "p90": 0.654045, + "max": 0.790983 + } + }, + { + "id": "distribution.flanged_rotational_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.002586, + "p10": 0.094383, + "median": 0.42875, + "p90": 0.5, + "max": 0.657143 + } + }, + { + "id": "distribution.flanged_rotational_part.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.113333, + "p10": 0.263632, + "median": 0.411932, + "p90": 0.514398, + "max": 0.587836 + } + }, + { + "id": "distribution.flanged_rotational_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.139254, + "p10": 0.318228, + "median": 0.680179, + "p90": 0.901929, + "max": 0.975028 + } + }, + { + "id": "distribution.flanged_rotational_part.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.001172, + "p10": 0.0077, + "median": 0.033654, + "p90": 0.085498, + "max": 0.313751 + } + }, + { + "id": "distribution.flanged_rotational_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.057143, + "p10": 0.17219, + "median": 0.441667, + "p90": 0.782459, + "max": 0.974786 + } + }, + { + "id": "distribution.general_mechanical_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 20, + "confidence": 1.0, + "distribution": { + "sample_count": 20, + "min": 0.0, + "p10": 0.015724, + "median": 0.112837, + "p90": 0.392145, + "max": 0.866025 + } + }, + { + "id": "distribution.general_mechanical_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 20, + "confidence": 1.0, + "distribution": { + "sample_count": 20, + "min": 0.0, + "p10": 0.015724, + "median": 0.112837, + "p90": 0.346355, + "max": 0.433013 + } + }, + { + "id": "distribution.general_mechanical_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 20, + "confidence": 1.0, + "distribution": { + "sample_count": 20, + "min": 0.0, + "p10": 0.690689, + "median": 0.924961, + "p90": 0.985887, + "max": 1.0 + } + }, + { + "id": "distribution.general_mechanical_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 20, + "confidence": 1.0, + "distribution": { + "sample_count": 20, + "min": 0.0, + "p10": 0.003469, + "median": 0.052351, + "p90": 0.168652, + "max": 0.546904 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 46, + "confidence": 1.0, + "distribution": { + "sample_count": 46, + "min": 0.029762, + "p10": 0.066024, + "median": 0.17739, + "p90": 0.275666, + "max": 0.333333 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 46, + "confidence": 1.0, + "distribution": { + "sample_count": 46, + "min": 0.029762, + "p10": 0.066024, + "median": 0.17739, + "p90": 0.275494, + "max": 0.333333 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 46, + "confidence": 1.0, + "distribution": { + "sample_count": 46, + "min": 0.009277, + "p10": 0.672519, + "median": 0.838041, + "p90": 0.965847, + "max": 0.986218 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 46, + "confidence": 1.0, + "distribution": { + "sample_count": 46, + "min": 0.175334, + "p10": 0.5, + "median": 0.5, + "p90": 0.5, + "max": 0.55 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 46, + "confidence": 1.0, + "distribution": { + "sample_count": 46, + "min": 0.013782, + "p10": 0.029085, + "median": 0.110078, + "p90": 0.254798, + "max": 0.328811 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 46, + "confidence": 1.0, + "distribution": { + "sample_count": 46, + "min": 0.14, + "p10": 0.236718, + "median": 0.345513, + "p90": 0.737214, + "max": 0.882353 + } + }, + { + "id": "distribution.multi_axis_manifold.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.018939, + "p10": 0.210295, + "median": 0.545455, + "p90": 0.934131, + "max": 1.0 + } + }, + { + "id": "distribution.multi_axis_manifold.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.0175, + "p10": 0.079546, + "median": 0.3, + "p90": 0.693875, + "max": 0.833333 + } + }, + { + "id": "distribution.multi_axis_manifold.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.009143, + "p10": 0.050841, + "median": 0.132265, + "p90": 0.534539, + "max": 0.864691 + } + }, + { + "id": "distribution.multi_axis_manifold.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 50, + "confidence": 1.0, + "distribution": { + "sample_count": 50, + "min": 0.021429, + "p10": 0.1225, + "median": 0.290119, + "p90": 0.507007, + "max": 1.806003 + } + }, + { + "id": "distribution.multi_axis_manifold.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 55, + "confidence": 1.0, + "distribution": { + "sample_count": 55, + "min": 0.135309, + "p10": 0.351032, + "median": 0.85919, + "p90": 0.949159, + "max": 0.990857 + } + }, + { + "id": "distribution.multi_axis_manifold.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 50, + "confidence": 1.0, + "distribution": { + "sample_count": 50, + "min": 0.003676, + "p10": 0.034035, + "median": 0.090454, + "p90": 0.335473, + "max": 1.900495 + } + }, + { + "id": "distribution.patterned_plate.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 209, + "confidence": 1.0, + "distribution": { + "sample_count": 209, + "min": 0.347826, + "p10": 0.395721, + "median": 0.663265, + "p90": 0.971699, + "max": 1.0 + } + }, + { + "id": "distribution.patterned_plate.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 209, + "confidence": 1.0, + "distribution": { + "sample_count": 209, + "min": 0.001092, + "p10": 0.017606, + "median": 0.076923, + "p90": 0.15, + "max": 0.214286 + } + }, + { + "id": "distribution.patterned_plate.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 209, + "confidence": 1.0, + "distribution": { + "sample_count": 209, + "min": 0.000133, + "p10": 0.012468, + "median": 0.069802, + "p90": 0.191394, + "max": 0.996549 + } + }, + { + "id": "distribution.patterned_plate.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 209, + "confidence": 1.0, + "distribution": { + "sample_count": 209, + "min": 0.088996, + "p10": 0.25, + "median": 0.388304, + "p90": 0.513597, + "max": 0.643742 + } + }, + { + "id": "distribution.patterned_plate.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 209, + "confidence": 1.0, + "distribution": { + "sample_count": 209, + "min": 0.003451, + "p10": 0.801314, + "median": 0.927227, + "p90": 0.983973, + "max": 0.999867 + } + }, + { + "id": "distribution.patterned_plate.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 209, + "confidence": 1.0, + "distribution": { + "sample_count": 209, + "min": 0.001522, + "p10": 0.00657, + "median": 0.025, + "p90": 0.053303, + "max": 0.304846 + } + }, + { + "id": "distribution.patterned_prismatic_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 339, + "confidence": 1.0, + "distribution": { + "sample_count": 339, + "min": 0.009341, + "p10": 0.074884, + "median": 0.365854, + "p90": 0.853398, + "max": 1.0 + } + }, + { + "id": "distribution.patterned_prismatic_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 339, + "confidence": 1.0, + "distribution": { + "sample_count": 339, + "min": 0.000667, + "p10": 0.026082, + "median": 0.2, + "p90": 0.506667, + "max": 0.961145 + } + }, + { + "id": "distribution.patterned_prismatic_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 339, + "confidence": 1.0, + "distribution": { + "sample_count": 339, + "min": 0.000252, + "p10": 0.024748, + "median": 0.113412, + "p90": 0.299477, + "max": 0.820212 + } + }, + { + "id": "distribution.patterned_prismatic_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 33, + "confidence": 1.0, + "distribution": { + "sample_count": 33, + "min": 0.00431, + "p10": 0.014977, + "median": 0.0875, + "p90": 0.209382, + "max": 0.45 + } + }, + { + "id": "distribution.patterned_prismatic_part.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 339, + "confidence": 1.0, + "distribution": { + "sample_count": 339, + "min": 0.010638, + "p10": 0.212193, + "median": 0.333599, + "p90": 0.482526, + "max": 0.64955 + } + }, + { + "id": "distribution.patterned_prismatic_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 339, + "confidence": 1.0, + "distribution": { + "sample_count": 339, + "min": 0.142723, + "p10": 0.683922, + "median": 0.88492, + "p90": 0.973808, + "max": 0.999748 + } + }, + { + "id": "distribution.patterned_prismatic_part.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 339, + "confidence": 1.0, + "distribution": { + "sample_count": 339, + "min": 0.000833, + "p10": 0.0075, + "median": 0.035, + "p90": 0.111111, + "max": 0.483198 + } + }, + { + "id": "distribution.patterned_prismatic_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 33, + "confidence": 1.0, + "distribution": { + "sample_count": 33, + "min": 1.0, + "p10": 1.0, + "median": 1.0, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.plate_or_bracket.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 37, + "confidence": 1.0, + "distribution": { + "sample_count": 37, + "min": 0.363636, + "p10": 0.441666, + "median": 0.637255, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.plate_or_bracket.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 37, + "confidence": 1.0, + "distribution": { + "sample_count": 37, + "min": 0.002381, + "p10": 0.00639, + "median": 0.045455, + "p90": 0.149714, + "max": 0.214286 + } + }, + { + "id": "distribution.plate_or_bracket.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 37, + "confidence": 1.0, + "distribution": { + "sample_count": 37, + "min": 0.0, + "p10": 0.0, + "median": 0.0, + "p90": 0.039962, + "max": 0.253783 + } + }, + { + "id": "distribution.plate_or_bracket.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 37, + "confidence": 1.0, + "distribution": { + "sample_count": 37, + "min": 0.746217, + "p10": 0.958585, + "median": 1.0, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.prismatic_block.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 72, + "confidence": 1.0, + "distribution": { + "sample_count": 72, + "min": 0.009231, + "p10": 0.050836, + "median": 0.401005, + "p90": 0.866025, + "max": 1.0 + } + }, + { + "id": "distribution.prismatic_block.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 72, + "confidence": 1.0, + "distribution": { + "sample_count": 72, + "min": 0.0, + "p10": 0.010426, + "median": 0.2008, + "p90": 0.545129, + "max": 0.95 + } + }, + { + "id": "distribution.prismatic_block.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 72, + "confidence": 1.0, + "distribution": { + "sample_count": 72, + "min": 0.0, + "p10": 0.0, + "median": 0.030749, + "p90": 0.238869, + "max": 0.351376 + } + }, + { + "id": "distribution.prismatic_block.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 25, + "confidence": 1.0, + "distribution": { + "sample_count": 25, + "min": 0.027273, + "p10": 0.070947, + "median": 0.324561, + "p90": 0.60353, + "max": 0.707107 + } + }, + { + "id": "distribution.prismatic_block.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 72, + "confidence": 1.0, + "distribution": { + "sample_count": 72, + "min": 0.60741, + "p10": 0.678579, + "median": 0.963402, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.prismatic_block.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 25, + "confidence": 1.0, + "distribution": { + "sample_count": 25, + "min": 0.006932, + "p10": 0.015009, + "median": 0.08, + "p90": 0.245733, + "max": 1.0 + } + }, + { + "id": "distribution.rotational_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 123, + "confidence": 1.0, + "distribution": { + "sample_count": 123, + "min": 0.333507, + "p10": 0.393271, + "median": 1.0, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.rotational_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 123, + "confidence": 1.0, + "distribution": { + "sample_count": 123, + "min": 0.033654, + "p10": 0.125, + "median": 0.410256, + "p90": 0.830952, + "max": 1.0 + } + }, + { + "id": "distribution.rotational_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 123, + "confidence": 1.0, + "distribution": { + "sample_count": 123, + "min": 0.079284, + "p10": 0.224996, + "median": 0.541667, + "p90": 0.817692, + "max": 0.977011 + } + }, + { + "id": "distribution.rotational_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 123, + "confidence": 1.0, + "distribution": { + "sample_count": 123, + "min": 0.13, + "p10": 0.405553, + "median": 0.5, + "p90": 0.5, + "max": 0.5 + } + }, + { + "id": "distribution.rotational_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 123, + "confidence": 1.0, + "distribution": { + "sample_count": 123, + "min": 0.01974, + "p10": 0.143071, + "median": 0.33689, + "p90": 0.664368, + "max": 0.88406 + } + }, + { + "id": "distribution.rotational_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 123, + "confidence": 1.0, + "distribution": { + "sample_count": 123, + "min": 0.092308, + "p10": 0.23, + "median": 0.480389, + "p90": 0.813062, + "max": 0.95 + } + }, + { + "id": "distribution.shaft.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 40, + "confidence": 1.0, + "distribution": { + "sample_count": 40, + "min": 0.03871, + "p10": 0.052165, + "median": 0.15095, + "p90": 0.302, + "max": 0.333333 + } + }, + { + "id": "distribution.shaft.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 40, + "confidence": 1.0, + "distribution": { + "sample_count": 40, + "min": 0.03871, + "p10": 0.052165, + "median": 0.149074, + "p90": 0.287143, + "max": 0.333333 + } + }, + { + "id": "distribution.shaft.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 40, + "confidence": 1.0, + "distribution": { + "sample_count": 40, + "min": 0.084584, + "p10": 0.316214, + "median": 0.917311, + "p90": 0.96565, + "max": 0.996933 + } + }, + { + "id": "distribution.shaft.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 40, + "confidence": 1.0, + "distribution": { + "sample_count": 40, + "min": 0.13125, + "p10": 0.363481, + "median": 0.5, + "p90": 0.5, + "max": 0.5 + } + }, + { + "id": "distribution.shaft.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 40, + "confidence": 1.0, + "distribution": { + "sample_count": 40, + "min": 0.003067, + "p10": 0.021978, + "median": 0.067524, + "p90": 0.571665, + "max": 0.908523 + } + }, + { + "id": "distribution.shaft.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the learned dimensionless range only when the user leaves the corresponding relationship unspecified.", + "semantic_rationale": "A normalized relationship transfers across scale while explicit user dimensions remain authoritative.", + "support": 40, + "confidence": 1.0, + "distribution": { + "sample_count": 40, + "min": 0.2, + "p10": 0.32825, + "median": 0.666667, + "p90": 0.93875, + "max": 0.96 + } + }, + { + "id": "motif.flanged_rotational_part.coaxial_cylindrical_stack+stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 55, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.planar_dominant_body+multi_level_planar_profile", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 29, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.repeated_axial_hole_pattern+circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 48, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.rotational_body+coaxial_cylindrical_stack", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 55, + "confidence": 1.0 + }, + { + "id": "motif.hollow_or_stepped_shaft.coaxial_cylindrical_stack+stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 46, + "confidence": 1.0 + }, + { + "id": "motif.hollow_or_stepped_shaft.rotational_body+coaxial_cylindrical_stack", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 46, + "confidence": 1.0 + }, + { + "id": "motif.hollow_or_stepped_shaft.shaft_like_body+elongated_body", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "elongated_body", + "shaft_like_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 46, + "confidence": 1.0 + }, + { + "id": "motif.multi_axis_manifold.cylindrical_feature_network+multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 55, + "confidence": 1.0 + }, + { + "id": "motif.multi_axis_manifold.planar_dominant_body+multi_level_planar_profile", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 44, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.cylindrical_feature_network+multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 76, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.planar_dominant_body+multi_level_planar_profile", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 202, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.repeated_axial_hole_pattern+circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 66, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network+multi_axis_passage", "kind": "feature_motif", "scope": [ "patterned_prismatic_part" ], "when": { "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 156, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.planar_dominant_body+multi_level_planar_profile", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 323, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.repeated_axial_hole_pattern+circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 159, + "confidence": 1.0 + }, + { + "id": "motif.plate_or_bracket.planar_dominant_body+multi_level_planar_profile", + "kind": "feature_motif", + "scope": [ + "plate_or_bracket" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 35, + "confidence": 1.0 + }, + { + "id": "motif.prismatic_block.planar_dominant_body+multi_level_planar_profile", + "kind": "feature_motif", + "scope": [ + "prismatic_block" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 67, + "confidence": 1.0 + }, + { + "id": "motif.rotational_part.coaxial_cylindrical_stack+stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 123, + "confidence": 1.0 + }, + { + "id": "motif.rotational_part.rotational_body+coaxial_cylindrical_stack", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 123, + "confidence": 1.0 + }, + { + "id": "motif.shaft.coaxial_cylindrical_stack+stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 40, + "confidence": 1.0 + }, + { + "id": "motif.shaft.rotational_body+coaxial_cylindrical_stack", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 40, + "confidence": 1.0 + }, + { + "id": "motif.shaft.shaft_like_body+elongated_body", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "elongated_body", + "shaft_like_body" + ], + "relations": [] + }, + "guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.", + "semantic_rationale": "The composition recurs across independent dimensional variants in the same family.", + "support": 40, + "confidence": 1.0 + }, + { + "id": "reconstruction.flanged_rotational_part.primary", + "kind": "reconstruction_grammar", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "multi_level_planar_profile", + "repeated_axial_hole_pattern", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relations": [ + "coaxial_stack", + "common_pattern_radius", + "dominant_axis_alignment", + "repeated_radius_group", + "shared_passage_axis" + ] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 37, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "axial_segment_length_roles", + "coaxial_diameter_roles", + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "pattern_angular_or_linear_phase_role", + "pattern_member_diameter_role", + "pattern_radius_or_spacing_role", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span", + "shoulder_position_roles" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "circular_equal_radius_pattern", + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "multi_level_planar_profile", + "repeated_axial_hole_pattern", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relation_roles": [ + "coaxial_stack", + "common_pattern_radius", + "dominant_axis_alignment", + "repeated_radius_group", + "shared_passage_axis" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "revolve_profile", + "feature_roles": [ + "rotational_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "construct_coaxial_stack", + "operation": "add_or_cut_coaxial_profiles", + "feature_roles": [ + "coaxial_cylindrical_stack" + ], + "reference_roles": [ + "primary_axis" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "construct_repeated_feature_pattern", + "operation": "pattern_semantic_feature", + "feature_roles": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "circular_equal_radius_pattern", + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "multi_level_planar_profile", + "repeated_axial_hole_pattern", + "rotational_body", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "coaxial_stack", + "common_pattern_radius", + "dominant_axis_alignment", + "repeated_radius_group", + "shared_passage_axis", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.hollow_or_stepped_shaft.primary", + "kind": "reconstruction_grammar", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "elongated_body", + "multi_level_planar_profile", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relations": [ + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis" + ] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 39, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "axial_segment_length_roles", + "coaxial_diameter_roles", + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span", + "shoulder_position_roles" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "elongated_body", + "multi_level_planar_profile", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relation_roles": [ + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "revolve_profile", + "feature_roles": [ + "elongated_body", + "rotational_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "construct_coaxial_stack", + "operation": "add_or_cut_coaxial_profiles", + "feature_roles": [ + "coaxial_cylindrical_stack" + ], + "reference_roles": [ + "primary_axis" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "elongated_body", + "multi_level_planar_profile", + "rotational_body", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.multi_axis_manifold.primary", + "kind": "reconstruction_grammar", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "cylindrical_feature_network", "multi_axis_passage", + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 44, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "cylindrical_feature_network", + "multi_axis_passage", + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relation_roles": [ + "multiple_axis_system" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [ + "planar_dominant_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "cylindrical_feature_network", + "multi_axis_passage", + "multi_level_planar_profile", + "planar_dominant_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "multiple_axis_system", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.patterned_plate.primary", + "kind": "reconstruction_grammar", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_level_planar_profile", + "planar_dominant_body", + "plate_like_body", "repeated_axial_hole_pattern" ], "relations": [ "repeated_radius_group" ] }, - "guidance": "Represent the passage system and repeated axial holes as distinct semantic feature groups within the same prismatic part.", - "semantic_rationale": "Separating passage roles from the repeated mounting pattern allows either group to change while preserving the other group’s geometric intent.", - "support": 1, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 202, "confidence": 1.0, - "promotion_state": "candidate", - "required_support": 20, - "remaining_support": 19, - "required_confidence": 0.8, - "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "pattern_angular_or_linear_phase_role", + "pattern_member_diameter_role", + "pattern_radius_or_spacing_role", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "cylindrical_feature_network", + "multi_level_planar_profile", + "planar_dominant_body", + "plate_like_body", + "repeated_axial_hole_pattern" + ], + "relation_roles": [ + "repeated_radius_group" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [ + "planar_dominant_body", + "plate_like_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "construct_repeated_feature_pattern", + "operation": "pattern_semantic_feature", + "feature_roles": [ + "repeated_axial_hole_pattern" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "cylindrical_feature_network", + "multi_level_planar_profile", + "planar_dominant_body", + "plate_like_body", + "repeated_axial_hole_pattern" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "repeated_radius_group", + "surface_mix_consistency" + ] + } }, { - "id": "validation.patterned_prismatic_part.feature_group_integrity", + "id": "reconstruction.patterned_prismatic_part.primary", + "kind": "reconstruction_grammar", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_level_planar_profile", + "planar_dominant_body", + "repeated_axial_hole_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 323, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "pattern_angular_or_linear_phase_role", + "pattern_member_diameter_role", + "pattern_radius_or_spacing_role", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "cylindrical_feature_network", + "multi_level_planar_profile", + "planar_dominant_body", + "repeated_axial_hole_pattern" + ], + "relation_roles": [ + "repeated_radius_group" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [ + "planar_dominant_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "construct_repeated_feature_pattern", + "operation": "pattern_semantic_feature", + "feature_roles": [ + "repeated_axial_hole_pattern" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "cylindrical_feature_network", + "multi_level_planar_profile", + "planar_dominant_body", + "repeated_axial_hole_pattern" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "repeated_radius_group", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.plate_or_bracket.primary", + "kind": "reconstruction_grammar", + "scope": [ + "plate_or_bracket" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body", + "plate_like_body" + ], + "relations": [] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 35, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "multi_level_planar_profile", + "planar_dominant_body", + "plate_like_body" + ], + "relation_roles": [], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [ + "planar_dominant_body", + "plate_like_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "multi_level_planar_profile", + "planar_dominant_body", + "plate_like_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.prismatic_block.primary", + "kind": "reconstruction_grammar", + "scope": [ + "prismatic_block" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 67, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "planar_level_offset_roles", + "primary_axis_span", + "primary_transverse_span", + "profile_length_roles", + "profile_width_roles", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relation_roles": [], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [ + "planar_dominant_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": [ + "multi_level_planar_profile" + ], + "reference_roles": [ + "primary_transverse_plane" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.rotational_part.primary", + "kind": "reconstruction_grammar", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relations": [ + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis" + ] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 123, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "axial_segment_length_roles", + "coaxial_diameter_roles", + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "primary_axis_span", + "primary_transverse_span", + "secondary_transverse_span", + "shoulder_position_roles" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relation_roles": [ + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "revolve_profile", + "feature_roles": [ + "rotational_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "construct_coaxial_stack", + "operation": "add_or_cut_coaxial_profiles", + "feature_roles": [ + "coaxial_cylindrical_stack" + ], + "reference_roles": [ + "primary_axis" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "rotational_body", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis", + "surface_mix_consistency" + ] + } + }, + { + "id": "reconstruction.shaft.primary", + "kind": "reconstruction_grammar", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "elongated_body", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relations": [ + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis" + ] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 40, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "axial_segment_length_roles", + "coaxial_diameter_roles", + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "primary_axis_span", + "primary_transverse_span", + "secondary_transverse_span", + "shoulder_position_roles" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "elongated_body", + "rotational_body", + "stepped_cylindrical_passage" + ], + "relation_roles": [ + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis" + ], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "revolve_profile", + "feature_roles": [ + "elongated_body", + "rotational_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "construct_coaxial_stack", + "operation": "add_or_cut_coaxial_profiles", + "feature_roles": [ + "coaxial_cylindrical_stack" + ], + "reference_roles": [ + "primary_axis" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "coaxial_cylindrical_stack", + "cylindrical_feature_network", + "elongated_body", + "rotational_body", + "stepped_cylindrical_passage" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "coaxial_stack", + "dominant_axis_alignment", + "shared_passage_axis", + "surface_mix_consistency" + ] + } + }, + { + "id": "validation.flanged_rotational_part.family_structure", + "kind": "validation_rule", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "rotational_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 55, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.hollow_or_stepped_shaft.family_structure", + "kind": "validation_rule", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "rotational_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 46, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.multi_axis_manifold.family_structure", + "kind": "validation_rule", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 44, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.patterned_plate.family_structure", + "kind": "validation_rule", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 207, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.patterned_prismatic_part.family_structure", "kind": "validation_rule", "scope": [ "patterned_prismatic_part" ], "when": { "features": [ - "multi_axis_passage", - "repeated_axial_hole_pattern" + "planar_dominant_body" ], - "relations": [ - "repeated_radius_group" + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 324, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.plate_or_bracket.family_structure", + "kind": "validation_rule", + "scope": [ + "plate_or_bracket" + ], + "when": { + "features": [ + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 37, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.prismatic_block.family_structure", + "kind": "validation_rule", + "scope": [ + "prismatic_block" + ], + "when": { + "features": [ + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 72, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.rotational_part.family_structure", + "kind": "validation_rule", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "rotational_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 123, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + }, + { + "id": "validation.shaft.family_structure", + "kind": "validation_rule", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "rotational_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 40, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships." + } + ], + "candidate_experiences": [ + { + "id": "reconstruction.general_mechanical_part.primary", + "kind": "reconstruction_grammar", + "scope": [ + "general_mechanical_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "elongated_body" + ], + "relations": [] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 16, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "passage_axis_offset_roles", + "passage_diameter_roles", + "passage_extent_roles", + "primary_axis_span", + "primary_transverse_span", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [ + "cylindrical_feature_network", + "elongated_body" + ], + "relation_roles": [], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [ + "elongated_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + "cylindrical_feature_network" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [ + "cylindrical_feature_network", + "elongated_body" + ], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "surface_mix_consistency" ] }, - "guidance": "Validate the passage system and repeated axial hole pattern as separate relationship groups after regeneration.", - "semantic_rationale": "Relationship-group validation can detect unintended coupling or pattern drift without relying on one source part’s dimensions.", - "support": 1, - "confidence": 1.0, - "check": "Confirm that passage features remain distinct from the repeated axial hole group and that the repeated group remains internally coherent.", "promotion_state": "candidate", "required_support": 20, - "remaining_support": 19, + "remaining_support": 4, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "reconstruction.hybrid_freeform_part.primary", + "kind": "reconstruction_grammar", + "scope": [ + "hybrid_freeform_part" + ], + "when": { + "features": [], + "relations": [] + }, + "guidance": "Establish semantic datums and a primary envelope before composing passages, repeated features, and finishing regions.", + "semantic_rationale": "The symbolic reconstruction stages preserve family structure while allowing every dimension to vary.", + "support": 4, + "confidence": 1.0, + "check": "Confirm the reconstructed part preserves the requested feature roles, datum relationships, and closed solid topology.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "primary_axis_span", + "primary_transverse_span", + "secondary_transverse_span" + ], + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ], + "feature_roles": [], + "relation_roles": [], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane" + ] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis" + ] + }, + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis" + ] + } + ], + "cardinality_classes": [], + "validation_roles": [ + "bounding_proportion_consistency", + "closed_solid", + "surface_mix_consistency" + ] + }, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 16, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "validation.general_mechanical_part.family_structure", + "kind": "validation_rule", + "scope": [ + "general_mechanical_part" + ], + "when": { + "features": [ + "elongated_body" + ], + "relations": [] + }, + "guidance": "Validate the family envelope, feature-role presence, datum relationships, and solid topology together.", + "semantic_rationale": "Joint validation catches structurally wrong reconstructions that isolated dimension checks can miss.", + "support": 16, + "confidence": 1.0, + "check": "Confirm a closed solid with the expected family envelope, feature roles, and datum relationships.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 4, "required_confidence": 0.8, "consumer_policy": "visible_for_review_but_not_available_to_cad_router" } diff --git a/cad-experience-plugin/.codex-plugin/plugin.json b/cad-experience-plugin/.codex-plugin/plugin.json index 37021da..85ce84a 100644 --- a/cad-experience-plugin/.codex-plugin/plugin.json +++ b/cad-experience-plugin/.codex-plugin/plugin.json @@ -1,15 +1,15 @@ { "name": "cad-experience-plugin", - "version": "0.1.0", - "description": "Parse STEP inboxes and use the active model plus deterministic evidence checks to distill backend-neutral CAD experience.", + "version": "0.1.0+codex.20260723115752", + "description": "Parse STEP inboxes and use the active model plus deterministic evidence checks to distill backend-neutral CAD reconstruction grammar and experience.", "author": { "name": "Local developer" }, "skills": "./skills/", "interface": { "displayName": "CAD Experience", - "shortDescription": "Use model review to distill reusable CAD methods.", - "longDescription": "CAD Experience maintains a visible STEP input/output staging area, gives the active large model a sanitized semantic review packet, verifies every proposed method against the accumulated corpus, and promotes only repeatedly supported backend-neutral experience.", + "shortDescription": "Distill reusable CAD reconstruction grammar.", + "longDescription": "CAD Experience maintains a visible STEP input/output staging area, gives the active large model sanitized symbolic parameters, datums, canonical stages, constraints, and validation evidence, verifies every proposal against the accumulated corpus, and promotes only repeatedly supported backend-neutral reconstruction grammar and experience.", "developerName": "Local developer", "category": "Productivity", "capabilities": [ @@ -18,9 +18,10 @@ "Batch CAD experience induction", "Large-model semantic review", "Deterministic proposal evidence verification", + "Family-level symbolic reconstruction grammar", "Generalized experience queries", "Instance-data leakage audits" ], - "defaultPrompt": "Parse the STEP input folder, distill the output JSON, and report promoted generalized CAD experience without exposing source-part geometry." + "defaultPrompt": "Parse the STEP input folder, distill symbolic family-level reconstruction grammar from the output JSON, and report promoted generalized CAD experience without exposing source-part geometry." } } diff --git a/cad-experience-plugin/benchmarks/ab50/batch-results-25-50.json b/cad-experience-plugin/benchmarks/ab50/batch-results-25-50.json new file mode 100644 index 0000000..f10a56e --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/batch-results-25-50.json @@ -0,0 +1,100 @@ +[ + { + "id": "136930", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/136930/136930.step\"}", + "error": "" + }, + { + "id": "138022", + "variant": "baseline", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "138022", + "variant": "experience", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "136930", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/136930/136930.step\"}", + "error": "" + }, + { + "id": "134828", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/134828/134828.step\"}", + "error": "" + }, + { + "id": "116166", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/116166/116166.step\"}", + "error": "" + }, + { + "id": "134828", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/134828/134828.step\"}", + "error": "" + }, + { + "id": "145574", + "variant": "baseline", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "145574", + "variant": "experience", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "150196", + "variant": "baseline", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "150196", + "variant": "experience", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "150587", + "variant": "experience", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "150587", + "variant": "baseline", + "returncode": -9, + "output": "", + "error": "" + }, + { + "id": "116166", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/116166/116166.step\"}", + "error": "" + } +] diff --git a/cad-experience-plugin/benchmarks/ab50/batch-results.json b/cad-experience-plugin/benchmarks/ab50/batch-results.json new file mode 100644 index 0000000..2f043e4 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/batch-results.json @@ -0,0 +1,212 @@ +[ + { + "id": "000166", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"skipped\", \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/000166/000166.step\"}", + "error": "" + }, + { + "id": "011680", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"skipped\", \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/011680/011680.step\"}", + "error": "" + }, + { + "id": "000166", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/000166/000166.step\"}", + "error": "" + }, + { + "id": "011680", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/011680/011680.step\"}", + "error": "" + }, + { + "id": "016033", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/016033/016033.step\"}", + "error": "" + }, + { + "id": "016033", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/016033/016033.step\"}", + "error": "" + }, + { + "id": "004764", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/004764/004764.step\"}", + "error": "" + }, + { + "id": "020543", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/020543/020543.step\"}", + "error": "" + }, + { + "id": "024675", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/024675/024675.step\"}", + "error": "" + }, + { + "id": "004764", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/004764/004764.step\"}", + "error": "" + }, + { + "id": "029190", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/029190/029190.step\"}", + "error": "" + }, + { + "id": "024675", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/024675/024675.step\"}", + "error": "" + }, + { + "id": "020543", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/020543/020543.step\"}", + "error": "" + }, + { + "id": "029190", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/029190/029190.step\"}", + "error": "" + }, + { + "id": "030352", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/030352/030352.step\"}", + "error": "" + }, + { + "id": "046014", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/046014/046014.step\"}", + "error": "" + }, + { + "id": "046014", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/046014/046014.step\"}", + "error": "" + }, + { + "id": "030352", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/030352/030352.step\"}", + "error": "" + }, + { + "id": "047048", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/047048/047048.step\"}", + "error": "" + }, + { + "id": "048346", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/048346/048346.step\"}", + "error": "" + }, + { + "id": "048346", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/048346/048346.step\"}", + "error": "" + }, + { + "id": "047048", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/047048/047048.step\"}", + "error": "" + }, + { + "id": "053664", + "variant": "experience", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/053664/053664.step\"}", + "error": "" + }, + { + "id": "065487", + "variant": "experience", + "returncode": 1, + "output": "{\"status\": \"failed\", \"returncode\": 1, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/065487/065487.step\"}", + "error": "" + }, + { + "id": "067186", + "variant": "baseline", + "returncode": 1, + "output": "{\"status\": \"failed\", \"returncode\": 1, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/067186/067186.step\"}", + "error": "" + }, + { + "id": "053664", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/053664/053664.step\"}", + "error": "" + }, + { + "id": "067186", + "variant": "experience", + "returncode": 1, + "output": "{\"status\": \"failed\", \"returncode\": 1, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/067186/067186.step\"}", + "error": "" + }, + { + "id": "073706", + "variant": "baseline", + "returncode": 1, + "output": "{\"status\": \"failed\", \"returncode\": 1, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/073706/073706.step\"}", + "error": "" + }, + { + "id": "073706", + "variant": "experience", + "returncode": 1, + "output": "{\"status\": \"failed\", \"returncode\": 1, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/073706/073706.step\"}", + "error": "" + }, + { + "id": "065487", + "variant": "baseline", + "returncode": 0, + "output": "{\"status\": \"ok\", \"returncode\": 0, \"output\": \"/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/065487/065487.step\"}", + "error": "" + } +] diff --git a/cad-experience-plugin/benchmarks/ab50/empty-library.json b/cad-experience-plugin/benchmarks/ab50/empty-library.json new file mode 100644 index 0000000..863d9fc --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/empty-library.json @@ -0,0 +1,28 @@ +{ + "schema_version": "2.0", + "library_kind": "generalized_cad_experience", + "generated_at": "2026-07-23T09:46:25+00:00", + "status": "empty", + "policy": { + "instance_parameters_allowed": false, + "absolute_coordinates_allowed": false, + "single_case_promotion_allowed": false, + "minimum_support": 20, + "minimum_confidence": 0.8 + }, + "corpus_summary": { + "accepted_case_count": 0, + "duplicate_case_count": 0, + "rejected_case_count": 0, + "family_count": 0 + }, + "experience_summary": { + "promoted_experience_count": 0, + "candidate_experience_count": 0, + "by_kind": [], + "by_scope": [], + "candidate_by_kind": [] + }, + "experiences": [], + "candidate_experiences": [] +} diff --git a/cad-experience-plugin/benchmarks/ab50/evaluate.py b/cad-experience-plugin/benchmarks/ab50/evaluate.py new file mode 100644 index 0000000..b731d49 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/evaluate.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python3 +"""Evaluate blind baseline/experience STEP reconstructions against held-out STEP.""" +from __future__ import annotations + +import argparse +import csv +import importlib.util +import itertools +import json +import math +from pathlib import Path +from typing import Any + +import numpy as np +from build123d import import_step +from scipy.spatial import cKDTree + +HERE = Path(__file__).resolve().parent +EXTRACTOR = ( + HERE.parents[1] + / "skills/cad-experience-builder/scripts/step_to_case.py" +) + + +def load_extractor(): + spec = importlib.util.spec_from_file_location("ab50_step_to_case", EXTRACTOR) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +EXTRACT = load_extractor() + + +def rel_error(actual: float, expected: float) -> float: + return abs(actual - expected) / max(abs(expected), 1e-9) + + +def feature_names(payload: dict[str, Any]) -> set[str]: + result: set[str] = set() + for item in payload.get("design_ir", {}).get("features", []): + if isinstance(item, str): + result.add(item) + elif isinstance(item, dict): + name = item.get("kind") or item.get("type") or item.get("name") + if name: + result.add(str(name)) + return result + + +def f1_score(left: set[str], right: set[str]) -> float: + if not left and not right: + return 1.0 + if not left or not right: + return 0.0 + overlap = len(left & right) + precision = overlap / len(right) + recall = overlap / len(left) + return 2 * precision * recall / max(precision + recall, 1e-9) + + +def mesh(shape, tolerance: float = 0.35) -> tuple[np.ndarray, np.ndarray]: + vertices, triangles = shape.tessellate(tolerance) + points = np.asarray([[v.X, v.Y, v.Z] for v in vertices], dtype=float) + faces = np.asarray(triangles, dtype=int) + return points, faces + + +def sample_surface( + vertices: np.ndarray, faces: np.ndarray, count: int, seed: int +) -> np.ndarray: + tri = vertices[faces] + cross = np.cross(tri[:, 1] - tri[:, 0], tri[:, 2] - tri[:, 0]) + areas = np.linalg.norm(cross, axis=1) / 2.0 + total = float(areas.sum()) + if total <= 1e-12: + return vertices.copy() + rng = np.random.default_rng(seed) + chosen = rng.choice(len(faces), size=count, p=areas / total) + selected = tri[chosen] + u = rng.random(count) + v = rng.random(count) + reflected = u + v > 1.0 + u[reflected] = 1.0 - u[reflected] + v[reflected] = 1.0 - v[reflected] + return ( + selected[:, 0] + + u[:, None] * (selected[:, 1] - selected[:, 0]) + + v[:, None] * (selected[:, 2] - selected[:, 0]) + ) + + +def cube_transforms() -> list[np.ndarray]: + transforms = [] + for permutation in itertools.permutations(range(3)): + base = np.eye(3)[:, permutation] + for signs in itertools.product((-1.0, 1.0), repeat=3): + transforms.append(base @ np.diag(signs)) + return transforms + + +TRANSFORMS = cube_transforms() + + +def surface_distance( + reference, generated, seed: int, samples: int +) -> tuple[float, float]: + ref_v, ref_f = mesh(reference) + gen_v, gen_f = mesh(generated) + ref = sample_surface(ref_v, ref_f, samples, seed) + gen = sample_surface(gen_v, gen_f, samples, seed + 1) + ref -= (ref.min(axis=0) + ref.max(axis=0)) / 2.0 + gen -= (gen.min(axis=0) + gen.max(axis=0)) / 2.0 + ref_tree = cKDTree(ref) + best_chamfer = math.inf + best_hausdorff = math.inf + for transform in TRANSFORMS: + candidate = gen @ transform + gen_tree = cKDTree(candidate) + d_gr = ref_tree.query(candidate, workers=-1)[0] + d_rg = gen_tree.query(ref, workers=-1)[0] + chamfer = float((d_gr.mean() + d_rg.mean()) / 2.0) + hausdorff = float(max(np.quantile(d_gr, 0.95), np.quantile(d_rg, 0.95))) + if chamfer < best_chamfer: + best_chamfer = chamfer + best_hausdorff = hausdorff + diagonal = max(float(np.linalg.norm(np.ptp(ref, axis=0))), 1e-9) + return best_chamfer / diagonal, best_hausdorff / diagonal + + +def task_experience(step_path: Path) -> tuple[int, int]: + task = step_path.parent / "cad-task.json" + if not task.is_file(): + return 0, 0 + payload = json.loads(task.read_text()) + exp = payload.get("experience", {}) + returned = exp.get("methods_returned") + if returned is None: + returned = len(exp.get("methods", exp.get("methods_applied", []))) + if isinstance(returned, list): + returned = len(returned) + return int(returned or 0), len(exp.get("methods_applied", [])) + + +def evaluate_one( + case: dict[str, Any], variant: str, samples: int +) -> dict[str, Any]: + generated_path = Path(case[f"{variant}_step"]) + row: dict[str, Any] = { + "id": case["id"], + "family": case["family"], + "variant": variant, + "generated": generated_path.is_file(), + } + if not generated_path.is_file(): + row["error"] = "missing STEP" + return row + try: + reference = import_step(case["reference_step"]) + generated = import_step(str(generated_path)) + ref_case = json.loads(Path(case["case_json"]).read_text()) + gen_case = EXTRACT.extract_step_case(generated_path) + ref_bbox = sorted(float(v) for v in reference.bounding_box().size) + gen_bbox = sorted(float(v) for v in generated.bounding_box().size) + bbox_error = float( + np.mean([rel_error(a, b) for a, b in zip(gen_bbox, ref_bbox)]) + ) + volume_error = rel_error(float(generated.volume), float(reference.volume)) + area_error = rel_error(float(generated.area), float(reference.area)) + face_error = rel_error(len(generated.faces()), len(reference.faces())) + edge_error = rel_error(len(generated.edges()), len(reference.edges())) + chamfer, hausdorff95 = surface_distance( + reference, generated, int(case["id"]), samples + ) + feature_f1 = f1_score(feature_names(ref_case), feature_names(gen_case)) + family_match = float( + ref_case["design_ir"]["part_family"] + == gen_case["design_ir"]["part_family"] + ) + returned, applied = task_experience(generated_path) + + # Transparent 0–100 similarity index. It is a benchmark summary, not a + # claim of manufacturing interchangeability or exact B-rep identity. + score = 100.0 * ( + 0.18 * math.exp(-volume_error) + + 0.12 * math.exp(-area_error) + + 0.16 * math.exp(-bbox_error) + + 0.24 * math.exp(-8.0 * chamfer) + + 0.10 * math.exp(-(face_error + edge_error) / 2.0) + + 0.15 * feature_f1 + + 0.05 * family_match + ) + row.update( + { + "valid": bool(generated.is_valid), + "solid_count": len(generated.solids()), + "reference_volume": float(reference.volume), + "generated_volume": float(generated.volume), + "volume_rel_error": volume_error, + "surface_area_rel_error": area_error, + "bbox_mape": bbox_error, + "face_rel_error": face_error, + "edge_rel_error": edge_error, + "surface_chamfer_norm": chamfer, + "surface_hausdorff95_norm": hausdorff95, + "feature_f1": feature_f1, + "family_match": family_match, + "experience_methods_returned": returned, + "experience_methods_applied": applied, + "similarity_score": score, + } + ) + except Exception as exc: + row["error"] = f"{type(exc).__name__}: {exc}" + return row + + +def mean(rows: list[dict[str, Any]], key: str) -> float: + values = [float(row[key]) for row in rows if key in row] + return sum(values) / len(values) if values else math.nan + + +def write_report(rows: list[dict[str, Any]]) -> None: + complete = [row for row in rows if "similarity_score" in row] + baseline = [row for row in complete if row["variant"] == "baseline"] + experience = [row for row in complete if row["variant"] == "experience"] + by_id = {} + for row in complete: + by_id.setdefault(row["id"], {})[row["variant"]] = row + pairs = [ + item for item in by_id.values() if {"baseline", "experience"} <= item.keys() + ] + paired_baseline = [item["baseline"] for item in pairs] + paired_experience = [item["experience"] for item in pairs] + deltas = [ + item["experience"]["similarity_score"] + - item["baseline"]["similarity_score"] + for item in pairs + ] + wins = sum(delta > 0.25 for delta in deltas) + ties = sum(abs(delta) <= 0.25 for delta in deltas) + losses = sum(delta < -0.25 for delta in deltas) + methods_returned = sum( + row.get("experience_methods_returned", 0) for row in experience + ) + methods_applied = sum( + row.get("experience_methods_applied", 0) for row in experience + ) + report = f"""# CAD 经验库 50 件 A/B 盲重建报告 + +## 口径 + +- 测试集:固定分层抽取 50 件;这 50 件不进入经验库。 +- 训练经验:其余 950 件生成的通用经验库。 +- 两组输入完全相同:仅多视图图片和文字描述;生成过程禁止读取参考 STEP 和解析 JSON。 +- Baseline:空经验库;Experience:950 件经验库。 +- 参考 STEP 只在生成完成后用于评分。 +- “相似度”是体积、面积、包围盒、表面距离、拓扑和语义特征的综合诊断分,不代表制造级 1:1 或可互换性。 + +## 当前完成度 + +- 有效 Baseline:{len(baseline)}/50 +- 有效 Experience:{len(experience)}/50 +- 完整配对:{len(pairs)}/50 +- 经验方法返回/实际采用:{methods_returned}/{methods_applied} + +## 汇总 + +| 指标 | 无经验 | 有经验 | 差值 | +|---|---:|---:|---:| +| 综合相似度(0–100,越高越好) | {mean(paired_baseline, "similarity_score"):.2f} | {mean(paired_experience, "similarity_score"):.2f} | {mean(paired_experience, "similarity_score") - mean(paired_baseline, "similarity_score"):+.2f} | +| 体积相对误差(越低越好) | {mean(paired_baseline, "volume_rel_error"):.3f} | {mean(paired_experience, "volume_rel_error"):.3f} | {mean(paired_experience, "volume_rel_error") - mean(paired_baseline, "volume_rel_error"):+.3f} | +| 表面积相对误差 | {mean(paired_baseline, "surface_area_rel_error"):.3f} | {mean(paired_experience, "surface_area_rel_error"):.3f} | {mean(paired_experience, "surface_area_rel_error") - mean(paired_baseline, "surface_area_rel_error"):+.3f} | +| 包围盒尺寸 MAPE | {mean(paired_baseline, "bbox_mape"):.3f} | {mean(paired_experience, "bbox_mape"):.3f} | {mean(paired_experience, "bbox_mape") - mean(paired_baseline, "bbox_mape"):+.3f} | +| 归一化表面 Chamfer | {mean(paired_baseline, "surface_chamfer_norm"):.4f} | {mean(paired_experience, "surface_chamfer_norm"):.4f} | {mean(paired_experience, "surface_chamfer_norm") - mean(paired_baseline, "surface_chamfer_norm"):+.4f} | +| 特征 F1 | {mean(paired_baseline, "feature_f1"):.3f} | {mean(paired_experience, "feature_f1"):.3f} | {mean(paired_experience, "feature_f1") - mean(paired_baseline, "feature_f1"):+.3f} | + +经验组胜/平/负(±0.25 分视为平):**{wins}/{ties}/{losses}**。 +""" + (HERE / "report.md").write_text(report) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--samples", type=int, default=1600) + args = parser.parse_args() + manifest = json.loads((HERE / "manifest.json").read_text()) + rows = [ + evaluate_one(case, variant, args.samples) + for case in manifest["cases"] + for variant in ("baseline", "experience") + ] + (HERE / "metrics.json").write_text( + json.dumps(rows, indent=2, ensure_ascii=False) + "\n" + ) + keys = sorted({key for row in rows for key in row}) + with (HERE / "metrics.csv").open("w", newline="") as stream: + writer = csv.DictWriter(stream, fieldnames=keys) + writer.writeheader() + writer.writerows(rows) + write_report(rows) + completed = sum("similarity_score" in row for row in rows) + print(json.dumps({"evaluated": completed, "expected": 100})) + + +if __name__ == "__main__": + main() diff --git a/cad-experience-plugin/benchmarks/ab50/manifest.json b/cad-experience-plugin/benchmarks/ab50/manifest.json new file mode 100644 index 0000000..aff4ccb --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/manifest.json @@ -0,0 +1,708 @@ +{ + "schema_version": "1.0", + "selection_seed": 20260723, + "holdout_count": 50, + "train_count": 950, + "cases": [ + { + "id": "000166", + "family": "general_mechanical_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/000166.3fe709d953c5.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/000166.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/000166.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/000166.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/000166/000166.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/000166/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/000166/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/000166/000166.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/000166/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/000166/final.txt" + }, + { + "id": "004764", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/004764.6e3a06d88adc.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/004764.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/004764.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/004764.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/004764/004764.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/004764/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/004764/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/004764/004764.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/004764/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/004764/final.txt" + }, + { + "id": "011680", + "family": "plate_or_bracket", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/011680.77a85afd0f4b.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/011680.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/011680.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/011680.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/011680/011680.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/011680/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/011680/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/011680/011680.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/011680/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/011680/final.txt" + }, + { + "id": "016033", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/016033.500e53f944a6.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/016033.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/016033.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/016033.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/016033/016033.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/016033/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/016033/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/016033/016033.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/016033/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/016033/final.txt" + }, + { + "id": "020543", + "family": "flanged_rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/020543.0ad8f7e7459d.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/020543.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/020543.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/020543.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/020543/020543.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/020543/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/020543/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/020543/020543.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/020543/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/020543/final.txt" + }, + { + "id": "024675", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/024675.2d1f84f175f5.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/024675.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/024675.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/024675.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/024675/024675.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/024675/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/024675/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/024675/024675.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/024675/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/024675/final.txt" + }, + { + "id": "029190", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/029190.825d4001dea3.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/029190.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/029190.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/029190.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/029190/029190.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/029190/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/029190/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/029190/029190.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/029190/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/029190/final.txt" + }, + { + "id": "030352", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/030352.45db9222299d.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/030352.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/030352.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/030352.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/030352/030352.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/030352/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/030352/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/030352/030352.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/030352/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/030352/final.txt" + }, + { + "id": "046014", + "family": "flanged_rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/046014.4abae0d1a5a1.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/046014.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/046014.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/046014.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/046014/046014.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/046014/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/046014/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/046014/046014.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/046014/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/046014/final.txt" + }, + { + "id": "047048", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/047048.b5119254bc4b.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/047048.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/047048.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/047048.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/047048/047048.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/047048/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/047048/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/047048/047048.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/047048/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/047048/final.txt" + }, + { + "id": "048346", + "family": "hollow_or_stepped_shaft", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/048346.ba3e1b245d0e.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/048346.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/048346.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/048346.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/048346/048346.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/048346/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/048346/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/048346/048346.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/048346/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/048346/final.txt" + }, + { + "id": "053664", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/053664.90a0635aadaf.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/053664.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/053664.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/053664.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/053664/053664.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/053664/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/053664/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/053664/053664.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/053664/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/053664/final.txt" + }, + { + "id": "063487", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/063487.b524dcfc1124.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/063487.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/063487.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/063487.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/063487/063487.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/063487/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/063487/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/063487/063487.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/063487/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/063487/final.txt" + }, + { + "id": "065487", + "family": "flanged_rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/065487.444ad775ec5c.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/065487.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/065487.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/065487.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/065487/065487.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/065487/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/065487/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/065487/065487.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/065487/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/065487/final.txt" + }, + { + "id": "067186", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/067186.0fe747589ef9.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/067186.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/067186.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/067186.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/067186/067186.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/067186/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/067186/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/067186/067186.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/067186/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/067186/final.txt" + }, + { + "id": "073706", + "family": "rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/073706.9727acff0f04.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/073706.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/073706.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/073706.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/073706/073706.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/073706/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/073706/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/073706/073706.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/073706/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/073706/final.txt" + }, + { + "id": "077955", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/077955.499f373b1c19.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/077955.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/077955.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/077955.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/077955/077955.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/077955/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/077955/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/077955/077955.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/077955/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/077955/final.txt" + }, + { + "id": "082187", + "family": "prismatic_block", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/082187.2cb2db4976cc.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/082187.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/082187.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/082187.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/082187/082187.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/082187/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/082187/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/082187/082187.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/082187/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/082187/final.txt" + }, + { + "id": "089970", + "family": "rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/089970.fcac7c13cac8.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/089970.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/089970.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/089970.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/089970/089970.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/089970/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/089970/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/089970/089970.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/089970/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/089970/final.txt" + }, + { + "id": "092092", + "family": "shaft", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/092092.878532374151.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/092092.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/092092.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/092092.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/092092/092092.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/092092/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/092092/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/092092/092092.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/092092/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/092092/final.txt" + }, + { + "id": "095520", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/095520.f77225b8bb80.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/095520.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/095520.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/095520.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/095520/095520.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/095520/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/095520/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/095520/095520.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/095520/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/095520/final.txt" + }, + { + "id": "102128", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/102128.5226541130cc.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/102128.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/102128.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/102128.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/102128/102128.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/102128/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/102128/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/102128/102128.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/102128/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/102128/final.txt" + }, + { + "id": "104465", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/104465.367ecc4c0d1f.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/104465.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/104465.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/104465.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/104465/104465.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/104465/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/104465/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/104465/104465.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/104465/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/104465/final.txt" + }, + { + "id": "110143", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/110143.d0705ec5cf9b.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/110143.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/110143.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/110143.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/110143/110143.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/110143/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/110143/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/110143/110143.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/110143/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/110143/final.txt" + }, + { + "id": "112069", + "family": "prismatic_block", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/112069.527e20a67202.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/112069.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/112069.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/112069.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/112069/112069.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/112069/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/112069/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/112069/112069.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/112069/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/112069/final.txt" + }, + { + "id": "116166", + "family": "rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/116166.daa7c0fb7095.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/116166.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/116166.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/116166.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/116166/116166.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/116166/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/116166/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/116166/116166.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/116166/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/116166/final.txt" + }, + { + "id": "134828", + "family": "multi_axis_manifold", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/134828.4b43ff12062c.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/134828.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/134828.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/134828.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/134828/134828.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/134828/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/134828/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/134828/134828.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/134828/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/134828/final.txt" + }, + { + "id": "136930", + "family": "rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/136930.84481239875b.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/136930.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/136930.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/136930.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/136930/136930.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/136930/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/136930/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/136930/136930.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/136930/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/136930/final.txt" + }, + { + "id": "138022", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/138022.144778123756.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/138022.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/138022.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/138022.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/138022/138022.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/138022/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/138022/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/138022/138022.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/138022/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/138022/final.txt" + }, + { + "id": "141680", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/141680.6f110692a1c2.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/141680.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/141680.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/141680.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/141680/141680.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/141680/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/141680/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/141680/141680.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/141680/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/141680/final.txt" + }, + { + "id": "145574", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/145574.861af237e8e8.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/145574.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/145574.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/145574.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/145574/145574.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/145574/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/145574/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/145574/145574.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/145574/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/145574/final.txt" + }, + { + "id": "150196", + "family": "prismatic_block", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/150196.d357a5b35875.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/150196.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/150196.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/150196.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/150196/150196.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/150196/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/150196/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/150196/150196.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/150196/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/150196/final.txt" + }, + { + "id": "150587", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/150587.ff4fa06ecb48.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/150587.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/150587.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/150587.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/150587/150587.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/150587/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/150587/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/150587/150587.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/150587/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/150587/final.txt" + }, + { + "id": "152568", + "family": "multi_axis_manifold", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/152568.92e33e9a9248.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/152568.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/152568.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/152568.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/152568/152568.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/152568/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/152568/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/152568/152568.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/152568/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/152568/final.txt" + }, + { + "id": "153245", + "family": "plate_or_bracket", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/153245.0603ada945fa.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/153245.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/153245.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/153245.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/153245/153245.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/153245/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/153245/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/153245/153245.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/153245/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/153245/final.txt" + }, + { + "id": "158256", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/158256.a27578bb9a47.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/158256.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/158256.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/158256.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/158256/158256.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/158256/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/158256/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/158256/158256.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/158256/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/158256/final.txt" + }, + { + "id": "162558", + "family": "hollow_or_stepped_shaft", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/162558.27dadf557afe.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/162558.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/162558.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/162558.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/162558/162558.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/162558/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/162558/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/162558/162558.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/162558/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/162558/final.txt" + }, + { + "id": "167962", + "family": "multi_axis_manifold", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/167962.1306cad65795.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/167962.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/167962.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/167962.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/167962/167962.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/167962/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/167962/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/167962/167962.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/167962/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/167962/final.txt" + }, + { + "id": "168239", + "family": "rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/168239.b54b54a2287f.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/168239.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/168239.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/168239.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/168239/168239.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/168239/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/168239/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/168239/168239.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/168239/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/168239/final.txt" + }, + { + "id": "174675", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/174675.25863f859f2b.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/174675.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/174675.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/174675.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/174675/174675.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/174675/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/174675/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/174675/174675.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/174675/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/174675/final.txt" + }, + { + "id": "183151", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/183151.ceee2bf361bc.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/183151.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/183151.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/183151.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/183151/183151.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/183151/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/183151/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/183151/183151.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/183151/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/183151/final.txt" + }, + { + "id": "189669", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/189669.66b0db2ea3d6.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/189669.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/189669.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/189669.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/189669/189669.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/189669/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/189669/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/189669/189669.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/189669/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/189669/final.txt" + }, + { + "id": "194029", + "family": "rotational_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/194029.316b264a1a88.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/194029.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/194029.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/194029.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/194029/194029.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/194029/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/194029/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/194029/194029.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/194029/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/194029/final.txt" + }, + { + "id": "201547", + "family": "prismatic_block", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/201547.c7cc9318deda.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/201547.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/201547.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/201547.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/201547/201547.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/201547/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/201547/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/201547/201547.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/201547/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/201547/final.txt" + }, + { + "id": "204730", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/204730.1fade6b3bb74.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/204730.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/204730.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/204730.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/204730/204730.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/204730/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/204730/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/204730/204730.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/204730/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/204730/final.txt" + }, + { + "id": "211326", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/211326.a4e6d3a4590a.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/211326.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/211326.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/211326.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/211326/211326.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/211326/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/211326/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/211326/211326.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/211326/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/211326/final.txt" + }, + { + "id": "214404", + "family": "patterned_plate", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/214404.da6a44a7013c.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/214404.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/214404.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/214404.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/214404/214404.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/214404/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/214404/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/214404/214404.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/214404/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/214404/final.txt" + }, + { + "id": "229115", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/229115.c4c1680e53b5.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/229115.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/229115.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/229115.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/229115/229115.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/229115/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/229115/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/229115/229115.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/229115/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/229115/final.txt" + }, + { + "id": "234097", + "family": "hybrid_freeform_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/234097.cea424081999.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/234097.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/234097.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/234097.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/234097/234097.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/234097/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/234097/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/234097/234097.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/234097/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/234097/final.txt" + }, + { + "id": "242307", + "family": "patterned_prismatic_part", + "case_json": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/parser/output/242307.cd1b3d043967.case.json", + "image": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/image/242307.png", + "description": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/Des_Text/242307.txt", + "reference_step": "/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset/model_step/242307.step", + "baseline_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/242307/242307.step", + "baseline_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/242307/codex.jsonl", + "baseline_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/baseline/242307/final.txt", + "experience_step": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/242307/242307.step", + "experience_log": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/242307/codex.jsonl", + "experience_message": "/Users/jerry/linkhand/CadSet/cad-experience-plugin/benchmarks/ab50/generated/experience/242307/final.txt" + } + ] +} diff --git a/cad-experience-plugin/benchmarks/ab50/metrics.csv b/cad-experience-plugin/benchmarks/ab50/metrics.csv new file mode 100644 index 0000000..03f269b --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/metrics.csv @@ -0,0 +1,101 @@ +bbox_mape,edge_rel_error,error,experience_methods_applied,experience_methods_returned,face_rel_error,family,family_match,feature_f1,generated,generated_volume,id,reference_volume,similarity_score,solid_count,surface_area_rel_error,surface_chamfer_norm,surface_hausdorff95_norm,valid,variant,volume_rel_error +0.0,0.5,,0,0,0.25,general_mechanical_part,1.0,0.8571428571428571,True,125663.70614359173,000166,125663.70614359173,93.81711632834559,1,6.425513440379324e-14,0.004847580881730266,0.00977766538694562,True,baseline,0.0 +0.0,0.5,,0,0,0.25,general_mechanical_part,1.0,0.8571428571428571,True,125663.70614359173,000166,125663.70614359173,93.81711632834559,1,6.425513440379324e-14,0.004847580881730266,0.00977766538694562,True,experience,0.0 +0.45066264681866697,0.125,,0,0,0.1111111111111111,patterned_plate,0.0,0.8571428571428571,True,106129.48703937844,004764,36749.40939347248,54.33835930595081,1,1.0309687869495758,0.05550481239248205,0.1775660613682025,True,baseline,1.8879236099568286 +0.3413730293323282,0.125,,3,18,0.1111111111111111,patterned_plate,0.0,0.8571428571428571,True,88774.08881569223,004764,36749.40939347248,59.488074232257325,1,0.7876989178243013,0.0465090322155671,0.15562347480527094,True,experience,1.4156602862700836 +0.5696649029982362,0.0,,0,0,0.0,plate_or_bracket,1.0,1.0,True,19016.0,011680,898496.0,60.445468751589296,1,0.9780420050604243,0.10796723414266624,0.36481967349123434,True,baseline,0.9788357432865589 +0.583774250440917,0.0,,4,10,0.0,plate_or_bracket,1.0,1.0,True,13547.999999999998,011680,898496.0,59.90070139134242,1,0.9842171382062035,0.11235674854244815,0.374964117147598,True,experience,0.9849214687655816 +0.18571427761876255,0.2631578947368421,,0,0,0.26666666666666666,patterned_plate,1.0,1.0,True,23882.59512641282,016033,44618.37587532573,81.50778192907364,1,0.34116223818430896,0.018452946285241585,0.08713798240651745,True,baseline,0.4647363410728704 +0.3238095238095235,0.02631578947368421,,3,10,0.06666666666666667,patterned_plate,1.0,0.9411764705882353,True,13866.369040708962,016033,44618.37587532573,75.91174289423121,1,0.5000404499582691,0.026851094204940862,0.12169100582216402,True,experience,0.6892229094251465 +0.5959595959595959,0.2857142857142857,,0,0,0.28125,flanged_rotational_part,1.0,1.0,True,118756.98485692723,020543,24145.808587596228,51.56160371886595,1,1.5105685440071281,0.08451218347517844,0.22747541674825858,True,baseline,3.9183271053483395 +0.23232323232323235,0.05194805194805195,,0,0,0.1875,flanged_rotational_part,1.0,0.888888888888889,True,55638.90609255368,020543,24145.808587596228,69.33575293745653,1,0.4640684949891876,0.042969081471212464,0.11104259063963073,True,experience,1.3042883774509648 +0.06666666666666685,0.2,,0,0,0.19480519480519481,patterned_prismatic_part,1.0,1.0,True,569438.4483554161,024675,580617.6091315141,91.92371160771847,1,0.2739018704584683,0.011073315847120254,0.07352156562273186,True,baseline,0.019253912730652068 +0.5777777777777777,0.8933333333333333,,0,0,1.4025974025974026,patterned_prismatic_part,1.0,0.9333333333333333,True,38876.03946418978,024675,580617.6091315141,56.84188202691976,1,0.8746474563374864,0.07094038930770122,0.33837892456299423,True,experience,0.9330436437807312 +0.0980952380952385,0.16666666666666666,,0,0,0.14285714285714285,patterned_plate,1.0,0.9333333333333333,True,73807.58425433026,029190,131254.75510206894,83.74861954252185,1,0.1560183040026604,0.024101504873521428,0.04232654923555514,True,baseline,0.4376768735202429 +0.14047619047618823,1.5277777777777777,,0,0,1.6428571428571428,patterned_plate,1.0,0.7999999999999999,True,71846.37021205797,029190,131254.75510206894,73.91053356207678,1,0.20263501345527155,0.02460078307658505,0.04143094591291666,True,experience,0.45261891535901033 +0.31241830065359194,0.9285714285714286,,0,0,0.5625,patterned_plate,1.0,0.923076923076923,True,34352.594678913054,030352,60881.15044407846,73.94372815488485,1,0.48742381144539443,0.02510282770499014,0.10902462973941958,True,baseline,0.4357433388111292 +0.4306604575163398,2.642857142857143,,3,10,2.0625,patterned_plate,1.0,0.7692307692307692,True,11173.345159683424,030352,60881.15044407846,59.45331217188532,1,0.6732862799472543,0.03957952772551367,0.16850566658178062,True,experience,0.8164728314398964 +0.21212121212121218,0.5,,0,0,0.43243243243243246,flanged_rotational_part,1.0,0.9600000000000001,True,73578.89519968569,046014,162271.3445381513,75.92217396437314,1,0.333964798541362,0.033942930350360764,0.05126001418573268,True,baseline,0.5465687709120651 +0.13636363636363644,0.5,,4,10,0.43243243243243246,flanged_rotational_part,1.0,0.9600000000000001,True,111532.3028776836,046014,162271.3445381513,81.61765078194381,1,0.24200901291799348,0.02662187229047825,0.043202965297600246,True,experience,0.31268023202050027 +0.6851851851851847,0.3709677419354839,,0,0,0.34782608695652173,patterned_prismatic_part,1.0,0.823529411764706,True,167251.53322574112,047048,33461.296317388085,44.79000279500974,1,1.6260155362793598,0.11322328439048983,0.2789388077989153,True,baseline,3.9983578531842254 +0.7175925925925921,0.1693548387096774,,3,18,0.2391304347826087,patterned_prismatic_part,1.0,0.823529411764706,True,169277.67589913897,047048,33461.296317388085,44.38628038601891,1,1.587008333438104,0.13259494837120114,0.321530970786483,True,experience,4.058909681606514 +1.4575235605335388e-16,0.5,,0,0,0.3125,hollow_or_stepped_shaft,1.0,0.9166666666666666,True,8278002.394429736,048346,8341713.8934443,93.45850460439323,1,0.01545893719801078,0.008802226092164953,0.016975575422477428,True,baseline,0.007637698898380382 +1.4575235605335388e-16,0.5,,0,0,0.3125,hollow_or_stepped_shaft,1.0,0.9166666666666666,True,8292610.800268921,048346,8341713.8934443,93.51807327218812,1,0.012238325281746575,0.008857323306102671,0.017730676062993916,True,experience,0.005886451369899948 +0.02857142857142833,0.2222222222222222,,0,0,0.21212121212121213,patterned_plate,0.0,0.8571428571428571,True,315117.2806010815,053664,349435.5647059466,85.57701767939263,1,0.07576465816363272,0.012696566244914555,0.02434484580541925,True,baseline,0.0982106218459597 +0.07936507936507906,0.0,,0,0,0.030303030303030304,patterned_plate,0.0,0.8571428571428571,True,282776.14120936824,053664,349435.5647059466,83.57268406993335,1,0.167398697885095,0.016320456106002173,0.05162627100789126,True,experience,0.19076313412080106 +,,missing STEP,,,,patterned_prismatic_part,,,False,,063487,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,063487,,,,,,,,experience, +0.05572210294349599,0.420863309352518,,0,0,0.4396551724137931,flanged_rotational_part,1.0,0.923076923076923,True,68680.44493144708,065487,81354.16556588435,87.21779338748289,1,0.060356551334099576,0.022572421105818447,0.053770414665160235,True,baseline,0.15578453231351141 +,,missing STEP,,,,flanged_rotational_part,,,False,,065487,,,,,,,,experience, +,,missing STEP,,,,patterned_plate,,,False,,067186,,,,,,,,baseline, +,,missing STEP,,,,patterned_plate,,,False,,067186,,,,,,,,experience, +,,missing STEP,,,,rotational_part,,,False,,073706,,,,,,,,baseline, +,,missing STEP,,,,rotational_part,,,False,,073706,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,077955,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,077955,,,,,,,,experience, +,,missing STEP,,,,prismatic_block,,,False,,082187,,,,,,,,baseline, +,,missing STEP,,,,prismatic_block,,,False,,082187,,,,,,,,experience, +,,missing STEP,,,,rotational_part,,,False,,089970,,,,,,,,baseline, +,,missing STEP,,,,rotational_part,,,False,,089970,,,,,,,,experience, +,,missing STEP,,,,shaft,,,False,,092092,,,,,,,,baseline, +,,missing STEP,,,,shaft,,,False,,092092,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,095520,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,095520,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,102128,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,102128,,,,,,,,experience, +,,missing STEP,,,,patterned_plate,,,False,,104465,,,,,,,,baseline, +,,missing STEP,,,,patterned_plate,,,False,,104465,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,110143,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,110143,,,,,,,,experience, +,,missing STEP,,,,prismatic_block,,,False,,112069,,,,,,,,baseline, +,,missing STEP,,,,prismatic_block,,,False,,112069,,,,,,,,experience, +0.22361111004444456,0.6538461538461539,,0,0,0.6153846153846154,rotational_part,1.0,0.761904761904762,True,67519.74039233122,116166,147536.10631008807,71.3680012563105,1,0.38925591734814796,0.03424668647245051,0.07518718288900171,True,baseline,0.5423510754010293 +0.18888888915554566,0.7307692307692307,,3,16,0.6923076923076923,rotational_part,1.0,0.8571428571428572,True,69565.58402815007,116166,147536.10631008807,73.4520592182128,1,0.365981954180488,0.032489227662340435,0.07289222461786297,True,experience,0.528484343473599 +0.6746686351889055,0.45217391304347826,,0,0,0.45,multi_axis_manifold,0.0,0.7692307692307692,True,34234.94531047202,134828,349556.2389887021,48.526262227273214,1,0.9239731944207157,0.10449739414590258,0.3144594900968871,True,baseline,0.9020616956815967 +0.7062506154470962,0.5043478260869565,,5,14,0.5,multi_axis_manifold,0.0,0.7692307692307692,True,38190.323227729335,134828,349556.2389887021,48.60371993717729,1,0.9152269986233839,0.09836507300701838,0.29918959165673675,True,experience,0.8907462692177447 +7.3809523809523805,0.5,,0,0,0.3333333333333333,rotational_part,1.0,0.9411764705882353,True,25446.900494077883,136930,38.877209088173686,25.720060532485707,1,72.6363636363658,1.666072751413829,2.6857505921882785,True,baseline,653.545454545469 +6.936507936507937,0.5,,0,0,0.3333333333333333,rotational_part,1.0,0.9411764705882353,True,21205.750411731566,136930,38.877209088173686,25.725642575854934,1,67.18181818181998,1.6549008981976585,2.6698915132197145,True,experience,544.4545454545574 +,,missing STEP,,,,patterned_prismatic_part,,,False,,138022,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,138022,,,,,,,,experience, +,,missing STEP,,,,patterned_plate,,,False,,141680,,,,,,,,baseline, +,,missing STEP,,,,patterned_plate,,,False,,141680,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,145574,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,145574,,,,,,,,experience, +,,missing STEP,,,,prismatic_block,,,False,,150196,,,,,,,,baseline, +,,missing STEP,,,,prismatic_block,,,False,,150196,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,150587,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,150587,,,,,,,,experience, +,,missing STEP,,,,multi_axis_manifold,,,False,,152568,,,,,,,,baseline, +,,missing STEP,,,,multi_axis_manifold,,,False,,152568,,,,,,,,experience, +,,missing STEP,,,,plate_or_bracket,,,False,,153245,,,,,,,,baseline, +,,missing STEP,,,,plate_or_bracket,,,False,,153245,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,158256,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,158256,,,,,,,,experience, +,,missing STEP,,,,hollow_or_stepped_shaft,,,False,,162558,,,,,,,,baseline, +,,missing STEP,,,,hollow_or_stepped_shaft,,,False,,162558,,,,,,,,experience, +,,missing STEP,,,,multi_axis_manifold,,,False,,167962,,,,,,,,baseline, +,,missing STEP,,,,multi_axis_manifold,,,False,,167962,,,,,,,,experience, +,,missing STEP,,,,rotational_part,,,False,,168239,,,,,,,,baseline, +,,missing STEP,,,,rotational_part,,,False,,168239,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,174675,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,174675,,,,,,,,experience, +,,missing STEP,,,,patterned_plate,,,False,,183151,,,,,,,,baseline, +,,missing STEP,,,,patterned_plate,,,False,,183151,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,189669,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,189669,,,,,,,,experience, +,,missing STEP,,,,rotational_part,,,False,,194029,,,,,,,,baseline, +,,missing STEP,,,,rotational_part,,,False,,194029,,,,,,,,experience, +,,missing STEP,,,,prismatic_block,,,False,,201547,,,,,,,,baseline, +,,missing STEP,,,,prismatic_block,,,False,,201547,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,204730,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,204730,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,211326,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,211326,,,,,,,,experience, +,,missing STEP,,,,patterned_plate,,,False,,214404,,,,,,,,baseline, +,,missing STEP,,,,patterned_plate,,,False,,214404,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,229115,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,229115,,,,,,,,experience, +,,missing STEP,,,,hybrid_freeform_part,,,False,,234097,,,,,,,,baseline, +,,missing STEP,,,,hybrid_freeform_part,,,False,,234097,,,,,,,,experience, +,,missing STEP,,,,patterned_prismatic_part,,,False,,242307,,,,,,,,baseline, +,,missing STEP,,,,patterned_prismatic_part,,,False,,242307,,,,,,,,experience, diff --git a/cad-experience-plugin/benchmarks/ab50/metrics.json b/cad-experience-plugin/benchmarks/ab50/metrics.json new file mode 100644 index 0000000..67fa2f9 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/metrics.json @@ -0,0 +1,1167 @@ +[ + { + "id": "000166", + "family": "general_mechanical_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 125663.70614359173, + "generated_volume": 125663.70614359173, + "volume_rel_error": 0.0, + "surface_area_rel_error": 6.425513440379324e-14, + "bbox_mape": 0.0, + "face_rel_error": 0.25, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 0.004847580881730266, + "surface_hausdorff95_norm": 0.00977766538694562, + "feature_f1": 0.8571428571428571, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 93.81711632834559 + }, + { + "id": "000166", + "family": "general_mechanical_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 125663.70614359173, + "generated_volume": 125663.70614359173, + "volume_rel_error": 0.0, + "surface_area_rel_error": 6.425513440379324e-14, + "bbox_mape": 0.0, + "face_rel_error": 0.25, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 0.004847580881730266, + "surface_hausdorff95_norm": 0.00977766538694562, + "feature_f1": 0.8571428571428571, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 93.81711632834559 + }, + { + "id": "004764", + "family": "patterned_plate", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 36749.40939347248, + "generated_volume": 106129.48703937844, + "volume_rel_error": 1.8879236099568286, + "surface_area_rel_error": 1.0309687869495758, + "bbox_mape": 0.45066264681866697, + "face_rel_error": 0.1111111111111111, + "edge_rel_error": 0.125, + "surface_chamfer_norm": 0.05550481239248205, + "surface_hausdorff95_norm": 0.1775660613682025, + "feature_f1": 0.8571428571428571, + "family_match": 0.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 54.33835930595081 + }, + { + "id": "004764", + "family": "patterned_plate", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 36749.40939347248, + "generated_volume": 88774.08881569223, + "volume_rel_error": 1.4156602862700836, + "surface_area_rel_error": 0.7876989178243013, + "bbox_mape": 0.3413730293323282, + "face_rel_error": 0.1111111111111111, + "edge_rel_error": 0.125, + "surface_chamfer_norm": 0.0465090322155671, + "surface_hausdorff95_norm": 0.15562347480527094, + "feature_f1": 0.8571428571428571, + "family_match": 0.0, + "experience_methods_returned": 18, + "experience_methods_applied": 3, + "similarity_score": 59.488074232257325 + }, + { + "id": "011680", + "family": "plate_or_bracket", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 898496.0, + "generated_volume": 19016.0, + "volume_rel_error": 0.9788357432865589, + "surface_area_rel_error": 0.9780420050604243, + "bbox_mape": 0.5696649029982362, + "face_rel_error": 0.0, + "edge_rel_error": 0.0, + "surface_chamfer_norm": 0.10796723414266624, + "surface_hausdorff95_norm": 0.36481967349123434, + "feature_f1": 1.0, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 60.445468751589296 + }, + { + "id": "011680", + "family": "plate_or_bracket", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 898496.0, + "generated_volume": 13547.999999999998, + "volume_rel_error": 0.9849214687655816, + "surface_area_rel_error": 0.9842171382062035, + "bbox_mape": 0.583774250440917, + "face_rel_error": 0.0, + "edge_rel_error": 0.0, + "surface_chamfer_norm": 0.11235674854244815, + "surface_hausdorff95_norm": 0.374964117147598, + "feature_f1": 1.0, + "family_match": 1.0, + "experience_methods_returned": 10, + "experience_methods_applied": 4, + "similarity_score": 59.90070139134242 + }, + { + "id": "016033", + "family": "patterned_plate", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 44618.37587532573, + "generated_volume": 23882.59512641282, + "volume_rel_error": 0.4647363410728704, + "surface_area_rel_error": 0.34116223818430896, + "bbox_mape": 0.18571427761876255, + "face_rel_error": 0.26666666666666666, + "edge_rel_error": 0.2631578947368421, + "surface_chamfer_norm": 0.018452946285241585, + "surface_hausdorff95_norm": 0.08713798240651745, + "feature_f1": 1.0, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 81.50778192907364 + }, + { + "id": "016033", + "family": "patterned_plate", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 44618.37587532573, + "generated_volume": 13866.369040708962, + "volume_rel_error": 0.6892229094251465, + "surface_area_rel_error": 0.5000404499582691, + "bbox_mape": 0.3238095238095235, + "face_rel_error": 0.06666666666666667, + "edge_rel_error": 0.02631578947368421, + "surface_chamfer_norm": 0.026851094204940862, + "surface_hausdorff95_norm": 0.12169100582216402, + "feature_f1": 0.9411764705882353, + "family_match": 1.0, + "experience_methods_returned": 10, + "experience_methods_applied": 3, + "similarity_score": 75.91174289423121 + }, + { + "id": "020543", + "family": "flanged_rotational_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 24145.808587596228, + "generated_volume": 118756.98485692723, + "volume_rel_error": 3.9183271053483395, + "surface_area_rel_error": 1.5105685440071281, + "bbox_mape": 0.5959595959595959, + "face_rel_error": 0.28125, + "edge_rel_error": 0.2857142857142857, + "surface_chamfer_norm": 0.08451218347517844, + "surface_hausdorff95_norm": 0.22747541674825858, + "feature_f1": 1.0, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 51.56160371886595 + }, + { + "id": "020543", + "family": "flanged_rotational_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 24145.808587596228, + "generated_volume": 55638.90609255368, + "volume_rel_error": 1.3042883774509648, + "surface_area_rel_error": 0.4640684949891876, + "bbox_mape": 0.23232323232323235, + "face_rel_error": 0.1875, + "edge_rel_error": 0.05194805194805195, + "surface_chamfer_norm": 0.042969081471212464, + "surface_hausdorff95_norm": 0.11104259063963073, + "feature_f1": 0.888888888888889, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 69.33575293745653 + }, + { + "id": "024675", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 580617.6091315141, + "generated_volume": 569438.4483554161, + "volume_rel_error": 0.019253912730652068, + "surface_area_rel_error": 0.2739018704584683, + "bbox_mape": 0.06666666666666685, + "face_rel_error": 0.19480519480519481, + "edge_rel_error": 0.2, + "surface_chamfer_norm": 0.011073315847120254, + "surface_hausdorff95_norm": 0.07352156562273186, + "feature_f1": 1.0, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 91.92371160771847 + }, + { + "id": "024675", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 580617.6091315141, + "generated_volume": 38876.03946418978, + "volume_rel_error": 0.9330436437807312, + "surface_area_rel_error": 0.8746474563374864, + "bbox_mape": 0.5777777777777777, + "face_rel_error": 1.4025974025974026, + "edge_rel_error": 0.8933333333333333, + "surface_chamfer_norm": 0.07094038930770122, + "surface_hausdorff95_norm": 0.33837892456299423, + "feature_f1": 0.9333333333333333, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 56.84188202691976 + }, + { + "id": "029190", + "family": "patterned_plate", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 131254.75510206894, + "generated_volume": 73807.58425433026, + "volume_rel_error": 0.4376768735202429, + "surface_area_rel_error": 0.1560183040026604, + "bbox_mape": 0.0980952380952385, + "face_rel_error": 0.14285714285714285, + "edge_rel_error": 0.16666666666666666, + "surface_chamfer_norm": 0.024101504873521428, + "surface_hausdorff95_norm": 0.04232654923555514, + "feature_f1": 0.9333333333333333, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 83.74861954252185 + }, + { + "id": "029190", + "family": "patterned_plate", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 131254.75510206894, + "generated_volume": 71846.37021205797, + "volume_rel_error": 0.45261891535901033, + "surface_area_rel_error": 0.20263501345527155, + "bbox_mape": 0.14047619047618823, + "face_rel_error": 1.6428571428571428, + "edge_rel_error": 1.5277777777777777, + "surface_chamfer_norm": 0.02460078307658505, + "surface_hausdorff95_norm": 0.04143094591291666, + "feature_f1": 0.7999999999999999, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 73.91053356207678 + }, + { + "id": "030352", + "family": "patterned_plate", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 60881.15044407846, + "generated_volume": 34352.594678913054, + "volume_rel_error": 0.4357433388111292, + "surface_area_rel_error": 0.48742381144539443, + "bbox_mape": 0.31241830065359194, + "face_rel_error": 0.5625, + "edge_rel_error": 0.9285714285714286, + "surface_chamfer_norm": 0.02510282770499014, + "surface_hausdorff95_norm": 0.10902462973941958, + "feature_f1": 0.923076923076923, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 73.94372815488485 + }, + { + "id": "030352", + "family": "patterned_plate", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 60881.15044407846, + "generated_volume": 11173.345159683424, + "volume_rel_error": 0.8164728314398964, + "surface_area_rel_error": 0.6732862799472543, + "bbox_mape": 0.4306604575163398, + "face_rel_error": 2.0625, + "edge_rel_error": 2.642857142857143, + "surface_chamfer_norm": 0.03957952772551367, + "surface_hausdorff95_norm": 0.16850566658178062, + "feature_f1": 0.7692307692307692, + "family_match": 1.0, + "experience_methods_returned": 10, + "experience_methods_applied": 3, + "similarity_score": 59.45331217188532 + }, + { + "id": "046014", + "family": "flanged_rotational_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 162271.3445381513, + "generated_volume": 73578.89519968569, + "volume_rel_error": 0.5465687709120651, + "surface_area_rel_error": 0.333964798541362, + "bbox_mape": 0.21212121212121218, + "face_rel_error": 0.43243243243243246, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 0.033942930350360764, + "surface_hausdorff95_norm": 0.05126001418573268, + "feature_f1": 0.9600000000000001, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 75.92217396437314 + }, + { + "id": "046014", + "family": "flanged_rotational_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 162271.3445381513, + "generated_volume": 111532.3028776836, + "volume_rel_error": 0.31268023202050027, + "surface_area_rel_error": 0.24200901291799348, + "bbox_mape": 0.13636363636363644, + "face_rel_error": 0.43243243243243246, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 0.02662187229047825, + "surface_hausdorff95_norm": 0.043202965297600246, + "feature_f1": 0.9600000000000001, + "family_match": 1.0, + "experience_methods_returned": 10, + "experience_methods_applied": 4, + "similarity_score": 81.61765078194381 + }, + { + "id": "047048", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 33461.296317388085, + "generated_volume": 167251.53322574112, + "volume_rel_error": 3.9983578531842254, + "surface_area_rel_error": 1.6260155362793598, + "bbox_mape": 0.6851851851851847, + "face_rel_error": 0.34782608695652173, + "edge_rel_error": 0.3709677419354839, + "surface_chamfer_norm": 0.11322328439048983, + "surface_hausdorff95_norm": 0.2789388077989153, + "feature_f1": 0.823529411764706, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 44.79000279500974 + }, + { + "id": "047048", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 33461.296317388085, + "generated_volume": 169277.67589913897, + "volume_rel_error": 4.058909681606514, + "surface_area_rel_error": 1.587008333438104, + "bbox_mape": 0.7175925925925921, + "face_rel_error": 0.2391304347826087, + "edge_rel_error": 0.1693548387096774, + "surface_chamfer_norm": 0.13259494837120114, + "surface_hausdorff95_norm": 0.321530970786483, + "feature_f1": 0.823529411764706, + "family_match": 1.0, + "experience_methods_returned": 18, + "experience_methods_applied": 3, + "similarity_score": 44.38628038601891 + }, + { + "id": "048346", + "family": "hollow_or_stepped_shaft", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 8341713.8934443, + "generated_volume": 8278002.394429736, + "volume_rel_error": 0.007637698898380382, + "surface_area_rel_error": 0.01545893719801078, + "bbox_mape": 1.4575235605335388e-16, + "face_rel_error": 0.3125, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 0.008802226092164953, + "surface_hausdorff95_norm": 0.016975575422477428, + "feature_f1": 0.9166666666666666, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 93.45850460439323 + }, + { + "id": "048346", + "family": "hollow_or_stepped_shaft", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 8341713.8934443, + "generated_volume": 8292610.800268921, + "volume_rel_error": 0.005886451369899948, + "surface_area_rel_error": 0.012238325281746575, + "bbox_mape": 1.4575235605335388e-16, + "face_rel_error": 0.3125, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 0.008857323306102671, + "surface_hausdorff95_norm": 0.017730676062993916, + "feature_f1": 0.9166666666666666, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 93.51807327218812 + }, + { + "id": "053664", + "family": "patterned_plate", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 349435.5647059466, + "generated_volume": 315117.2806010815, + "volume_rel_error": 0.0982106218459597, + "surface_area_rel_error": 0.07576465816363272, + "bbox_mape": 0.02857142857142833, + "face_rel_error": 0.21212121212121213, + "edge_rel_error": 0.2222222222222222, + "surface_chamfer_norm": 0.012696566244914555, + "surface_hausdorff95_norm": 0.02434484580541925, + "feature_f1": 0.8571428571428571, + "family_match": 0.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 85.57701767939263 + }, + { + "id": "053664", + "family": "patterned_plate", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 349435.5647059466, + "generated_volume": 282776.14120936824, + "volume_rel_error": 0.19076313412080106, + "surface_area_rel_error": 0.167398697885095, + "bbox_mape": 0.07936507936507906, + "face_rel_error": 0.030303030303030304, + "edge_rel_error": 0.0, + "surface_chamfer_norm": 0.016320456106002173, + "surface_hausdorff95_norm": 0.05162627100789126, + "feature_f1": 0.8571428571428571, + "family_match": 0.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 83.57268406993335 + }, + { + "id": "063487", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "063487", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "065487", + "family": "flanged_rotational_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 81354.16556588435, + "generated_volume": 68680.44493144708, + "volume_rel_error": 0.15578453231351141, + "surface_area_rel_error": 0.060356551334099576, + "bbox_mape": 0.05572210294349599, + "face_rel_error": 0.4396551724137931, + "edge_rel_error": 0.420863309352518, + "surface_chamfer_norm": 0.022572421105818447, + "surface_hausdorff95_norm": 0.053770414665160235, + "feature_f1": 0.923076923076923, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 87.21779338748289 + }, + { + "id": "065487", + "family": "flanged_rotational_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "067186", + "family": "patterned_plate", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "067186", + "family": "patterned_plate", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "073706", + "family": "rotational_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "073706", + "family": "rotational_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "077955", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "077955", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "082187", + "family": "prismatic_block", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "082187", + "family": "prismatic_block", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "089970", + "family": "rotational_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "089970", + "family": "rotational_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "092092", + "family": "shaft", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "092092", + "family": "shaft", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "095520", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "095520", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "102128", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "102128", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "104465", + "family": "patterned_plate", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "104465", + "family": "patterned_plate", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "110143", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "110143", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "112069", + "family": "prismatic_block", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "112069", + "family": "prismatic_block", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "116166", + "family": "rotational_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 147536.10631008807, + "generated_volume": 67519.74039233122, + "volume_rel_error": 0.5423510754010293, + "surface_area_rel_error": 0.38925591734814796, + "bbox_mape": 0.22361111004444456, + "face_rel_error": 0.6153846153846154, + "edge_rel_error": 0.6538461538461539, + "surface_chamfer_norm": 0.03424668647245051, + "surface_hausdorff95_norm": 0.07518718288900171, + "feature_f1": 0.761904761904762, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 71.3680012563105 + }, + { + "id": "116166", + "family": "rotational_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 147536.10631008807, + "generated_volume": 69565.58402815007, + "volume_rel_error": 0.528484343473599, + "surface_area_rel_error": 0.365981954180488, + "bbox_mape": 0.18888888915554566, + "face_rel_error": 0.6923076923076923, + "edge_rel_error": 0.7307692307692307, + "surface_chamfer_norm": 0.032489227662340435, + "surface_hausdorff95_norm": 0.07289222461786297, + "feature_f1": 0.8571428571428572, + "family_match": 1.0, + "experience_methods_returned": 16, + "experience_methods_applied": 3, + "similarity_score": 73.4520592182128 + }, + { + "id": "134828", + "family": "multi_axis_manifold", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 349556.2389887021, + "generated_volume": 34234.94531047202, + "volume_rel_error": 0.9020616956815967, + "surface_area_rel_error": 0.9239731944207157, + "bbox_mape": 0.6746686351889055, + "face_rel_error": 0.45, + "edge_rel_error": 0.45217391304347826, + "surface_chamfer_norm": 0.10449739414590258, + "surface_hausdorff95_norm": 0.3144594900968871, + "feature_f1": 0.7692307692307692, + "family_match": 0.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 48.526262227273214 + }, + { + "id": "134828", + "family": "multi_axis_manifold", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 349556.2389887021, + "generated_volume": 38190.323227729335, + "volume_rel_error": 0.8907462692177447, + "surface_area_rel_error": 0.9152269986233839, + "bbox_mape": 0.7062506154470962, + "face_rel_error": 0.5, + "edge_rel_error": 0.5043478260869565, + "surface_chamfer_norm": 0.09836507300701838, + "surface_hausdorff95_norm": 0.29918959165673675, + "feature_f1": 0.7692307692307692, + "family_match": 0.0, + "experience_methods_returned": 14, + "experience_methods_applied": 5, + "similarity_score": 48.60371993717729 + }, + { + "id": "136930", + "family": "rotational_part", + "variant": "baseline", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 38.877209088173686, + "generated_volume": 25446.900494077883, + "volume_rel_error": 653.545454545469, + "surface_area_rel_error": 72.6363636363658, + "bbox_mape": 7.3809523809523805, + "face_rel_error": 0.3333333333333333, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 1.666072751413829, + "surface_hausdorff95_norm": 2.6857505921882785, + "feature_f1": 0.9411764705882353, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 25.720060532485707 + }, + { + "id": "136930", + "family": "rotational_part", + "variant": "experience", + "generated": true, + "valid": true, + "solid_count": 1, + "reference_volume": 38.877209088173686, + "generated_volume": 21205.750411731566, + "volume_rel_error": 544.4545454545574, + "surface_area_rel_error": 67.18181818181998, + "bbox_mape": 6.936507936507937, + "face_rel_error": 0.3333333333333333, + "edge_rel_error": 0.5, + "surface_chamfer_norm": 1.6549008981976585, + "surface_hausdorff95_norm": 2.6698915132197145, + "feature_f1": 0.9411764705882353, + "family_match": 1.0, + "experience_methods_returned": 0, + "experience_methods_applied": 0, + "similarity_score": 25.725642575854934 + }, + { + "id": "138022", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "138022", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "141680", + "family": "patterned_plate", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "141680", + "family": "patterned_plate", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "145574", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "145574", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "150196", + "family": "prismatic_block", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "150196", + "family": "prismatic_block", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "150587", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "150587", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "152568", + "family": "multi_axis_manifold", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "152568", + "family": "multi_axis_manifold", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "153245", + "family": "plate_or_bracket", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "153245", + "family": "plate_or_bracket", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "158256", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "158256", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "162558", + "family": "hollow_or_stepped_shaft", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "162558", + "family": "hollow_or_stepped_shaft", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "167962", + "family": "multi_axis_manifold", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "167962", + "family": "multi_axis_manifold", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "168239", + "family": "rotational_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "168239", + "family": "rotational_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "174675", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "174675", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "183151", + "family": "patterned_plate", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "183151", + "family": "patterned_plate", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "189669", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "189669", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "194029", + "family": "rotational_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "194029", + "family": "rotational_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "201547", + "family": "prismatic_block", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "201547", + "family": "prismatic_block", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "204730", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "204730", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "211326", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "211326", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "214404", + "family": "patterned_plate", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "214404", + "family": "patterned_plate", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "229115", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "229115", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "234097", + "family": "hybrid_freeform_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "234097", + "family": "hybrid_freeform_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + }, + { + "id": "242307", + "family": "patterned_prismatic_part", + "variant": "baseline", + "generated": false, + "error": "missing STEP" + }, + { + "id": "242307", + "family": "patterned_prismatic_part", + "variant": "experience", + "generated": false, + "error": "missing STEP" + } +] diff --git a/cad-experience-plugin/benchmarks/ab50/report.md b/cad-experience-plugin/benchmarks/ab50/report.md new file mode 100644 index 0000000..2535570 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/report.md @@ -0,0 +1,39 @@ +# CAD 经验库 50 件 A/B 盲重建报告 + +> 运行状态:2026-07-23 因 Codex 账户用量上限中断;当前为 15/50 +> 完整配对的阶段结果。系统提示可在 2026-07-29 10:24 后继续。 + +## 口径 + +- 测试集:固定分层抽取 50 件;这 50 件不进入经验库。 +- 训练经验:其余 950 件生成的通用经验库。 +- 两组输入完全相同:仅多视图图片和文字描述;生成过程禁止读取参考 STEP 和解析 JSON。 +- Baseline:空经验库;Experience:950 件经验库。 +- 参考 STEP 只在生成完成后用于评分。 +- “相似度”是体积、面积、包围盒、表面距离、拓扑和语义特征的综合诊断分,不代表制造级 1:1 或可互换性。 + +## 当前完成度 + +- 有效 Baseline:16/50 +- 有效 Experience:15/50 +- 完整配对:15/50 +- 经验方法返回/实际采用:106/28 + +## 汇总 + +| 指标 | 无经验 | 有经验 | 差值 | +|---|---:|---:|---:| +| 综合相似度(0–100,越高越好) | 69.11 | 66.64 | -2.47 | +| 体积相对误差(越低越好) | 44.519 | 37.136 | -7.383 | +| 表面积相对误差 | 5.385 | 4.997 | -0.388 | +| 包围盒尺寸 MAPE | 0.766 | 0.760 | -0.006 | +| 归一化表面 Chamfer | 0.1537 | 0.1559 | +0.0023 | +| 特征 F1 | 0.907 | 0.878 | -0.029 | + +经验组胜/平/负(±0.25 分视为平):**4/4/7**。 + +## 阶段解释 + +- 经验组的体积、表面积和包围盒平均误差较低,说明通用构造经验对尺度/主体关系有一定帮助。 +- 经验组的表面距离、特征 F1 和综合分暂时较差,说明部分经验会诱导多余或错误特征,当前不能宣称整体提升。 +- 每组由独立的大模型运行生成;即使经验命中为 0,两次结果也可能因模型随机性不同。因此最终应完成 50 对,并对若干样本做重复运行,才能把经验效果与随机波动分开。 diff --git a/cad-experience-plugin/benchmarks/ab50/run_batch.py b/cad-experience-plugin/benchmarks/ab50/run_batch.py new file mode 100644 index 0000000..5c9f84d --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/run_batch.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path + +HERE = Path(__file__).resolve().parent + + +def run(case_id: str, variant: str) -> dict: + completed = subprocess.run( + [sys.executable, str(HERE / "run_case.py"), case_id, variant], + capture_output=True, + text=True, + ) + return { + "id": case_id, + "variant": variant, + "returncode": completed.returncode, + "output": completed.stdout.strip(), + "error": completed.stderr.strip(), + } + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--workers", type=int, default=4) + parser.add_argument("--start-index", type=int, default=0) + parser.add_argument("--end-index", type=int) + args = parser.parse_args() + manifest = json.loads((HERE / "manifest.json").read_text()) + cases = manifest["cases"][args.start_index : args.end_index] + jobs = [ + (case["id"], variant) + for case in cases + for variant in ("baseline", "experience") + ] + results_name = ( + "batch-results.json" + if args.start_index == 0 and args.end_index is None + else f"batch-results-{args.start_index}-{args.end_index or 'end'}.json" + ) + results_path = HERE / results_name + results = [] + with ThreadPoolExecutor(max_workers=args.workers) as pool: + futures = {pool.submit(run, *job): job for job in jobs} + for future in as_completed(futures): + result = future.result() + results.append(result) + print(json.dumps(result, ensure_ascii=False), flush=True) + results_path.write_text( + json.dumps(results, indent=2, ensure_ascii=False) + "\n" + ) + failed = [item for item in results if item["returncode"] != 0] + print(json.dumps({"completed": len(results), "failed": len(failed)})) + raise SystemExit(1 if failed else 0) + + +if __name__ == "__main__": + main() diff --git a/cad-experience-plugin/benchmarks/ab50/run_case.py b/cad-experience-plugin/benchmarks/ab50/run_case.py new file mode 100644 index 0000000..27ab1f8 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/run_case.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import subprocess +from pathlib import Path + +HERE = Path(__file__).resolve().parent +WORKSPACE = HERE.parents[2] + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("case_id") + parser.add_argument("variant", choices=("baseline", "experience")) + args = parser.parse_args() + manifest = json.loads((HERE / "manifest.json").read_text()) + case = next(item for item in manifest["cases"] if item["id"] == args.case_id) + output = Path(case[f"{args.variant}_step"]) + output.parent.mkdir(parents=True, exist_ok=True) + if output.is_file(): + print(json.dumps({"status": "skipped", "output": str(output)})) + return + + description = Path(case["description"]).read_text() + library = ( + HERE / "empty-library.json" + if args.variant == "baseline" + else HERE / "train-library.json" + ) + prompt = f""" +This is a controlled CAD reconstruction benchmark. +Use $cad-router and $cad to reconstruct the single mechanical part shown in +the attached multi-view image and described below. Generate a new parametric +STEP from the image and description only. + +Hard isolation rules: +- Do not search for, inspect, import, copy, or reference any teacher STEP, + SLDPRT, parser/input, parser/output, case JSON, or SLDPRT-NET dataset file. +- The attached image and the description in this prompt are the only + part-specific evidence. +- Run cad-router with the explicit generalized experience library + {library.resolve()}; do not use any other experience library. +- Use the router-selected backend and actually apply returned generalized + methods when present. +- Do not preserve an exact STEP base: this must be a blind reconstruction. + +Write the primary output exactly to: +{output.resolve()} +Keep the editable generator and model-spec.json in the same directory. +Also write the router result to route.json and record in cad-task.json the +experience methods returned and the subset actually applied. Record an empty +list when nothing matches; never claim an experience method was used unless +the router returned it. +Validate that the STEP opens and contains positive-volume geometry. For this +batch benchmark, do not open CAD Viewer or create browser tabs. + +Description: +{description} +""" + command = [ + "codex", + "exec", + "--ephemeral", + "--json", + "--image", + case["image"], + "--cd", + str(WORKSPACE), + "--sandbox", + "danger-full-access", + "--output-last-message", + case[f"{args.variant}_message"], + prompt, + ] + with Path(case[f"{args.variant}_log"]).open("w") as stream: + completed = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT) + print(json.dumps({ + "status": "ok" if completed.returncode == 0 and output.is_file() else "failed", + "returncode": completed.returncode, + "output": str(output), + })) + raise SystemExit(0 if completed.returncode == 0 and output.is_file() else 1) + + +if __name__ == "__main__": + main() diff --git a/cad-experience-plugin/benchmarks/ab50/setup_benchmark.py b/cad-experience-plugin/benchmarks/ab50/setup_benchmark.py new file mode 100644 index 0000000..471ec58 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/setup_benchmark.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json +import random +from collections import defaultdict +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +OUTPUT = ROOT / "parser" / "output" +HERE = Path(__file__).resolve().parent +DATASET = Path("/Users/jerry/linkhand/cad/SLDPRT-NET/sub_datset/sub_datset") +TARGET_COUNT = 50 + + +def main() -> None: + rows: list[dict] = [] + for path in sorted(OUTPUT.glob("*.json")): + payload = json.loads(path.read_text()) + stem = Path(payload["provenance"]["source_path"]).stem + image = DATASET / "image" / f"{stem}.png" + description = DATASET / "Des_Text" / f"{stem}.txt" + reference = DATASET / "model_step" / f"{stem}.step" + if image.is_file() and description.is_file() and reference.is_file(): + rows.append( + { + "id": stem, + "family": payload["design_ir"]["part_family"], + "case_json": str(path.resolve()), + "image": str(image), + "description": str(description), + "reference_step": str(reference), + } + ) + + by_family: dict[str, list[dict]] = defaultdict(list) + for row in rows: + by_family[row["family"]].append(row) + rng = random.Random(20260723) + selected: list[dict] = [] + total = len(rows) + for family, family_rows in sorted(by_family.items()): + rng.shuffle(family_rows) + quota = max(1, round(TARGET_COUNT * len(family_rows) / total)) + selected.extend(family_rows[: min(quota, len(family_rows))]) + selected_ids = {row["id"] for row in selected} + if len(selected) > TARGET_COUNT: + selected = selected[:TARGET_COUNT] + elif len(selected) < TARGET_COUNT: + remainder = [row for row in rows if row["id"] not in selected_ids] + rng.shuffle(remainder) + selected.extend(remainder[: TARGET_COUNT - len(selected)]) + selected = sorted(selected, key=lambda row: row["id"]) + selected_ids = {row["id"] for row in selected} + + train_dir = HERE / "train_cases" + train_dir.mkdir(parents=True, exist_ok=True) + for path in train_dir.glob("*.json"): + path.unlink() + for row in rows: + if row["id"] in selected_ids: + continue + source = Path(row["case_json"]) + (train_dir / source.name).symlink_to(source) + + for row in selected: + for variant in ("baseline", "experience"): + output_dir = HERE / "generated" / variant / row["id"] + output_dir.mkdir(parents=True, exist_ok=True) + row[f"{variant}_step"] = str(output_dir / f"{row['id']}.step") + row[f"{variant}_log"] = str(output_dir / "codex.jsonl") + row[f"{variant}_message"] = str(output_dir / "final.txt") + + manifest = { + "schema_version": "1.0", + "selection_seed": 20260723, + "holdout_count": len(selected), + "train_count": len(rows) - len(selected), + "cases": selected, + } + (HERE / "manifest.json").write_text( + json.dumps(manifest, indent=2, ensure_ascii=False) + "\n" + ) + print(json.dumps({ + "holdout_count": len(selected), + "train_count": len(rows) - len(selected), + "family_counts": { + family: sum(row["family"] == family for row in selected) + for family in sorted({row["family"] for row in selected}) + }, + }, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/cad-experience-plugin/benchmarks/ab50/train-experience-draft.json b/cad-experience-plugin/benchmarks/ab50/train-experience-draft.json new file mode 100644 index 0000000..0ac3ed6 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/train-experience-draft.json @@ -0,0 +1,2505 @@ +{ + "schema_version": "2.0", + "draft_kind": "llm_generalized_experience_proposals", + "review_intent": "Transfer semantic construction methods, constraints, and dimensionless family priors without source geometry.", + "proposals": [ + { + "id": "motif.flanged_rotational_part.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ], + "required_relations": [], + "context_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ] + }, + "guidance": "Organize repeated axial holes as one circular pattern referenced to a semantic pattern center.", + "semantic_rationale": "Pattern-level representation keeps count, radius role, and radial placement coherent across dimensional variants." + }, + { + "id": "motif.flanged_rotational_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.flanged_rotational_part.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change." + }, + { + "id": "motif.flanged_rotational_part.rotational_body_and_flange_like_rotational_body", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "flange_like_rotational_body" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "flange_like_rotational_body" + ] + }, + "guidance": "Separate the flange-like region from the remaining rotational profile while retaining their shared axis.", + "semantic_rationale": "Role separation permits flange and hub edits without losing rotational continuity." + }, + { + "id": "motif.flanged_rotational_part.shaft_like_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "shaft_like_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "shaft_like_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Model a shaft-like body through named axial sections and shoulders.", + "semantic_rationale": "Named sections make length and diameter edits local while preserving the overall shaft topology." + }, + { + "id": "motif.flanged_rotational_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families." + }, + { + "id": "motif.flanged_rotational_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "motif.flanged_rotational_part.rotational_body_and_toroidal_blend_or_groove", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "toroidal_blend_or_groove" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "toroidal_blend_or_groove" + ] + }, + "guidance": "Attach toroidal blend or groove candidates to the surrounding rotational profile rather than treating them as free geometry.", + "semantic_rationale": "Referencing the host profile preserves the local transition role when the host diameter changes." + }, + { + "id": "constraint.flanged_rotational_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern" + ], + "required_relations": [ + "repeated_radius_group" + ], + "context_features": [ + "repeated_axial_hole_pattern" + ] + }, + "guidance": "Preserve a shared radius role across members of a repeated axial feature group.", + "semantic_rationale": "The invariant applies across sizes because it constrains equality of roles rather than an absolute radius.", + "check": "Confirm that the repeated radius group relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.common_pattern_radius", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ], + "required_relations": [ + "common_pattern_radius" + ], + "context_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ] + }, + "guidance": "Preserve a common radial placement role for members of a circular repeated-feature pattern.", + "semantic_rationale": "A shared pattern radius keeps the array coherent while permitting overall scaling.", + "check": "Confirm that the common pattern radius relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "multi_axis_passage" + ], + "required_relations": [ + "multiple_axis_system" + ], + "context_features": [ + "multi_axis_passage" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.coaxial_stack", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "coaxial_stack" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "dominant_axis_alignment" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "distribution.flanged_rotational_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "pattern_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.flanged_rotational_part.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "observation_name": "repeated_feature_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "constraint.general_mechanical_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "general_mechanical_part" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "distribution.general_mechanical_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.general_mechanical_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.general_mechanical_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.general_mechanical_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.general_mechanical_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.general_mechanical_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.hollow_or_stepped_shaft.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.hollow_or_stepped_shaft.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change." + }, + { + "id": "motif.hollow_or_stepped_shaft.shaft_like_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "shaft_like_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "shaft_like_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Model a shaft-like body through named axial sections and shoulders.", + "semantic_rationale": "Named sections make length and diameter edits local while preserving the overall shaft topology." + }, + { + "id": "motif.hollow_or_stepped_shaft.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families." + }, + { + "id": "motif.hollow_or_stepped_shaft.rotational_body_and_toroidal_blend_or_groove", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "toroidal_blend_or_groove" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "toroidal_blend_or_groove" + ] + }, + "guidance": "Attach toroidal blend or groove candidates to the surrounding rotational profile rather than treating them as free geometry.", + "semantic_rationale": "Referencing the host profile preserves the local transition role when the host diameter changes." + }, + { + "id": "constraint.hollow_or_stepped_shaft.shared_passage_axis", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.multiple_axis_system", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "multi_axis_passage" + ], + "required_relations": [ + "multiple_axis_system" + ], + "context_features": [ + "multi_axis_passage" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.coaxial_stack", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "coaxial_stack" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "dominant_axis_alignment" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "distribution.hollow_or_stepped_shaft.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.hollow_or_stepped_shaft.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.hollow_or_stepped_shaft.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.hollow_or_stepped_shaft.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.hollow_or_stepped_shaft.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.hollow_or_stepped_shaft.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.multi_axis_manifold.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.multi_axis_manifold.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families." + }, + { + "id": "motif.multi_axis_manifold.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "constraint.multi_axis_manifold.shared_passage_axis", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.multi_axis_manifold.multiple_axis_system", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "evidence_query": { + "required_features": [ + "multi_axis_passage" + ], + "required_relations": [ + "multiple_axis_system" + ], + "context_features": [ + "multi_axis_passage" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.multi_axis_manifold.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "distribution.multi_axis_manifold.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.multi_axis_manifold.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.multi_axis_manifold.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.multi_axis_manifold.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.multi_axis_manifold.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "observation_name": "pattern_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.multi_axis_manifold.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "observation_name": "repeated_feature_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.patterned_plate.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ], + "required_relations": [], + "context_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ] + }, + "guidance": "Organize repeated axial holes as one circular pattern referenced to a semantic pattern center.", + "semantic_rationale": "Pattern-level representation keeps count, radius role, and radial placement coherent across dimensional variants." + }, + { + "id": "motif.patterned_plate.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.patterned_plate.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families." + }, + { + "id": "motif.patterned_plate.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "constraint.patterned_plate.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern" + ], + "required_relations": [ + "repeated_radius_group" + ], + "context_features": [ + "repeated_axial_hole_pattern" + ] + }, + "guidance": "Preserve a shared radius role across members of a repeated axial feature group.", + "semantic_rationale": "The invariant applies across sizes because it constrains equality of roles rather than an absolute radius.", + "check": "Confirm that the repeated radius group relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.common_pattern_radius", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ], + "required_relations": [ + "common_pattern_radius" + ], + "context_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ] + }, + "guidance": "Preserve a common radial placement role for members of a circular repeated-feature pattern.", + "semantic_rationale": "A shared pattern radius keeps the array coherent while permitting overall scaling.", + "check": "Confirm that the common pattern radius relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.shared_passage_axis", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "multi_axis_passage" + ], + "required_relations": [ + "multiple_axis_system" + ], + "context_features": [ + "multi_axis_passage" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "distribution.patterned_plate.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "pattern_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "repeated_feature_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_plate.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.patterned_prismatic_part.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ], + "required_relations": [], + "context_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ] + }, + "guidance": "Organize repeated axial holes as one circular pattern referenced to a semantic pattern center.", + "semantic_rationale": "Pattern-level representation keeps count, radius role, and radial placement coherent across dimensional variants." + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families." + }, + { + "id": "motif.patterned_prismatic_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "constraint.patterned_prismatic_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern" + ], + "required_relations": [ + "repeated_radius_group" + ], + "context_features": [ + "repeated_axial_hole_pattern" + ] + }, + "guidance": "Preserve a shared radius role across members of a repeated axial feature group.", + "semantic_rationale": "The invariant applies across sizes because it constrains equality of roles rather than an absolute radius.", + "check": "Confirm that the repeated radius group relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.common_pattern_radius", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ], + "required_relations": [ + "common_pattern_radius" + ], + "context_features": [ + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern" + ] + }, + "guidance": "Preserve a common radial placement role for members of a circular repeated-feature pattern.", + "semantic_rationale": "A shared pattern radius keeps the array coherent while permitting overall scaling.", + "check": "Confirm that the common pattern radius relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "multi_axis_passage" + ], + "required_relations": [ + "multiple_axis_system" + ], + "context_features": [ + "multi_axis_passage" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "distribution.patterned_prismatic_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "pattern_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.patterned_prismatic_part.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "observation_name": "repeated_feature_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.plate_or_bracket.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "plate_or_bracket" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "distribution.plate_or_bracket.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.plate_or_bracket.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.plate_or_bracket.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.plate_or_bracket.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.plate_or_bracket.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "observation_name": "pattern_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.plate_or_bracket.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "observation_name": "repeated_feature_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.prismatic_block.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "prismatic_block" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "constraint.prismatic_block.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "prismatic_block" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "distribution.prismatic_block.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "pattern_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.prismatic_block.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "observation_name": "repeated_feature_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.rotational_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.rotational_part.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change." + }, + { + "id": "motif.rotational_part.rotational_body_and_flange_like_rotational_body", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "flange_like_rotational_body" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "flange_like_rotational_body" + ] + }, + "guidance": "Separate the flange-like region from the remaining rotational profile while retaining their shared axis.", + "semantic_rationale": "Role separation permits flange and hub edits without losing rotational continuity." + }, + { + "id": "motif.rotational_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families." + }, + { + "id": "motif.rotational_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "required_relations": [], + "context_features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits." + }, + { + "id": "motif.rotational_part.rotational_body_and_toroidal_blend_or_groove", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "toroidal_blend_or_groove" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "toroidal_blend_or_groove" + ] + }, + "guidance": "Attach toroidal blend or groove candidates to the surrounding rotational profile rather than treating them as free geometry.", + "semantic_rationale": "Referencing the host profile preserves the local transition role when the host diameter changes." + }, + { + "id": "constraint.rotational_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "multi_axis_passage" + ], + "required_relations": [ + "multiple_axis_system" + ], + "context_features": [ + "multi_axis_passage" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.coaxial_stack", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "coaxial_stack" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "dominant_axis_alignment" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "distribution.rotational_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.rotational_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.rotational_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.rotational_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.rotational_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.rotational_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "motif.shaft.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "required_relations": [], + "context_features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change." + }, + { + "id": "motif.shaft.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "rotational_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change." + }, + { + "id": "motif.shaft.shaft_like_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "shaft_like_body", + "stepped_rotational_profile" + ], + "required_relations": [], + "context_features": [ + "shaft_like_body", + "stepped_rotational_profile" + ] + }, + "guidance": "Model a shaft-like body through named axial sections and shoulders.", + "semantic_rationale": "Named sections make length and diameter edits local while preserving the overall shaft topology." + }, + { + "id": "constraint.shaft.shared_passage_axis", + "kind": "constraint", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "stepped_cylindrical_passage" + ], + "required_relations": [ + "shared_passage_axis" + ], + "context_features": [ + "stepped_cylindrical_passage" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "conical_transition" + ], + "required_relations": [ + "transition_axis_continuity" + ], + "context_features": [ + "conical_transition" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.coaxial_stack", + "kind": "constraint", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "coaxial_stack" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "shaft" + ], + "evidence_query": { + "required_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "required_relations": [ + "dominant_axis_alignment" + ], + "context_features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "distribution.shaft.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "observation_name": "bbox_short_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.shaft.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "observation_name": "bbox_middle_to_long", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.shaft.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "observation_name": "planar_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.shaft.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "observation_name": "cylindrical_area_fraction", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.shaft.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "observation_name": "largest_coaxial_radius_to_transverse_span", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + }, + { + "id": "distribution.shaft.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "observation_name": "smallest_to_largest_coaxial_radius", + "evidence_query": { + "required_features": [], + "required_relations": [], + "context_features": [] + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions." + } + ] +} diff --git a/cad-experience-plugin/benchmarks/ab50/train-library.json b/cad-experience-plugin/benchmarks/ab50/train-library.json new file mode 100644 index 0000000..7bb4fc7 --- /dev/null +++ b/cad-experience-plugin/benchmarks/ab50/train-library.json @@ -0,0 +1,3198 @@ +{ + "schema_version": "2.0", + "library_kind": "generalized_cad_experience", + "generated_at": "2026-07-23T09:46:25+00:00", + "induction_mode": "llm_proposals_with_deterministic_evidence_verification", + "status": "ready", + "policy": { + "instance_parameters_allowed": false, + "absolute_coordinates_allowed": false, + "single_case_promotion_allowed": false, + "llm_semantic_review_required": true, + "draft_evidence_verified": true, + "router_consumable": true, + "minimum_support": 20, + "minimum_confidence": 0.8 + }, + "corpus_summary": { + "accepted_case_count": 950, + "duplicate_case_count": 0, + "rejected_case_count": 0, + "family_count": 11 + }, + "experience_summary": { + "llm_proposal_count": 139, + "promoted_experience_count": 112, + "candidate_experience_count": 27, + "by_kind": [ + { + "kind": "constraint", + "count": 29 + }, + { + "kind": "dimensionless_distribution", + "count": 56 + }, + { + "kind": "feature_motif", + "count": 27 + } + ], + "by_scope": [ + { + "scope": "flanged_rotational_part", + "count": 19 + }, + { + "scope": "hollow_or_stepped_shaft", + "count": 13 + }, + { + "scope": "multi_axis_manifold", + "count": 9 + }, + { + "scope": "patterned_plate", + "count": 15 + }, + { + "scope": "patterned_prismatic_part", + "count": 17 + }, + { + "scope": "plate_or_bracket", + "count": 5 + }, + { + "scope": "prismatic_block", + "count": 7 + }, + { + "scope": "rotational_part", + "count": 14 + }, + { + "scope": "shaft", + "count": 13 + } + ], + "candidate_by_kind": [ + { + "kind": "constraint", + "count": 7 + }, + { + "kind": "dimensionless_distribution", + "count": 12 + }, + { + "kind": "feature_motif", + "count": 8 + } + ] + }, + "experiences": [ + { + "id": "constraint.flanged_rotational_part.coaxial_stack", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "support": 52, + "confidence": 1.0, + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.common_pattern_radius", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [ + "common_pattern_radius" + ] + }, + "guidance": "Preserve a common radial placement role for members of a circular repeated-feature pattern.", + "semantic_rationale": "A shared pattern radius keeps the array coherent while permitting overall scaling.", + "support": 45, + "confidence": 1.0, + "check": "Confirm that the common pattern radius relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "support": 52, + "confidence": 1.0, + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "repeated_axial_hole_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Preserve a shared radius role across members of a repeated axial feature group.", + "semantic_rationale": "The invariant applies across sizes because it constrains equality of roles rather than an absolute radius.", + "support": 52, + "confidence": 1.0, + "check": "Confirm that the repeated radius group relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 52, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.flanged_rotational_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 22, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.coaxial_stack", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "support": 44, + "confidence": 1.0, + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "support": 44, + "confidence": 1.0, + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.shared_passage_axis", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 44, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.hollow_or_stepped_shaft.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 34, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.multi_axis_manifold.multiple_axis_system", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "support": 52, + "confidence": 1.0, + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.common_pattern_radius", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [ + "common_pattern_radius" + ] + }, + "guidance": "Preserve a common radial placement role for members of a circular repeated-feature pattern.", + "semantic_rationale": "A shared pattern radius keeps the array coherent while permitting overall scaling.", + "support": 62, + "confidence": 1.0, + "check": "Confirm that the common pattern radius relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "support": 74, + "confidence": 1.0, + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "repeated_axial_hole_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Preserve a shared radius role across members of a repeated axial feature group.", + "semantic_rationale": "The invariant applies across sizes because it constrains equality of roles rather than an absolute radius.", + "support": 199, + "confidence": 1.0, + "check": "Confirm that the repeated radius group relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.shared_passage_axis", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 103, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_plate.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 51, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.common_pattern_radius", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [ + "common_pattern_radius" + ] + }, + "guidance": "Preserve a common radial placement role for members of a circular repeated-feature pattern.", + "semantic_rationale": "A shared pattern radius keeps the array coherent while permitting overall scaling.", + "support": 151, + "confidence": 1.0, + "check": "Confirm that the common pattern radius relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "support": 149, + "confidence": 1.0, + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "repeated_axial_hole_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Preserve a shared radius role across members of a repeated axial feature group.", + "semantic_rationale": "The invariant applies across sizes because it constrains equality of roles rather than an absolute radius.", + "support": 322, + "confidence": 1.0, + "check": "Confirm that the repeated radius group relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 158, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.patterned_prismatic_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 84, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.coaxial_stack", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "support": 117, + "confidence": 1.0, + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "support": 117, + "confidence": 1.0, + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.shared_passage_axis", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 117, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.rotational_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 56, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.coaxial_stack", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "coaxial_stack" + ] + }, + "guidance": "Preserve coaxiality across the named sections of a rotational stack.", + "semantic_rationale": "Coaxial constraints maintain rotational function across dimensional variants.", + "support": 39, + "confidence": 1.0, + "check": "Confirm that the coaxial stack relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.dominant_axis_alignment", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "coaxial_cylindrical_stack", + "rotational_body" + ], + "relations": [ + "dominant_axis_alignment" + ] + }, + "guidance": "Reference rotational sections and dependent features to one dominant semantic axis.", + "semantic_rationale": "A dominant datum axis stabilizes both creation and later parameter edits.", + "support": 39, + "confidence": 1.0, + "check": "Confirm that the dominant axis alignment relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.shared_passage_axis", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 39, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification." + }, + { + "id": "constraint.shaft.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 27, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification." + }, + { + "id": "distribution.flanged_rotational_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.01, + "p10": 0.166169, + "median": 0.909632, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.flanged_rotational_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.005, + "p10": 0.03456, + "median": 0.273751, + "p90": 0.64569, + "max": 0.886752 + } + }, + { + "id": "distribution.flanged_rotational_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.024972, + "p10": 0.089239, + "median": 0.312106, + "p90": 0.615614, + "max": 0.790983 + } + }, + { + "id": "distribution.flanged_rotational_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.002586, + "p10": 0.079846, + "median": 0.422709, + "p90": 0.5, + "max": 0.657143 + } + }, + { + "id": "distribution.flanged_rotational_part.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.113333, + "p10": 0.25729, + "median": 0.410564, + "p90": 0.514069, + "max": 0.587836 + } + }, + { + "id": "distribution.flanged_rotational_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.181639, + "p10": 0.341337, + "median": 0.68225, + "p90": 0.910761, + "max": 0.975028 + } + }, + { + "id": "distribution.flanged_rotational_part.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.001172, + "p10": 0.00755, + "median": 0.033494, + "p90": 0.085875, + "max": 0.313751 + } + }, + { + "id": "distribution.flanged_rotational_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.057143, + "p10": 0.163048, + "median": 0.420833, + "p90": 0.748016, + "max": 0.908571 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 44, + "confidence": 1.0, + "distribution": { + "sample_count": 44, + "min": 0.029762, + "p10": 0.063343, + "median": 0.170197, + "p90": 0.272968, + "max": 0.333333 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 44, + "confidence": 1.0, + "distribution": { + "sample_count": 44, + "min": 0.029762, + "p10": 0.063343, + "median": 0.170197, + "p90": 0.27199, + "max": 0.333333 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 44, + "confidence": 1.0, + "distribution": { + "sample_count": 44, + "min": 0.009277, + "p10": 0.668668, + "median": 0.838041, + "p90": 0.966874, + "max": 0.986218 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 44, + "confidence": 1.0, + "distribution": { + "sample_count": 44, + "min": 0.175334, + "p10": 0.5, + "median": 0.5, + "p90": 0.5, + "max": 0.55 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 44, + "confidence": 1.0, + "distribution": { + "sample_count": 44, + "min": 0.013782, + "p10": 0.029027, + "median": 0.110078, + "p90": 0.256398, + "max": 0.328811 + } + }, + { + "id": "distribution.hollow_or_stepped_shaft.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 44, + "confidence": 1.0, + "distribution": { + "sample_count": 44, + "min": 0.14, + "p10": 0.243638, + "median": 0.345513, + "p90": 0.737982, + "max": 0.882353 + } + }, + { + "id": "distribution.multi_axis_manifold.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.018939, + "p10": 0.188324, + "median": 0.520671, + "p90": 0.906417, + "max": 1.0 + } + }, + { + "id": "distribution.multi_axis_manifold.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.0175, + "p10": 0.09096, + "median": 0.3, + "p90": 0.700038, + "max": 0.833333 + } + }, + { + "id": "distribution.multi_axis_manifold.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.009143, + "p10": 0.054095, + "median": 0.131117, + "p90": 0.495034, + "max": 0.864691 + } + }, + { + "id": "distribution.multi_axis_manifold.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 47, + "confidence": 1.0, + "distribution": { + "sample_count": 47, + "min": 0.021429, + "p10": 0.115, + "median": 0.285714, + "p90": 0.518682, + "max": 1.806003 + } + }, + { + "id": "distribution.multi_axis_manifold.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 52, + "confidence": 1.0, + "distribution": { + "sample_count": 52, + "min": 0.135309, + "p10": 0.439274, + "median": 0.860923, + "p90": 0.945887, + "max": 0.990857 + } + }, + { + "id": "distribution.multi_axis_manifold.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 47, + "confidence": 1.0, + "distribution": { + "sample_count": 47, + "min": 0.013245, + "p10": 0.035222, + "median": 0.090909, + "p90": 0.318432, + "max": 1.900495 + } + }, + { + "id": "distribution.patterned_plate.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 199, + "confidence": 1.0, + "distribution": { + "sample_count": 199, + "min": 0.347826, + "p10": 0.390821, + "median": 0.6625, + "p90": 0.971699, + "max": 1.0 + } + }, + { + "id": "distribution.patterned_plate.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 199, + "confidence": 1.0, + "distribution": { + "sample_count": 199, + "min": 0.001092, + "p10": 0.017031, + "median": 0.076923, + "p90": 0.150612, + "max": 0.214286 + } + }, + { + "id": "distribution.patterned_plate.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 199, + "confidence": 1.0, + "distribution": { + "sample_count": 199, + "min": 0.000133, + "p10": 0.013706, + "median": 0.072976, + "p90": 0.194719, + "max": 0.996549 + } + }, + { + "id": "distribution.patterned_plate.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 199, + "confidence": 1.0, + "distribution": { + "sample_count": 199, + "min": 0.088996, + "p10": 0.25, + "median": 0.388304, + "p90": 0.511533, + "max": 0.643742 + } + }, + { + "id": "distribution.patterned_plate.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 199, + "confidence": 1.0, + "distribution": { + "sample_count": 199, + "min": 0.003451, + "p10": 0.799834, + "median": 0.925302, + "p90": 0.983357, + "max": 0.999867 + } + }, + { + "id": "distribution.patterned_plate.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 199, + "confidence": 1.0, + "distribution": { + "sample_count": 199, + "min": 0.001522, + "p10": 0.006146, + "median": 0.025, + "p90": 0.053552, + "max": 0.304846 + } + }, + { + "id": "distribution.patterned_prismatic_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 322, + "confidence": 1.0, + "distribution": { + "sample_count": 322, + "min": 0.009341, + "p10": 0.0755, + "median": 0.361499, + "p90": 0.843896, + "max": 1.0 + } + }, + { + "id": "distribution.patterned_prismatic_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 322, + "confidence": 1.0, + "distribution": { + "sample_count": 322, + "min": 0.000667, + "p10": 0.026044, + "median": 0.2, + "p90": 0.5, + "max": 0.961145 + } + }, + { + "id": "distribution.patterned_prismatic_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 322, + "confidence": 1.0, + "distribution": { + "sample_count": 322, + "min": 0.000252, + "p10": 0.024342, + "median": 0.11379, + "p90": 0.313355, + "max": 0.820212 + } + }, + { + "id": "distribution.patterned_prismatic_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 29, + "confidence": 1.0, + "distribution": { + "sample_count": 29, + "min": 0.00431, + "p10": 0.018659, + "median": 0.0875, + "p90": 0.199186, + "max": 0.45 + } + }, + { + "id": "distribution.patterned_prismatic_part.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 322, + "confidence": 1.0, + "distribution": { + "sample_count": 322, + "min": 0.02381, + "p10": 0.211394, + "median": 0.333333, + "p90": 0.480026, + "max": 0.64955 + } + }, + { + "id": "distribution.patterned_prismatic_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 322, + "confidence": 1.0, + "distribution": { + "sample_count": 322, + "min": 0.142723, + "p10": 0.677592, + "median": 0.88485, + "p90": 0.974358, + "max": 0.999748 + } + }, + { + "id": "distribution.patterned_prismatic_part.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 322, + "confidence": 1.0, + "distribution": { + "sample_count": 322, + "min": 0.000833, + "p10": 0.0075, + "median": 0.034601, + "p90": 0.111062, + "max": 0.483198 + } + }, + { + "id": "distribution.patterned_prismatic_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 29, + "confidence": 1.0, + "distribution": { + "sample_count": 29, + "min": 1.0, + "p10": 1.0, + "median": 1.0, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.plate_or_bracket.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 35, + "confidence": 1.0, + "distribution": { + "sample_count": 35, + "min": 0.363636, + "p10": 0.450358, + "median": 0.637255, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.plate_or_bracket.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 35, + "confidence": 1.0, + "distribution": { + "sample_count": 35, + "min": 0.0025, + "p10": 0.007895, + "median": 0.045455, + "p90": 0.153143, + "max": 0.214286 + } + }, + { + "id": "distribution.plate_or_bracket.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 35, + "confidence": 1.0, + "distribution": { + "sample_count": 35, + "min": 0.0, + "p10": 0.0, + "median": 0.0, + "p90": 0.044098, + "max": 0.253783 + } + }, + { + "id": "distribution.plate_or_bracket.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 35, + "confidence": 1.0, + "distribution": { + "sample_count": 35, + "min": 0.746217, + "p10": 0.954932, + "median": 1.0, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.prismatic_block.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 68, + "confidence": 1.0, + "distribution": { + "sample_count": 68, + "min": 0.009231, + "p10": 0.047697, + "median": 0.423227, + "p90": 0.866615, + "max": 1.0 + } + }, + { + "id": "distribution.prismatic_block.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 68, + "confidence": 1.0, + "distribution": { + "sample_count": 68, + "min": 0.0, + "p10": 0.008469, + "median": 0.2008, + "p90": 0.553471, + "max": 0.95 + } + }, + { + "id": "distribution.prismatic_block.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 68, + "confidence": 1.0, + "distribution": { + "sample_count": 68, + "min": 0.0, + "p10": 0.0, + "median": 0.034461, + "p90": 0.240921, + "max": 0.351376 + } + }, + { + "id": "distribution.prismatic_block.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 24, + "confidence": 1.0, + "distribution": { + "sample_count": 24, + "min": 0.027273, + "p10": 0.076066, + "median": 0.328947, + "p90": 0.612726, + "max": 0.707107 + } + }, + { + "id": "distribution.prismatic_block.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 68, + "confidence": 1.0, + "distribution": { + "sample_count": 68, + "min": 0.60741, + "p10": 0.678426, + "median": 0.960692, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.prismatic_block.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 24, + "confidence": 1.0, + "distribution": { + "sample_count": 24, + "min": 0.006932, + "p10": 0.014654, + "median": 0.0775, + "p90": 0.2478, + "max": 1.0 + } + }, + { + "id": "distribution.rotational_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 117, + "confidence": 1.0, + "distribution": { + "sample_count": 117, + "min": 0.333507, + "p10": 0.395499, + "median": 0.998168, + "p90": 1.0, + "max": 1.0 + } + }, + { + "id": "distribution.rotational_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 117, + "confidence": 1.0, + "distribution": { + "sample_count": 117, + "min": 0.033654, + "p10": 0.125, + "median": 0.435374, + "p90": 0.833333, + "max": 1.0 + } + }, + { + "id": "distribution.rotational_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 117, + "confidence": 1.0, + "distribution": { + "sample_count": 117, + "min": 0.079284, + "p10": 0.22727, + "median": 0.542739, + "p90": 0.827523, + "max": 0.977011 + } + }, + { + "id": "distribution.rotational_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 117, + "confidence": 1.0, + "distribution": { + "sample_count": 117, + "min": 0.13, + "p10": 0.400971, + "median": 0.5, + "p90": 0.5, + "max": 0.5 + } + }, + { + "id": "distribution.rotational_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 117, + "confidence": 1.0, + "distribution": { + "sample_count": 117, + "min": 0.01974, + "p10": 0.141243, + "median": 0.328864, + "p90": 0.65977, + "max": 0.88406 + } + }, + { + "id": "distribution.rotational_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "rotational_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 117, + "confidence": 1.0, + "distribution": { + "sample_count": 117, + "min": 0.092308, + "p10": 0.24, + "median": 0.480389, + "p90": 0.826638, + "max": 0.95 + } + }, + { + "id": "distribution.shaft.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 39, + "confidence": 1.0, + "distribution": { + "sample_count": 39, + "min": 0.03871, + "p10": 0.05254, + "median": 0.151899, + "p90": 0.304, + "max": 0.333333 + } + }, + { + "id": "distribution.shaft.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 39, + "confidence": 1.0, + "distribution": { + "sample_count": 39, + "min": 0.03871, + "p10": 0.05254, + "median": 0.15, + "p90": 0.288571, + "max": 0.333333 + } + }, + { + "id": "distribution.shaft.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 39, + "confidence": 1.0, + "distribution": { + "sample_count": 39, + "min": 0.084584, + "p10": 0.310933, + "median": 0.917008, + "p90": 0.963947, + "max": 0.990099 + } + }, + { + "id": "distribution.shaft.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 39, + "confidence": 1.0, + "distribution": { + "sample_count": 39, + "min": 0.13125, + "p10": 0.351962, + "median": 0.5, + "p90": 0.5, + "max": 0.5 + } + }, + { + "id": "distribution.shaft.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 39, + "confidence": 1.0, + "distribution": { + "sample_count": 39, + "min": 0.009901, + "p10": 0.023093, + "median": 0.069354, + "p90": 0.589173, + "max": 0.908523 + } + }, + { + "id": "distribution.shaft.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "shaft" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 39, + "confidence": 1.0, + "distribution": { + "sample_count": 39, + "min": 0.2, + "p10": 0.3265, + "median": 0.666667, + "p90": 0.94, + "max": 0.96 + } + }, + { + "id": "motif.flanged_rotational_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 52, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 28, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [] + }, + "guidance": "Organize repeated axial holes as one circular pattern referenced to a semantic pattern center.", + "semantic_rationale": "Pattern-level representation keeps count, radius role, and radial placement coherent across dimensional variants.", + "support": 45, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.rotational_body_and_flange_like_rotational_body", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "flange_like_rotational_body", + "rotational_body" + ], + "relations": [] + }, + "guidance": "Separate the flange-like region from the remaining rotational profile while retaining their shared axis.", + "semantic_rationale": "Role separation permits flange and hub edits without losing rotational continuity.", + "support": 32, + "confidence": 1.0 + }, + { + "id": "motif.flanged_rotational_part.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change.", + "support": 52, + "confidence": 1.0 + }, + { + "id": "motif.hollow_or_stepped_shaft.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 44, + "confidence": 1.0 + }, + { + "id": "motif.hollow_or_stepped_shaft.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change.", + "support": 44, + "confidence": 1.0 + }, + { + "id": "motif.hollow_or_stepped_shaft.shaft_like_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "shaft_like_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Model a shaft-like body through named axial sections and shoulders.", + "semantic_rationale": "Named sections make length and diameter edits local while preserving the overall shaft topology.", + "support": 44, + "confidence": 1.0 + }, + { + "id": "motif.multi_axis_manifold.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families.", + "support": 52, + "confidence": 1.0 + }, + { + "id": "motif.multi_axis_manifold.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 42, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families.", + "support": 74, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 103, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 192, + "confidence": 1.0 + }, + { + "id": "motif.patterned_plate.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "patterned_plate" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [] + }, + "guidance": "Organize repeated axial holes as one circular pattern referenced to a semantic pattern center.", + "semantic_rationale": "Pattern-level representation keeps count, radius role, and radial placement coherent across dimensional variants.", + "support": 62, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families.", + "support": 149, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 158, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 306, + "confidence": 1.0 + }, + { + "id": "motif.patterned_prismatic_part.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "circular_equal_radius_pattern", + "repeated_axial_hole_pattern" + ], + "relations": [] + }, + "guidance": "Organize repeated axial holes as one circular pattern referenced to a semantic pattern center.", + "semantic_rationale": "Pattern-level representation keeps count, radius role, and radial placement coherent across dimensional variants.", + "support": 151, + "confidence": 1.0 + }, + { + "id": "motif.plate_or_bracket.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "plate_or_bracket" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 33, + "confidence": 1.0 + }, + { + "id": "motif.prismatic_block.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "prismatic_block" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 63, + "confidence": 1.0 + }, + { + "id": "motif.rotational_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 117, + "confidence": 1.0 + }, + { + "id": "motif.rotational_part.rotational_body_and_flange_like_rotational_body", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "flange_like_rotational_body", + "rotational_body" + ], + "relations": [] + }, + "guidance": "Separate the flange-like region from the remaining rotational profile while retaining their shared axis.", + "semantic_rationale": "Role separation permits flange and hub edits without losing rotational continuity.", + "support": 57, + "confidence": 1.0 + }, + { + "id": "motif.rotational_part.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change.", + "support": 117, + "confidence": 1.0 + }, + { + "id": "motif.rotational_part.rotational_body_and_toroidal_blend_or_groove", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "rotational_body", + "toroidal_blend_or_groove" + ], + "relations": [] + }, + "guidance": "Attach toroidal blend or groove candidates to the surrounding rotational profile rather than treating them as free geometry.", + "semantic_rationale": "Referencing the host profile preserves the local transition role when the host diameter changes.", + "support": 32, + "confidence": 1.0 + }, + { + "id": "motif.shaft.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 39, + "confidence": 1.0 + }, + { + "id": "motif.shaft.rotational_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "rotational_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Represent a rotational body as an ordered semantic profile of coaxial sections.", + "semantic_rationale": "A profile-level representation preserves shaft and hub topology when section proportions change.", + "support": 39, + "confidence": 1.0 + }, + { + "id": "motif.shaft.shaft_like_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "shaft" + ], + "when": { + "features": [ + "shaft_like_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Model a shaft-like body through named axial sections and shoulders.", + "semantic_rationale": "Named sections make length and diameter edits local while preserving the overall shaft topology.", + "support": 39, + "confidence": 1.0 + } + ], + "candidate_experiences": [ + { + "id": "constraint.flanged_rotational_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "support": 15, + "confidence": 1.0, + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 5, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.general_mechanical_part.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "general_mechanical_part" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 8, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 12, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.hollow_or_stepped_shaft.multiple_axis_system", + "kind": "constraint", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "support": 14, + "confidence": 1.0, + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 6, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.multi_axis_manifold.shared_passage_axis", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "stepped_cylindrical_passage" + ], + "relations": [ + "shared_passage_axis" + ] + }, + "guidance": "Preserve a common semantic axis through all stages of a stepped cylindrical passage.", + "semantic_rationale": "The relation maintains passage continuity independently of stage diameters and depths.", + "support": 19, + "confidence": 1.0, + "check": "Confirm that the shared passage axis relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.multi_axis_manifold.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 9, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 11, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.prismatic_block.transition_axis_continuity", + "kind": "constraint", + "scope": [ + "prismatic_block" + ], + "when": { + "features": [ + "conical_transition" + ], + "relations": [ + "transition_axis_continuity" + ] + }, + "guidance": "Keep conical transitions axis-continuous with their participating analytic regions.", + "semantic_rationale": "Axis continuity prevents eccentric transitions without fixing any source dimension.", + "support": 17, + "confidence": 1.0, + "check": "Confirm that the transition axis continuity relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 3, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.rotational_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent each passage direction as a distinct axis system and validate their intended intersections.", + "semantic_rationale": "Explicit axis systems preserve multi-direction passage intent during local edits.", + "support": 13, + "confidence": 1.0, + "check": "Confirm that the multiple axis system relationship remains valid after creation or modification.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 7, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.general_mechanical_part.bbox_middle_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.general_mechanical_part.bbox_short_to_long", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.general_mechanical_part.cylindrical_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.general_mechanical_part.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 17, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 3, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.general_mechanical_part.planar_area_fraction", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.general_mechanical_part.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "general_mechanical_part" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 17, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 3, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.patterned_plate.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.patterned_plate.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "patterned_plate" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.plate_or_bracket.pattern_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 9, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 11, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.plate_or_bracket.repeated_feature_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "plate_or_bracket" + ], + "when": { + "ratio": { + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 9, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 11, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.prismatic_block.largest_coaxial_radius_to_transverse_span", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "largest_coaxial_radius", + "denominator_role": "transverse_bounding_span" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "distribution.prismatic_block.smallest_to_largest_coaxial_radius", + "kind": "dimensionless_distribution", + "scope": [ + "prismatic_block" + ], + "when": { + "ratio": { + "numerator_role": "smallest_coaxial_radius", + "denominator_role": "largest_coaxial_radius" + } + }, + "guidance": "Use the family-conditioned dimensionless distribution only when the user has not supplied the corresponding relationship.", + "semantic_rationale": "A ratio distribution transfers scale-independent proportion evidence without exposing source dimensions.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.flanged_rotational_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families.", + "support": 15, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 5, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.flanged_rotational_part.rotational_body_and_toroidal_blend_or_groove", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "rotational_body", + "toroidal_blend_or_groove" + ], + "relations": [] + }, + "guidance": "Attach toroidal blend or groove candidates to the surrounding rotational profile rather than treating them as free geometry.", + "semantic_rationale": "Referencing the host profile preserves the local transition role when the host diameter changes.", + "support": 8, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 12, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.flanged_rotational_part.shaft_like_body_and_stepped_rotational_profile", + "kind": "feature_motif", + "scope": [ + "flanged_rotational_part" + ], + "when": { + "features": [ + "shaft_like_body", + "stepped_rotational_profile" + ], + "relations": [] + }, + "guidance": "Model a shaft-like body through named axial sections and shoulders.", + "semantic_rationale": "Named sections make length and diameter edits local while preserving the overall shaft topology.", + "support": 9, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 11, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.hollow_or_stepped_shaft.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families.", + "support": 14, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 6, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.hollow_or_stepped_shaft.rotational_body_and_toroidal_blend_or_groove", + "kind": "feature_motif", + "scope": [ + "hollow_or_stepped_shaft" + ], + "when": { + "features": [ + "rotational_body", + "toroidal_blend_or_groove" + ], + "relations": [] + }, + "guidance": "Attach toroidal blend or groove candidates to the surrounding rotational profile rather than treating them as free geometry.", + "semantic_rationale": "Referencing the host profile preserves the local transition role when the host diameter changes.", + "support": 15, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 5, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.multi_axis_manifold.cylindrical_feature_network_and_stepped_cylindrical_passage", + "kind": "feature_motif", + "scope": [ + "multi_axis_manifold" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "stepped_cylindrical_passage" + ], + "relations": [] + }, + "guidance": "Represent stepped cylindrical passages as one axis-related feature group rather than unrelated cylindrical cuts.", + "semantic_rationale": "Grouping the passage stages preserves their shared functional axis while allowing individual stage proportions to change.", + "support": 19, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 1, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.rotational_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [] + }, + "guidance": "Partition cylindrical passages by axis system before constructing or modifying their intersections.", + "semantic_rationale": "Axis-system partitioning reduces unintended coupling between independent passage families.", + "support": 13, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 7, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.rotational_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "rotational_part" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat major planar levels as named structural regions of the prismatic body.", + "semantic_rationale": "Named planar regions preserve plate, pocket, and shoulder relationships during edits.", + "support": 8, + "confidence": 1.0, + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 12, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + } + ] +} diff --git a/cad-experience-plugin/benchmarks/single-015133/experience-draft.json b/cad-experience-plugin/benchmarks/single-015133/experience-draft.json new file mode 100644 index 0000000..2b6e6df --- /dev/null +++ b/cad-experience-plugin/benchmarks/single-015133/experience-draft.json @@ -0,0 +1,71 @@ +{ + "schema_version": "1.0", + "draft_kind": "llm_generalized_experience_proposals", + "proposals": [ + { + "id": "constraint.patterned_prismatic_part.repeated_radius_group", + "kind": "constraint", + "scope": ["patterned_prismatic_part"], + "evidence_query": { + "required_features": ["repeated_axial_hole_pattern"], + "required_relations": ["repeated_radius_group"], + "context_features": ["repeated_axial_hole_pattern"] + }, + "guidance": "Preserve one shared semantic radius role across members of a repeated axial feature group.", + "semantic_rationale": "The equality relationship transfers across different overall sizes and placements.", + "check": "Confirm that repeated members intended to be equal retain the same radius role." + }, + { + "id": "constraint.patterned_prismatic_part.multiple_axis_system", + "kind": "constraint", + "scope": ["patterned_prismatic_part"], + "evidence_query": { + "required_features": ["cylindrical_feature_network", "multi_axis_passage"], + "required_relations": ["multiple_axis_system"], + "context_features": ["cylindrical_feature_network", "multi_axis_passage"] + }, + "guidance": "Represent passage directions as explicit semantic axes instead of unrelated local cuts.", + "semantic_rationale": "An explicit axis network preserves relationships when the body dimensions change.", + "check": "Confirm that every passage remains assigned to its intended axis system." + }, + { + "id": "motif.patterned_prismatic_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": ["patterned_prismatic_part"], + "evidence_query": { + "required_features": ["multi_level_planar_profile", "planar_dominant_body"], + "required_relations": [], + "context_features": ["multi_level_planar_profile", "planar_dominant_body"] + }, + "guidance": "Treat the planar-dominant body and its multiple profile levels as one reusable structural composition.", + "semantic_rationale": "The composition describes topology without depending on an instance dimension.", + "check": "Confirm that the expected planar levels remain distinct and connected to the primary body." + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": ["patterned_prismatic_part"], + "evidence_query": { + "required_features": ["cylindrical_feature_network", "multi_axis_passage"], + "required_relations": ["multiple_axis_system"], + "context_features": ["cylindrical_feature_network", "multi_axis_passage"] + }, + "guidance": "Model the cylindrical feature network and multi-axis passages as a coordinated feature system.", + "semantic_rationale": "The coordinated system transfers across variants while preserving semantic axes.", + "check": "Confirm that the cylindrical features and passages preserve their intended axis memberships." + }, + { + "id": "validation.patterned_prismatic_part.repeated_feature_consistency", + "kind": "validation_rule", + "scope": ["patterned_prismatic_part"], + "evidence_query": { + "required_features": ["repeated_axial_hole_pattern"], + "required_relations": ["repeated_radius_group"], + "context_features": ["repeated_axial_hole_pattern"] + }, + "guidance": "Validate repeated feature membership, equality roles, and attachment to the intended host region.", + "semantic_rationale": "These checks remain meaningful across dimensions and pattern placements.", + "check": "Verify repeated-member count consistency, shared radius roles, and host connectivity." + } + ] +} diff --git a/cad-experience-plugin/benchmarks/single-015133/report.md b/cad-experience-plugin/benchmarks/single-015133/report.md new file mode 100644 index 0000000..21fc895 --- /dev/null +++ b/cad-experience-plugin/benchmarks/single-015133/report.md @@ -0,0 +1,33 @@ +# 015133 单件高纯度蒸馏实验 + +## 对照结果 + +| 路线 | 可用于生成的信息 | 严格相似度 | 结论 | +|---|---|---:|---| +| 纯图片生成 | 多视图图片与文字描述 | 54.71 | 主体类别可辨认,但尺寸、孔系和端部结构损失明显 | +| 正式单件通用蒸馏 | 净化后的通用特征、关系和比例角色 | 无可执行提升 | 产生 5 条候选、0 条推广经验;单件证据不能证明通用性 | +| 专属 `exact_step_base` 模板 | 原始 B-Rep、哈希、几何签名与 JSON 修改层 | 100.00 | STEP 字节一致,199 面、488 边、体积与包围盒完全一致 | + +## 源几何 + +- 包围盒:213.5 × 78.5 × 76.0 mm +- 实体数:1 +- 面数:199 +- 边数:488 +- 体积:1174741.621833 mm³ +- 解析曲面:79 个平面、80 个圆柱面、40 个圆锥面 + +## 单件通用蒸馏实际保留的信息 + +- 7 个粗粒度特征类别 +- 2 个关系 +- 6 个无量纲比例角色 +- 不保留孔数量、孔中心、端板槽、各圆柱面的语义身份、具体尺寸或构造顺序 + +这些信息适合积累跨零件经验,但不足以重建一个具有 199 个面的指定实例。 + +## 结论 + +“单件高纯度”要达到 90 分以上,必须进入**实例模板/逆向参数化** +通道,而不是通用经验通道。通用经验负责教方法;实例模板负责保存指定 +零件的尺寸、特征图和精确基体。两类数据必须分库、分权限、分评价口径。 diff --git a/cad-experience-plugin/benchmarks/single-015133/semantic-batch.json b/cad-experience-plugin/benchmarks/single-015133/semantic-batch.json new file mode 100644 index 0000000..c04a831 --- /dev/null +++ b/cad-experience-plugin/benchmarks/single-015133/semantic-batch.json @@ -0,0 +1,231 @@ +{ + "schema_version": "1.0", + "batch_kind": "sanitized_cad_semantic_review", + "guardrails": { + "contains_instance_dimensions": false, + "contains_absolute_coordinates": false, + "llm_must_propose_methods_not_copy_answers": true, + "proposals_require_deterministic_evidence_verification": true + }, + "corpus_summary": { + "case_count": 1, + "family_count": 1 + }, + "cases": [ + { + "anonymous_case": "case_000001", + "family": "patterned_prismatic_part", + "features": [ + "cylindrical_feature_network", + "high_topological_complexity", + "moderate_aspect_body", + "multi_axis_passage", + "multi_level_planar_profile", + "planar_dominant_body", + "repeated_axial_hole_pattern" + ], + "relations": [ + "multiple_axis_system", + "repeated_radius_group" + ], + "candidate_rules": [], + "validation_targets": [], + "dimensionless_observation_roles": [ + { + "name": "bbox_short_to_long", + "numerator_role": "short_bounding_span", + "denominator_role": "long_bounding_span" + }, + { + "name": "bbox_middle_to_long", + "numerator_role": "middle_bounding_span", + "denominator_role": "long_bounding_span" + }, + { + "name": "planar_area_fraction", + "numerator_role": "planar_surface_area", + "denominator_role": "total_surface_area" + }, + { + "name": "cylindrical_area_fraction", + "numerator_role": "cylindrical_surface_area", + "denominator_role": "total_surface_area" + }, + { + "name": "pattern_radius_to_transverse_span", + "numerator_role": "pattern_radius", + "denominator_role": "transverse_bounding_span" + }, + { + "name": "repeated_feature_radius_to_transverse_span", + "numerator_role": "repeated_feature_radius", + "denominator_role": "transverse_bounding_span" + } + ] + } + ], + "existing_vocabulary": { + "promoted_ids": [ + "constraint.flanged_rotational_part.coaxial_stack", + "constraint.flanged_rotational_part.common_pattern_radius", + "constraint.flanged_rotational_part.dominant_axis_alignment", + "constraint.flanged_rotational_part.repeated_radius_group", + "constraint.flanged_rotational_part.shared_passage_axis", + "constraint.flanged_rotational_part.transition_axis_continuity", + "constraint.hollow_or_stepped_shaft.coaxial_stack", + "constraint.hollow_or_stepped_shaft.dominant_axis_alignment", + "constraint.hollow_or_stepped_shaft.shared_passage_axis", + "constraint.hollow_or_stepped_shaft.transition_axis_continuity", + "constraint.multi_axis_manifold.multiple_axis_system", + "constraint.multi_axis_manifold.shared_passage_axis", + "constraint.patterned_plate.common_pattern_radius", + "constraint.patterned_plate.multiple_axis_system", + "constraint.patterned_plate.repeated_radius_group", + "constraint.patterned_plate.shared_passage_axis", + "constraint.patterned_plate.transition_axis_continuity", + "constraint.patterned_prismatic_part.common_pattern_radius", + "constraint.patterned_prismatic_part.multiple_axis_system", + "constraint.patterned_prismatic_part.repeated_radius_group", + "constraint.patterned_prismatic_part.shared_passage_axis", + "constraint.patterned_prismatic_part.transition_axis_continuity", + "constraint.rotational_part.coaxial_stack", + "constraint.rotational_part.dominant_axis_alignment", + "constraint.rotational_part.shared_passage_axis", + "constraint.rotational_part.transition_axis_continuity", + "constraint.shaft.coaxial_stack", + "constraint.shaft.dominant_axis_alignment", + "constraint.shaft.shared_passage_axis", + "constraint.shaft.transition_axis_continuity", + "distribution.flanged_rotational_part.bbox_middle_to_long", + "distribution.flanged_rotational_part.bbox_short_to_long", + "distribution.flanged_rotational_part.cylindrical_area_fraction", + "distribution.flanged_rotational_part.largest_coaxial_radius_to_transverse_span", + "distribution.flanged_rotational_part.pattern_radius_to_transverse_span", + "distribution.flanged_rotational_part.planar_area_fraction", + "distribution.flanged_rotational_part.repeated_feature_radius_to_transverse_span", + "distribution.flanged_rotational_part.smallest_to_largest_coaxial_radius", + "distribution.general_mechanical_part.bbox_middle_to_long", + "distribution.general_mechanical_part.bbox_short_to_long", + "distribution.general_mechanical_part.cylindrical_area_fraction", + "distribution.general_mechanical_part.planar_area_fraction", + "distribution.hollow_or_stepped_shaft.bbox_middle_to_long", + "distribution.hollow_or_stepped_shaft.bbox_short_to_long", + "distribution.hollow_or_stepped_shaft.cylindrical_area_fraction", + "distribution.hollow_or_stepped_shaft.largest_coaxial_radius_to_transverse_span", + "distribution.hollow_or_stepped_shaft.planar_area_fraction", + "distribution.hollow_or_stepped_shaft.smallest_to_largest_coaxial_radius", + "distribution.multi_axis_manifold.bbox_middle_to_long", + "distribution.multi_axis_manifold.bbox_short_to_long", + "distribution.multi_axis_manifold.cylindrical_area_fraction", + "distribution.multi_axis_manifold.pattern_radius_to_transverse_span", + "distribution.multi_axis_manifold.planar_area_fraction", + "distribution.multi_axis_manifold.repeated_feature_radius_to_transverse_span", + "distribution.patterned_plate.bbox_middle_to_long", + "distribution.patterned_plate.bbox_short_to_long", + "distribution.patterned_plate.cylindrical_area_fraction", + "distribution.patterned_plate.pattern_radius_to_transverse_span", + "distribution.patterned_plate.planar_area_fraction", + "distribution.patterned_plate.repeated_feature_radius_to_transverse_span", + "distribution.patterned_prismatic_part.bbox_middle_to_long", + "distribution.patterned_prismatic_part.bbox_short_to_long", + "distribution.patterned_prismatic_part.cylindrical_area_fraction", + "distribution.patterned_prismatic_part.largest_coaxial_radius_to_transverse_span", + "distribution.patterned_prismatic_part.pattern_radius_to_transverse_span", + "distribution.patterned_prismatic_part.planar_area_fraction", + "distribution.patterned_prismatic_part.repeated_feature_radius_to_transverse_span", + "distribution.patterned_prismatic_part.smallest_to_largest_coaxial_radius", + "distribution.plate_or_bracket.bbox_middle_to_long", + "distribution.plate_or_bracket.bbox_short_to_long", + "distribution.plate_or_bracket.cylindrical_area_fraction", + "distribution.plate_or_bracket.planar_area_fraction", + "distribution.prismatic_block.bbox_middle_to_long", + "distribution.prismatic_block.bbox_short_to_long", + "distribution.prismatic_block.cylindrical_area_fraction", + "distribution.prismatic_block.pattern_radius_to_transverse_span", + "distribution.prismatic_block.planar_area_fraction", + "distribution.prismatic_block.repeated_feature_radius_to_transverse_span", + "distribution.rotational_part.bbox_middle_to_long", + "distribution.rotational_part.bbox_short_to_long", + "distribution.rotational_part.cylindrical_area_fraction", + "distribution.rotational_part.largest_coaxial_radius_to_transverse_span", + "distribution.rotational_part.planar_area_fraction", + "distribution.rotational_part.smallest_to_largest_coaxial_radius", + "distribution.shaft.bbox_middle_to_long", + "distribution.shaft.bbox_short_to_long", + "distribution.shaft.cylindrical_area_fraction", + "distribution.shaft.largest_coaxial_radius_to_transverse_span", + "distribution.shaft.planar_area_fraction", + "distribution.shaft.smallest_to_largest_coaxial_radius", + "motif.flanged_rotational_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.flanged_rotational_part.multi_level_planar_profile_and_planar_dominant_body", + "motif.flanged_rotational_part.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "motif.flanged_rotational_part.rotational_body_and_flange_like_rotational_body", + "motif.flanged_rotational_part.rotational_body_and_stepped_rotational_profile", + "motif.hollow_or_stepped_shaft.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.hollow_or_stepped_shaft.rotational_body_and_stepped_rotational_profile", + "motif.hollow_or_stepped_shaft.shaft_like_body_and_stepped_rotational_profile", + "motif.multi_axis_manifold.cylindrical_feature_network_and_multi_axis_passage", + "motif.multi_axis_manifold.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.multi_axis_manifold.multi_level_planar_profile_and_planar_dominant_body", + "motif.patterned_plate.cylindrical_feature_network_and_multi_axis_passage", + "motif.patterned_plate.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.patterned_plate.multi_level_planar_profile_and_planar_dominant_body", + "motif.patterned_plate.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "motif.patterned_prismatic_part.cylindrical_feature_network_and_multi_axis_passage", + "motif.patterned_prismatic_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.patterned_prismatic_part.multi_level_planar_profile_and_planar_dominant_body", + "motif.patterned_prismatic_part.repeated_axial_hole_pattern_and_circular_equal_radius_pattern", + "motif.plate_or_bracket.multi_level_planar_profile_and_planar_dominant_body", + "motif.prismatic_block.multi_level_planar_profile_and_planar_dominant_body", + "motif.rotational_part.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.rotational_part.rotational_body_and_flange_like_rotational_body", + "motif.rotational_part.rotational_body_and_stepped_rotational_profile", + "motif.rotational_part.rotational_body_and_toroidal_blend_or_groove", + "motif.shaft.cylindrical_feature_network_and_stepped_cylindrical_passage", + "motif.shaft.rotational_body_and_stepped_rotational_profile", + "motif.shaft.shaft_like_body_and_stepped_rotational_profile" + ], + "candidate_ids": [ + "constraint.flanged_rotational_part.multiple_axis_system", + "constraint.general_mechanical_part.transition_axis_continuity", + "constraint.hollow_or_stepped_shaft.multiple_axis_system", + "constraint.multi_axis_manifold.transition_axis_continuity", + "constraint.prismatic_block.transition_axis_continuity", + "constraint.rotational_part.multiple_axis_system", + "distribution.general_mechanical_part.largest_coaxial_radius_to_transverse_span", + "distribution.general_mechanical_part.smallest_to_largest_coaxial_radius", + "distribution.patterned_plate.largest_coaxial_radius_to_transverse_span", + "distribution.patterned_plate.smallest_to_largest_coaxial_radius", + "distribution.plate_or_bracket.pattern_radius_to_transverse_span", + "distribution.plate_or_bracket.repeated_feature_radius_to_transverse_span", + "distribution.prismatic_block.largest_coaxial_radius_to_transverse_span", + "distribution.prismatic_block.smallest_to_largest_coaxial_radius", + "motif.flanged_rotational_part.cylindrical_feature_network_and_multi_axis_passage", + "motif.flanged_rotational_part.rotational_body_and_toroidal_blend_or_groove", + "motif.flanged_rotational_part.shaft_like_body_and_stepped_rotational_profile", + "motif.hollow_or_stepped_shaft.cylindrical_feature_network_and_multi_axis_passage", + "motif.hollow_or_stepped_shaft.rotational_body_and_toroidal_blend_or_groove", + "motif.rotational_part.cylindrical_feature_network_and_multi_axis_passage", + "motif.rotational_part.multi_level_planar_profile_and_planar_dominant_body" + ] + }, + "draft_contract": { + "draft_kind": "llm_generalized_experience_proposals", + "proposal_required_fields": [ + "id", + "kind", + "scope", + "evidence_query", + "guidance", + "semantic_rationale" + ], + "allowed_kinds": [ + "feature_motif", + "constraint", + "design_rule", + "validation_rule", + "failure_repair", + "dimensionless_distribution" + ] + } +} diff --git a/cad-experience-plugin/benchmarks/single-015133/single-case-library.json b/cad-experience-plugin/benchmarks/single-015133/single-case-library.json new file mode 100644 index 0000000..50ce4e1 --- /dev/null +++ b/cad-experience-plugin/benchmarks/single-015133/single-case-library.json @@ -0,0 +1,173 @@ +{ + "schema_version": "2.0", + "library_kind": "generalized_cad_experience", + "generated_at": "2026-07-23T11:21:55+00:00", + "induction_mode": "llm_proposals_with_deterministic_evidence_verification", + "status": "collecting_evidence", + "policy": { + "instance_parameters_allowed": false, + "absolute_coordinates_allowed": false, + "single_case_promotion_allowed": false, + "llm_semantic_review_required": true, + "draft_evidence_verified": true, + "router_consumable": true, + "minimum_support": 20, + "minimum_confidence": 0.8 + }, + "corpus_summary": { + "accepted_case_count": 1, + "duplicate_case_count": 0, + "rejected_case_count": 0, + "family_count": 1 + }, + "experience_summary": { + "llm_proposal_count": 5, + "promoted_experience_count": 0, + "candidate_experience_count": 5, + "by_kind": [], + "by_scope": [], + "candidate_by_kind": [ + { + "kind": "constraint", + "count": 2 + }, + { + "kind": "feature_motif", + "count": 2 + }, + { + "kind": "validation_rule", + "count": 1 + } + ] + }, + "experiences": [], + "candidate_experiences": [ + { + "id": "constraint.patterned_prismatic_part.multiple_axis_system", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Represent passage directions as explicit semantic axes instead of unrelated local cuts.", + "semantic_rationale": "An explicit axis network preserves relationships when the body dimensions change.", + "support": 1, + "confidence": 1.0, + "check": "Confirm that every passage remains assigned to its intended axis system.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 19, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "constraint.patterned_prismatic_part.repeated_radius_group", + "kind": "constraint", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "repeated_axial_hole_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Preserve one shared semantic radius role across members of a repeated axial feature group.", + "semantic_rationale": "The equality relationship transfers across different overall sizes and placements.", + "support": 1, + "confidence": 1.0, + "check": "Confirm that repeated members intended to be equal retain the same radius role.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 19, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.patterned_prismatic_part.cylindrical_feature_network_and_multi_axis_passage", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "cylindrical_feature_network", + "multi_axis_passage" + ], + "relations": [ + "multiple_axis_system" + ] + }, + "guidance": "Model the cylindrical feature network and multi-axis passages as a coordinated feature system.", + "semantic_rationale": "The coordinated system transfers across variants while preserving semantic axes.", + "support": 1, + "confidence": 1.0, + "check": "Confirm that the cylindrical features and passages preserve their intended axis memberships.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 19, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "motif.patterned_prismatic_part.multi_level_planar_profile_and_planar_dominant_body", + "kind": "feature_motif", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "multi_level_planar_profile", + "planar_dominant_body" + ], + "relations": [] + }, + "guidance": "Treat the planar-dominant body and its multiple profile levels as one reusable structural composition.", + "semantic_rationale": "The composition describes topology without depending on an instance dimension.", + "support": 1, + "confidence": 1.0, + "check": "Confirm that the expected planar levels remain distinct and connected to the primary body.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 19, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + }, + { + "id": "validation.patterned_prismatic_part.repeated_feature_consistency", + "kind": "validation_rule", + "scope": [ + "patterned_prismatic_part" + ], + "when": { + "features": [ + "repeated_axial_hole_pattern" + ], + "relations": [ + "repeated_radius_group" + ] + }, + "guidance": "Validate repeated feature membership, equality roles, and attachment to the intended host region.", + "semantic_rationale": "These checks remain meaningful across dimensions and pattern placements.", + "support": 1, + "confidence": 1.0, + "check": "Verify repeated-member count consistency, shared radius roles, and host connectivity.", + "promotion_state": "candidate", + "required_support": 20, + "remaining_support": 19, + "required_confidence": 0.8, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router" + } + ] +} diff --git a/cad-experience-plugin/skills/cad-experience-builder/SKILL.md b/cad-experience-plugin/skills/cad-experience-builder/SKILL.md index 4476954..9523e6d 100644 --- a/cad-experience-plugin/skills/cad-experience-builder/SKILL.md +++ b/cad-experience-plugin/skills/cad-experience-builder/SKILL.md @@ -25,14 +25,18 @@ published library and require repeated support before promoting any experience. ``` 4. Read `work/review/semantic-batch.json` completely. Act as the semantic - distiller: compare families, features, relations, prior vocabulary, failure - rules, and validation targets; then author + distiller: compare families, parameter roles, datum roles, canonical + reconstruction stages, feature cardinality classes, relations, prior + vocabulary, failure rules, and validation targets; then author `work/review/experience-draft.json` using `references/llm-review-contract.md`. - Propose methods and invariants, not raw co-occurrence pairs. Do not infer - operation order, manufacturing intent, or failure causes that final B-Rep - evidence cannot support. + Propose methods, invariants, and family-level `reconstruction_grammar` + records, not raw co-occurrence pairs. A reconstruction grammar is a + canonical backend-neutral strategy inferred from repeated geometry; it is + not the source model's recovered feature history. Do not infer + manufacturing intent or failure causes that final B-Rep evidence cannot + support. 5. Publish only after the model draft exists: @@ -70,10 +74,15 @@ required. ## STEP extraction boundary -- The extractor reads analytic B-Rep surfaces, bounding boxes, topology counts, - axes, radii, and locations into a private evidence record. -- It derives only semantic candidates such as rotational stacks, axis - relationships, transitions, and repeated-hole motifs. +- The V2 extractor reads analytic B-Rep surfaces, bounding boxes, topology + counts, axes, radii, and locations into a private evidence record. +- It derives scale-independent body classes, part families, rotational and + prismatic motifs, passage and pattern relationships, complexity roles, and + normalized ratio observations before sanitization. +- It also derives private exact cardinality evidence plus sanitized symbolic + parameter roles, datum roles, feature-count classes, and canonical + reconstruction stages. These are the evidence used to learn how to rebuild a + family without publishing any teacher dimensions. - Exact source paths, hashes, dimensions, coordinates, and face references are permitted in private case JSON and forbidden in the published library. - Keep input/output as a human-visible staging area and induction corpus. Never @@ -86,7 +95,8 @@ required. - One case is evidence, not experience. - Publish generalized, dimension-free observations below threshold as visible `candidate_experiences`, but never expose them to generation consumers. -- Require `min_support` independent cases and `min_confidence` before promotion. +- Require `min_support` independent cases and conditional `min_confidence` + within the proposal's semantic context before promotion. - Promote feature compositions, semantic constraints, dimensionless distributions, validation checks, and failure-repair methods. - Reject absolute dimensions, coordinates, face references, source paths, @@ -105,6 +115,8 @@ The library is backend-neutral. A consumer receives: - `dimensionless_distribution`: multi-case ratio ranges; - `validation_rule`: checks independent of one part's coordinates; - `failure_repair`: reusable detection and repair methods. +- `reconstruction_grammar`: symbolic parameters, datums, canonical feature + stages, optional/required feature roles, and validation roles for a family. build123d, SimpleCADAPI, or another backend translates the same queried experience into its own operations. Experience must never contain executable diff --git a/cad-experience-plugin/skills/cad-experience-builder/agents/openai.yaml b/cad-experience-plugin/skills/cad-experience-builder/agents/openai.yaml index a87e5e0..213a77c 100644 --- a/cad-experience-plugin/skills/cad-experience-builder/agents/openai.yaml +++ b/cad-experience-plugin/skills/cad-experience-builder/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "CAD Experience Builder" - short_description: "用大模型语义复核与证据门禁蒸馏可迁移的 CAD 通用经验" - default_prompt: "Use $cad-experience-builder to prepare sanitized STEP evidence, perform model-led semantic induction, publish only evidence-verified generalized CAD experience, and report candidate versus promoted results." + short_description: "蒸馏可迁移的 CAD 重建语法、约束与验证经验" + default_prompt: "Use $cad-experience-builder to prepare sanitized STEP evidence, induce family-level symbolic reconstruction grammars and reusable methods, publish only deterministically verified experience, and report candidate versus promoted results." diff --git a/cad-experience-plugin/skills/cad-experience-builder/references/case-input-contract.md b/cad-experience-plugin/skills/cad-experience-builder/references/case-input-contract.md index d5edfc4..e0770cc 100644 --- a/cad-experience-plugin/skills/cad-experience-builder/references/case-input-contract.md +++ b/cad-experience-plugin/skills/cad-experience-builder/references/case-input-contract.md @@ -18,6 +18,25 @@ Supported fields: "part_family": "flanged_hub_adapter", "features": [{"type": "base_flange"}, {"type": "hollow_sleeve"}], "constraints": [{"id": "coaxial_stack", "type": "coaxial"}], + "reconstruction_evidence": { + "parameter_roles": ["overall_long_span", "coaxial_diameter_roles"], + "datum_roles": ["part_center", "primary_axis"], + "feature_roles": ["rotational_body", "coaxial_cylindrical_stack"], + "relation_roles": ["coaxial_stack"], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": ["part_center", "primary_axis"] + } + ], + "private_cardinality_evidence": { + "feature_role_count": 5, + "feature_role_count_class": "repeated" + }, + "validation_roles": ["closed_solid", "coaxial_stack"] + }, "normalized_observations": [ { "name": "sleeve_od_to_base_od", @@ -52,3 +71,11 @@ likely instance answers. Ratios are accepted only through Legacy distilled JSON can be used when it contains the fields above. Exact `parameters`, `evidence`, centers, axes, and surface references are ignored. + +V3 extractor output should also provide several independent semantic signals +per case: body-aspect class, topology-complexity role, analytic feature +networks, part family, relationship candidates, and role-named dimensionless +observations. It additionally provides symbolic reconstruction roles, canonical +reconstruction stages, and private exact cardinality evidence. Sanitization +converts exact counts into qualitative classes before LLM review. Do not encode +an absolute source dimension as a feature token. diff --git a/cad-experience-plugin/skills/cad-experience-builder/references/experience-library-schema.md b/cad-experience-plugin/skills/cad-experience-builder/references/experience-library-schema.md index ad9cc2f..387e3ed 100644 --- a/cad-experience-plugin/skills/cad-experience-builder/references/experience-library-schema.md +++ b/cad-experience-plugin/skills/cad-experience-builder/references/experience-library-schema.md @@ -62,3 +62,23 @@ case, positions, and coordinates are forbidden. Consumers query this library by requested family and feature vocabulary. They must not receive the induction corpus, private case JSON, or `candidate_experiences`. Only `experiences` is executable guidance. + +## Reconstruction grammar + +`reconstruction_grammar` is the executable bridge between generalized +experience and a backend-neutral design plan. It may contain only: + +- symbolic `parameter_roles`; +- symbolic `datum_roles`; +- required or optional semantic feature and relation roles; +- canonical stages with tokenized operation, feature, and reference roles; +- qualitative cardinality classes such as `single`, `paired`, `repeated`, or + `dense`; +- validation roles. + +It must not contain parameter values, exact member counts, absolute +coordinates, source identifiers, face references, or executable teacher +geometry. The publisher verifies that every promoted role and stage is present +in enough independent private cases. A consumer instantiates the grammar using +the user's dimensions, inferred dimensionless distributions, and backend +operations. diff --git a/cad-experience-plugin/skills/cad-experience-builder/references/llm-review-contract.md b/cad-experience-plugin/skills/cad-experience-builder/references/llm-review-contract.md index 12444e3..ec3906e 100644 --- a/cad-experience-plugin/skills/cad-experience-builder/references/llm-review-contract.md +++ b/cad-experience-plugin/skills/cad-experience-builder/references/llm-review-contract.md @@ -17,7 +17,8 @@ Write `work/review/experience-draft.json` with this shape: "scope": ["part_family"], "evidence_query": { "required_features": ["rotational_body", "central_passage"], - "required_relations": ["dominant_axis_alignment"] + "required_relations": ["dominant_axis_alignment"], + "context_features": ["rotational_body", "central_passage"] }, "guidance": "Preserve a shared axis across the participating feature roles.", "semantic_rationale": "The relationship remains meaningful when individual dimensions change.", @@ -28,17 +29,68 @@ Write `work/review/experience-draft.json` with this shape: ``` Allowed kinds are `feature_motif`, `constraint`, `design_rule`, -`validation_rule`, and `failure_repair`. +`validation_rule`, `failure_repair`, `dimensionless_distribution`, and +`reconstruction_grammar`. +Distribution proposals also provide `observation_name`; the publisher computes +the distribution from private evidence and never accepts values from the draft. + +A `reconstruction_grammar` proposal must additionally contain symbolic, +number-free reconstruction content: + +```json +{ + "id": "reconstruction.patterned_prismatic_part.primary", + "kind": "reconstruction_grammar", + "scope": ["patterned_prismatic_part"], + "evidence_query": { + "required_features": ["planar_dominant_body", "cylindrical_feature_network"], + "required_relations": [], + "context_features": ["planar_dominant_body"] + }, + "guidance": "Build a datum-driven envelope before adding the passage network and repeated features.", + "semantic_rationale": "The staged method transfers across dimensional variants while preserving feature relationships.", + "reconstruction_grammar": { + "parameter_roles": ["overall_long_span", "overall_middle_span", "overall_short_span"], + "datum_roles": ["part_center", "primary_axis", "primary_transverse_plane"], + "feature_roles": ["planar_dominant_body", "cylindrical_feature_network"], + "relation_roles": [], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": ["part_center", "primary_axis"] + }, + { + "id": "construct_primary_envelope", + "operation": "extrude_profile", + "feature_roles": ["planar_dominant_body"], + "reference_roles": ["part_center", "primary_axis"] + } + ], + "cardinality_classes": ["plane_dense", "cylinder_dense"], + "validation_roles": ["closed_solid", "surface_mix_consistency"] + } +} +``` Follow these rules: - Require an evidence query composed only of semantic feature and relation tokens present in the sanitized batch. - Use scopes to prevent unrelated part families from supporting a proposal. +- Use `context_features` to define the conditional population used for + confidence, so a valid subtype method is not diluted by unrelated variants + in the same broad family. - Explain why a proposal transfers across dimensions and variants. - Avoid dimensions, coordinates, source identifiers, face references, and numeric literals in prose. - Do not infer build order or causal failure mechanisms from final geometry. +- Treat `canonical_stages` as a recommended reconstruction order, never as a + claim about the source CAD history. +- For reconstruction grammars, retain only roles and stages repeated by the + scoped evidence. Do not insert family knowledge that the sanitized batch + does not support. - Prefer a small set of meaningful methods over every possible feature pair. - Preserve earlier proposal IDs when evidence still supports the same meaning. - Let the publisher calculate support and confidence; never claim them in the diff --git a/cad-experience-plugin/skills/cad-experience-builder/scripts/cad_experience.py b/cad-experience-plugin/skills/cad-experience-builder/scripts/cad_experience.py index 16887f4..eccec85 100755 --- a/cad-experience-plugin/skills/cad-experience-builder/scripts/cad_experience.py +++ b/cad-experience-plugin/skills/cad-experience-builder/scripts/cad_experience.py @@ -102,6 +102,71 @@ def list_tokens(value: Any) -> list[str]: return sorted({item for raw in value if (item := token(raw)) != "unknown"}) +def normalize_stage(raw: Any) -> dict[str, Any] | None: + if not isinstance(raw, dict): + return None + stage_id = token(raw.get("id")) + operation = token(raw.get("operation")) + if stage_id == "unknown" or operation == "unknown": + return None + return { + "id": stage_id, + "operation": operation, + "feature_roles": list_tokens(raw.get("feature_roles")), + "reference_roles": list_tokens(raw.get("reference_roles")), + } + + +def normalize_reconstruction(raw: Any) -> dict[str, Any]: + if not isinstance(raw, dict): + return { + "parameter_roles": [], + "datum_roles": [], + "feature_roles": [], + "relation_roles": [], + "canonical_stages": [], + "cardinality_classes": [], + "validation_roles": [], + } + private_cardinality = ( + raw.get("private_cardinality_evidence") + if isinstance(raw.get("private_cardinality_evidence"), dict) + else {} + ) + class_values = private_cardinality.get("surface_type_classes", {}) + cardinality_classes = list_tokens(raw.get("cardinality_classes")) + if isinstance(class_values, dict): + cardinality_classes.extend( + token(f"{kind}_{value}") + for kind, value in class_values.items() + ) + for role in ( + "feature_role_count_class", + "coaxial_radius_role_count_class", + "repeated_member_count_class", + ): + value = private_cardinality.get(role) + if value: + cardinality_classes.append(token(f"{role}.{value}")) + return { + "parameter_roles": list_tokens(raw.get("parameter_roles")), + "datum_roles": list_tokens(raw.get("datum_roles")), + "feature_roles": list_tokens(raw.get("feature_roles")), + "relation_roles": list_tokens(raw.get("relation_roles")), + "canonical_stages": [ + stage + for item in raw.get("canonical_stages", []) + if (stage := normalize_stage(item)) is not None + ] + if isinstance(raw.get("canonical_stages"), list) + else [], + "cardinality_classes": sorted( + {item for item in cardinality_classes if item != "unknown"} + ), + "validation_roles": list_tokens(raw.get("validation_roles")), + } + + def percentile(values: list[float], fraction: float) -> float: ordered = sorted(values) if len(ordered) == 1: @@ -232,6 +297,9 @@ def normalize_case(payload: dict[str, Any]) -> dict[str, Any]: "rules": rules, "validations": validations, "normalized_observations": observations, + "reconstruction": normalize_reconstruction( + design_ir.get("reconstruction_evidence") + ), } @@ -574,7 +642,10 @@ def extract_folder(input_dir: Path, output_dir: Path) -> dict[str, Any]: try: payload = json.loads(path.read_text(encoding="utf-8")) digest = payload.get("provenance", {}).get("source_sha256") - if isinstance(digest, str): + if ( + isinstance(digest, str) + and payload.get("extractor_version") == "3.0" + ): known_hashes[digest] = path except (OSError, json.JSONDecodeError, AttributeError): continue @@ -637,6 +708,7 @@ def prepare_semantic_batch( "relations": case["relations"], "candidate_rules": case["rules"], "validation_targets": case["validations"], + "reconstruction_evidence": case["reconstruction"], "dimensionless_observation_roles": [ { "name": item["name"], @@ -678,6 +750,8 @@ def prepare_semantic_batch( "design_rule", "validation_rule", "failure_repair", + "dimensionless_distribution", + "reconstruction_grammar", ], }, } @@ -693,6 +767,8 @@ def normalize_proposals(payload: dict[str, Any]) -> list[dict[str, Any]]: "design_rule", "validation_rule", "failure_repair", + "dimensionless_distribution", + "reconstruction_grammar", } for index, raw in enumerate(payload.get("proposals", [])): if not isinstance(raw, dict): @@ -707,6 +783,10 @@ def normalize_proposals(payload: dict[str, Any]) -> list[dict[str, Any]]: ) required_features = list_tokens(evidence.get("required_features")) required_relations = list_tokens(evidence.get("required_relations")) + context_features = list_tokens( + evidence.get("context_features") or required_features + ) + observation_name = token(raw.get("observation_name")) guidance = string_without_instance_numbers(raw.get("guidance")) rationale = string_without_instance_numbers(raw.get("semantic_rationale")) if proposal_id == "unknown": @@ -715,7 +795,11 @@ def normalize_proposals(payload: dict[str, Any]) -> list[dict[str, Any]]: raise ValueError(f"proposal {proposal_id} has an unsupported kind") if not scopes: raise ValueError(f"proposal {proposal_id} has no scope") - if not required_features and not required_relations: + if ( + not required_features + and not required_relations + and observation_name == "unknown" + ): raise ValueError(f"proposal {proposal_id} has no evidence query") if guidance is None or rationale is None: raise ValueError( @@ -728,10 +812,25 @@ def normalize_proposals(payload: dict[str, Any]) -> list[dict[str, Any]]: "evidence_query": { "required_features": required_features, "required_relations": required_relations, + "context_features": context_features, }, "guidance": guidance, "semantic_rationale": rationale, } + if observation_name != "unknown": + proposal["observation_name"] = observation_name + if kind == "reconstruction_grammar": + grammar = normalize_reconstruction(raw.get("reconstruction_grammar")) + if ( + not grammar["parameter_roles"] + or not grammar["datum_roles"] + or not grammar["canonical_stages"] + or not grammar["validation_roles"] + ): + raise ValueError( + f"proposal {proposal_id} has an incomplete reconstruction grammar" + ) + proposal["reconstruction_grammar"] = grammar for key in ("check", "repair", "failure_signature"): value = string_without_instance_numbers(raw.get(key)) if value is not None: @@ -752,6 +851,41 @@ def build_reviewed_library( promoted: list[dict[str, Any]] = [] candidates: list[dict[str, Any]] = [] family_counts = Counter(case["family"] for case in cases) + + def supports_reconstruction_grammar( + case: dict[str, Any], grammar: dict[str, Any] + ) -> bool: + evidence = case.get("reconstruction", {}) + for key in ( + "parameter_roles", + "datum_roles", + "feature_roles", + "relation_roles", + "validation_roles", + ): + required = set(grammar.get(key, [])) + available = set(evidence.get(key, [])) + if required and not required.issubset(available): + return False + available_stages = { + (item["id"], item["operation"]): item + for item in evidence.get("canonical_stages", []) + } + for required_stage in grammar.get("canonical_stages", []): + key = (required_stage["id"], required_stage["operation"]) + available = available_stages.get(key) + if available is None: + return False + if not set(required_stage.get("feature_roles", [])).issubset( + available.get("feature_roles", []) + ): + return False + if not set(required_stage.get("reference_roles", [])).issubset( + available.get("reference_roles", []) + ): + return False + return True + for proposal in proposals: scope = set(proposal["scope"]) relevant = [ @@ -765,14 +899,80 @@ def build_reviewed_library( required_relations = set( proposal["evidence_query"]["required_relations"] ) - supporting = [ + context_features = set( + proposal["evidence_query"].get("context_features", required_features) + ) + if proposal["kind"] == "dimensionless_distribution": + matched = [ + observation + for case in relevant + for observation in case["normalized_observations"] + if observation["name"] == proposal.get("observation_name") + ] + support = len(matched) + item: dict[str, Any] = { + "id": proposal["id"], + "kind": proposal["kind"], + "scope": proposal["scope"], + "when": { + "ratio": { + "numerator_role": ( + matched[0]["numerator_role"] if matched else "unknown" + ), + "denominator_role": ( + matched[0]["denominator_role"] if matched else "unknown" + ), + } + }, + "guidance": proposal["guidance"], + "semantic_rationale": proposal["semantic_rationale"], + "support": support, + "confidence": 1.0 if support else 0.0, + } + if matched: + values = [observation["value"] for observation in matched] + item["distribution"] = { + "sample_count": support, + "min": round(min(values), 6), + "p10": round(percentile(values, 0.10), 6), + "median": round(median(values), 6), + "p90": round(percentile(values, 0.90), 6), + "max": round(max(values), 6), + } + if eligible(support, item["confidence"], min_support, min_confidence): + promoted.append(item) + else: + item.pop("distribution", None) + item.update( + { + "promotion_state": "candidate", + "required_support": min_support, + "remaining_support": max(0, min_support - support), + "required_confidence": min_confidence, + "consumer_policy": "visible_for_review_but_not_available_to_cad_router", + } + ) + candidates.append(item) + continue + contextual = [ case for case in relevant + if context_features.issubset(case["features"]) + ] + supporting = [ + case + for case in contextual if required_features.issubset(case["features"]) and required_relations.issubset(case["relations"]) + and ( + proposal["kind"] != "reconstruction_grammar" + or supports_reconstruction_grammar( + case, proposal["reconstruction_grammar"] + ) + ) ] support = len(supporting) - confidence = support / len(relevant) if relevant else 0.0 + confidence = support / len(contextual) if contextual else 0.0 item: dict[str, Any] = { "id": proposal["id"], "kind": proposal["kind"], @@ -789,6 +989,8 @@ def build_reviewed_library( for key in ("check", "repair", "failure_signature"): if key in proposal: item[key] = proposal[key] + if proposal["kind"] == "reconstruction_grammar": + item["reconstruction_grammar"] = proposal["reconstruction_grammar"] if eligible(support, confidence, min_support, min_confidence): promoted.append(item) else: diff --git a/cad-experience-plugin/skills/cad-experience-builder/scripts/step_to_case.py b/cad-experience-plugin/skills/cad-experience-builder/scripts/step_to_case.py index cb8a1eb..dcb3836 100644 --- a/cad-experience-plugin/skills/cad-experience-builder/scripts/step_to_case.py +++ b/cad-experience-plugin/skills/cad-experience-builder/scripts/step_to_case.py @@ -11,6 +11,8 @@ from collections import Counter, defaultdict from pathlib import Path from typing import Any +EXTRACTOR_VERSION = "3.0" + def _rounded(value: float) -> float: return round(float(value), 6) @@ -42,6 +44,32 @@ def _radial_offset(location: list[float], center: list[float], axis: str) -> flo location[second] - center[second], ) +def _ratio(numerator: float, denominator: float) -> float: + return _rounded(numerator / denominator) if denominator > 1e-9 else 0.0 + + +def _observation( + name: str, value: float, numerator_role: str, denominator_role: str +) -> dict[str, Any]: + return { + "name": name, + "value": _rounded(value), + "numerator_role": numerator_role, + "denominator_role": denominator_role, + } + + +def _cardinality_class(count: int) -> str: + if count <= 0: + return "absent" + if count == 1: + return "single" + if count == 2: + return "paired" + if count <= 6: + return "repeated" + return "dense" + def _surface_record(face: Any, index: int) -> dict[str, Any]: from OCP.BRepAdaptor import BRepAdaptor_Surface @@ -125,6 +153,11 @@ def _semantic_summary( axis_votes[_canonical_axis(surface["axis"])] += max(surface["area"], 1.0) dominant_axis = axis_votes.most_common(1)[0][0] if axis_votes else "z" scale = max(bbox_size) if bbox_size else 1.0 + sorted_spans = sorted((max(float(value), 1e-9) for value in bbox_size)) + short_span, middle_span, long_span = sorted_spans + long_to_middle = _ratio(long_span, middle_span) + middle_to_short = _ratio(middle_span, short_span) + short_to_long = _ratio(short_span, long_span) coaxial_tolerance = max(scale * 0.005, 1e-4) parallel_cylinders: list[dict[str, Any]] = [] @@ -152,6 +185,9 @@ def _semantic_summary( repeated_group_sizes = sorted( (len(rows) for rows in repeated_groups.values()), reverse=True ) + largest_repeated_group = ( + max(repeated_groups.values(), key=len) if repeated_groups else [] + ) parallel_cones = [ item for item in cones if _canonical_axis(item["axis"]) == dominant_axis ] @@ -160,6 +196,66 @@ def _semantic_summary( has_multi_axis_passages = any( _canonical_axis(item["axis"]) != dominant_axis for item in cylinders ) + planes = [item for item in surfaces if item["type"] == "plane"] + toruses = [item for item in surfaces if item["type"] == "torus"] + spheres = [item for item in surfaces if item["type"] == "sphere"] + other_surfaces = [item for item in surfaces if item["type"] == "other"] + total_area = sum(max(float(item.get("area", 0.0)), 0.0) for item in surfaces) + planar_area = sum(max(float(item.get("area", 0.0)), 0.0) for item in planes) + cylindrical_area = sum( + max(float(item.get("area", 0.0)), 0.0) for item in cylinders + ) + planar_fraction = _ratio(planar_area, total_area) + cylindrical_fraction = _ratio(cylindrical_area, total_area) + elongated = long_to_middle >= 3.0 + plate_like = middle_to_short >= 4.0 and long_to_middle < 3.0 + compact = long_to_middle < 2.0 and middle_to_short < 2.5 + + transverse_axes = { + "x": (1, 2), + "y": (0, 2), + "z": (0, 1), + }[dominant_axis] + transverse_span = max(bbox_size[index] for index in transverse_axes) + dominant_span = bbox_size[{"x": 0, "y": 1, "z": 2}[dominant_axis]] + largest_coaxial_radius = max( + (surface["radius"] for surface in coaxial_cylinders), default=0.0 + ) + smallest_coaxial_radius = min( + (surface["radius"] for surface in coaxial_cylinders), default=0.0 + ) + + pattern_offsets = [ + _radial_offset(surface["location"], bbox_center, dominant_axis) + for surface in largest_repeated_group + ] + pattern_mean_offset = ( + sum(pattern_offsets) / len(pattern_offsets) if pattern_offsets else 0.0 + ) + pattern_spread = ( + max(pattern_offsets) - min(pattern_offsets) if pattern_offsets else 0.0 + ) + has_circular_pattern = bool( + len(pattern_offsets) >= 3 + and pattern_mean_offset > coaxial_tolerance + and pattern_spread / pattern_mean_offset <= 0.08 + ) + + # Multiple radii sharing a transverse center are a robust final-B-Rep + # signal for stepped bores/counterbores, without claiming operation order. + transverse_groups: dict[tuple[int, int, int], set[float]] = defaultdict(set) + axis_index = {"x": 0, "y": 1, "z": 2}[dominant_axis] + for surface in parallel_cylinders: + location = surface["location"] + key = ( + int(round(location[transverse_axes[0]] / max(coaxial_tolerance, 1e-6))), + int(round(location[transverse_axes[1]] / max(coaxial_tolerance, 1e-6))), + axis_index, + ) + transverse_groups[key].add(round(surface["radius"], 4)) + has_stepped_cylindrical_passage = any( + len(radii) >= 2 for radii in transverse_groups.values() + ) features: list[dict[str, Any]] = [] if has_rotational_stack: @@ -177,6 +273,51 @@ def _semantic_summary( features.append({"type": "repeated_axial_hole_pattern"}) if has_multi_axis_passages: features.append({"type": "multi_axis_passage"}) + features.append( + { + "type": ( + "elongated_body" + if elongated + else "plate_like_body" + if plate_like + else "compact_body" + if compact + else "moderate_aspect_body" + ) + } + ) + if planar_fraction >= 0.55: + features.append({"type": "planar_dominant_body"}) + if cylindrical_fraction >= 0.55: + features.append({"type": "cylindrical_dominant_body"}) + if len(planes) >= 6: + features.append({"type": "multi_level_planar_profile"}) + if len(cylinders) >= 2: + features.append({"type": "cylindrical_feature_network"}) + if toruses: + features.append({"type": "toroidal_blend_or_groove"}) + if spheres: + features.append({"type": "spherical_surface_feature"}) + if other_surfaces: + features.append({"type": "freeform_surface_region"}) + if has_circular_pattern: + features.append({"type": "circular_equal_radius_pattern"}) + if has_stepped_cylindrical_passage: + features.append({"type": "stepped_cylindrical_passage"}) + if has_rotational_stack: + features.append({"type": "stepped_rotational_profile"}) + if elongated: + features.append({"type": "shaft_like_body"}) + if len(distinct_coaxial_radii) >= 3 and elongated: + features.append({"type": "sleeve_or_hollow_shaft_candidate"}) + if transverse_span > 0 and dominant_span / transverse_span < 0.8: + features.append({"type": "flange_like_rotational_body"}) + if len(surfaces) >= 40: + features.append({"type": "high_topological_complexity"}) + elif len(surfaces) >= 16: + features.append({"type": "medium_topological_complexity"}) + else: + features.append({"type": "low_topological_complexity"}) constraints: list[dict[str, Any]] = [] if has_rotational_stack: @@ -194,15 +335,104 @@ def _semantic_summary( constraints.append( {"id": "transition_axis_continuity", "type": "axis_alignment"} ) + if has_circular_pattern: + constraints.append( + {"id": "common_pattern_radius", "type": "radial_pattern"} + ) + if has_multi_axis_passages: + constraints.append( + {"id": "multiple_axis_system", "type": "axis_network"} + ) + if has_stepped_cylindrical_passage: + constraints.append( + {"id": "shared_passage_axis", "type": "coaxial"} + ) - if has_rotational_stack and parallel_cones and has_repeated_axial_holes: - family = "flanged_hub_adapter_candidate" + if has_rotational_stack and has_repeated_axial_holes: + family = "flanged_rotational_part" + elif has_rotational_stack and elongated and len(distinct_coaxial_radii) >= 3: + family = "hollow_or_stepped_shaft" + elif has_rotational_stack and elongated: + family = "shaft" elif has_rotational_stack: - family = "rotational_part_candidate" + family = "rotational_part" + elif has_repeated_axial_holes and plate_like: + family = "patterned_plate" elif has_repeated_axial_holes: - family = "patterned_prismatic_part_candidate" + family = "patterned_prismatic_part" + elif has_multi_axis_passages and len(cylinders) >= 3: + family = "multi_axis_manifold" + elif plate_like and planes: + family = "plate_or_bracket" + elif planar_fraction >= 0.55: + family = "prismatic_block" + elif other_surfaces or toruses or spheres: + family = "hybrid_freeform_part" else: - family = "unclassified_part" + family = "general_mechanical_part" + + observations = [ + _observation( + "bbox_short_to_long", + short_to_long, + "short_bounding_span", + "long_bounding_span", + ), + _observation( + "bbox_middle_to_long", + _ratio(middle_span, long_span), + "middle_bounding_span", + "long_bounding_span", + ), + _observation( + "planar_area_fraction", + planar_fraction, + "planar_surface_area", + "total_surface_area", + ), + _observation( + "cylindrical_area_fraction", + cylindrical_fraction, + "cylindrical_surface_area", + "total_surface_area", + ), + ] + if largest_coaxial_radius > 0: + observations.append( + _observation( + "largest_coaxial_radius_to_transverse_span", + _ratio(largest_coaxial_radius, transverse_span), + "largest_coaxial_radius", + "transverse_bounding_span", + ) + ) + if smallest_coaxial_radius > 0 and largest_coaxial_radius > 0: + observations.append( + _observation( + "smallest_to_largest_coaxial_radius", + _ratio(smallest_coaxial_radius, largest_coaxial_radius), + "smallest_coaxial_radius", + "largest_coaxial_radius", + ) + ) + if pattern_mean_offset > 0: + observations.append( + _observation( + "pattern_radius_to_transverse_span", + _ratio(pattern_mean_offset, transverse_span), + "pattern_radius", + "transverse_bounding_span", + ) + ) + if largest_repeated_group: + observations.append( + _observation( + "repeated_feature_radius_to_transverse_span", + _ratio(largest_repeated_group[0]["radius"], transverse_span), + "repeated_feature_radius", + "transverse_bounding_span", + ) + ) summary = { "dominant_axis": dominant_axis, @@ -214,10 +444,240 @@ def _semantic_summary( else 0, "parallel_cone_surface_count": len(parallel_cones), "has_multi_axis_passages": has_multi_axis_passages, + "shape_class": ( + "elongated" if elongated else "plate_like" if plate_like else "compact" + if compact + else "moderate_aspect" + ), + "normalized_observations": observations, } return family, features, constraints, summary +def _reconstruction_evidence( + surfaces: list[dict[str, Any]], + features: list[dict[str, Any]], + constraints: list[dict[str, Any]], + semantic_summary: dict[str, Any], +) -> dict[str, Any]: + """Describe a canonical reconstruction grammar without claiming CAD history.""" + feature_roles = sorted( + { + str(item.get("type")) + for item in features + if isinstance(item, dict) and item.get("type") + } + ) + relation_roles = sorted( + { + str(item.get("id") or item.get("type")) + for item in constraints + if isinstance(item, dict) and (item.get("id") or item.get("type")) + } + ) + surface_counts = Counter(item.get("type", "other") for item in surfaces) + + parameter_roles = [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + "primary_axis_span", + "primary_transverse_span", + "secondary_transverse_span", + ] + if "cylindrical_feature_network" in feature_roles: + parameter_roles.extend( + [ + "passage_diameter_roles", + "passage_axis_offset_roles", + "passage_extent_roles", + ] + ) + if "coaxial_cylindrical_stack" in feature_roles: + parameter_roles.extend( + [ + "coaxial_diameter_roles", + "axial_segment_length_roles", + "shoulder_position_roles", + ] + ) + if "repeated_axial_hole_pattern" in feature_roles: + parameter_roles.extend( + [ + "pattern_member_diameter_role", + "pattern_radius_or_spacing_role", + "pattern_angular_or_linear_phase_role", + ] + ) + if "multi_level_planar_profile" in feature_roles: + parameter_roles.extend( + [ + "planar_level_offset_roles", + "profile_width_roles", + "profile_length_roles", + ] + ) + if "conical_transition" in feature_roles: + parameter_roles.append("transition_slope_role") + + stages: list[dict[str, Any]] = [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane", + ], + }, + { + "id": "construct_primary_envelope", + "operation": ( + "revolve_profile" + if "rotational_body" in feature_roles + else "extrude_profile" + ), + "feature_roles": [ + role + for role in ( + "rotational_body", + "planar_dominant_body", + "elongated_body", + "plate_like_body", + "compact_body", + "moderate_aspect_body", + ) + if role in feature_roles + ], + "reference_roles": ["part_center", "primary_axis"], + }, + ] + if "multi_level_planar_profile" in feature_roles: + stages.append( + { + "id": "establish_planar_levels", + "operation": "add_or_remove_profile_levels", + "feature_roles": ["multi_level_planar_profile"], + "reference_roles": ["primary_transverse_plane"], + } + ) + if "coaxial_cylindrical_stack" in feature_roles: + stages.append( + { + "id": "construct_coaxial_stack", + "operation": "add_or_cut_coaxial_profiles", + "feature_roles": ["coaxial_cylindrical_stack"], + "reference_roles": ["primary_axis"], + } + ) + if "cylindrical_feature_network" in feature_roles: + stages.append( + { + "id": "construct_passage_network", + "operation": "cut_semantic_passages", + "feature_roles": [ + role + for role in ( + "cylindrical_feature_network", + "central_passage_candidate", + "stepped_cylindrical_passage", + "multi_axis_passage", + ) + if role in feature_roles + ], + "reference_roles": ["primary_axis", "part_center"], + } + ) + if "repeated_axial_hole_pattern" in feature_roles: + stages.append( + { + "id": "construct_repeated_feature_pattern", + "operation": "pattern_semantic_feature", + "feature_roles": [ + role + for role in ( + "repeated_axial_hole_pattern", + "circular_equal_radius_pattern", + ) + if role in feature_roles + ], + "reference_roles": ["primary_axis", "part_center"], + } + ) + transition_roles = [ + role + for role in ( + "conical_transition", + "toroidal_blend_or_groove", + "spherical_surface_feature", + "freeform_surface_region", + ) + if role in feature_roles + ] + if transition_roles: + stages.append( + { + "id": "resolve_transitions_and_finishing", + "operation": "apply_transitions_or_blends", + "feature_roles": transition_roles, + "reference_roles": ["primary_axis"], + } + ) + stages.append( + { + "id": "validate_reconstructed_shape", + "operation": "validate_geometry_and_relations", + "feature_roles": feature_roles, + "reference_roles": ["part_center", "primary_axis"], + } + ) + + return { + "interpretation": "canonical_reconstruction_plan_not_recovered_history", + "parameter_roles": sorted(set(parameter_roles)), + "datum_roles": [ + "part_center", + "primary_axis", + "primary_transverse_plane", + "secondary_transverse_plane", + ], + "feature_roles": feature_roles, + "relation_roles": relation_roles, + "canonical_stages": stages, + "private_cardinality_evidence": { + "surface_type_counts": dict(sorted(surface_counts.items())), + "surface_type_classes": { + kind: _cardinality_class(count) + for kind, count in sorted(surface_counts.items()) + }, + "feature_role_count": len(feature_roles), + "feature_role_count_class": _cardinality_class(len(feature_roles)), + "coaxial_radius_role_count": int( + semantic_summary.get("distinct_coaxial_radius_count", 0) + ), + "coaxial_radius_role_count_class": _cardinality_class( + int(semantic_summary.get("distinct_coaxial_radius_count", 0)) + ), + "repeated_member_count": int( + semantic_summary.get("largest_repeated_radius_group", 0) + ), + "repeated_member_count_class": _cardinality_class( + int(semantic_summary.get("largest_repeated_radius_group", 0)) + ), + }, + "validation_roles": sorted( + { + "closed_solid", + "bounding_proportion_consistency", + "surface_mix_consistency", + *relation_roles, + } + ), + } + + def extract_step_case(source: Path) -> dict[str, Any]: from build123d import import_step @@ -239,9 +699,14 @@ def extract_step_case(source: Path) -> dict[str, Any]: family, features, constraints, semantic_summary = _semantic_summary( surfaces, bbox_center, bbox_size ) + reconstruction_evidence = _reconstruction_evidence( + surfaces, features, constraints, semantic_summary + ) digest = hashlib.sha256(source_bytes).hexdigest() + normalized_observations = semantic_summary.pop("normalized_observations", []) return { - "schema_version": "1.0", + "schema_version": "2.0", + "extractor_version": EXTRACTOR_VERSION, "case_kind": "private_step_evidence", "case_id": digest, "provenance": { @@ -267,8 +732,9 @@ def extract_step_case(source: Path) -> dict[str, Any]: "part_family": family, "features": features, "constraints": constraints, - "normalized_observations": [], + "normalized_observations": normalized_observations, "semantic_summary": semantic_summary, + "reconstruction_evidence": reconstruction_evidence, }, "experience": { "rules": [], diff --git a/cad-experience-plugin/tests/test_cad_experience.py b/cad-experience-plugin/tests/test_cad_experience.py index e6bbde4..b7e7bff 100644 --- a/cad-experience-plugin/tests/test_cad_experience.py +++ b/cad-experience-plugin/tests/test_cad_experience.py @@ -54,6 +54,38 @@ def make_case(index: int) -> dict: "denominator_role": "base_outer_diameter", } ], + "reconstruction_evidence": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + ], + "datum_roles": ["part_center", "primary_axis"], + "feature_roles": ["base_flange", "hollow_sleeve", "counterbore"], + "relation_roles": ["coaxial_stack"], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": ["part_center", "primary_axis"], + }, + { + "id": "construct_primary_envelope", + "operation": "revolve_profile", + "feature_roles": ["base_flange", "hollow_sleeve"], + "reference_roles": ["primary_axis"], + }, + ], + "private_cardinality_evidence": { + "surface_type_classes": { + "plane": "repeated", + "cylinder": "dense", + }, + "feature_role_count_class": "repeated", + }, + "validation_roles": ["closed_solid", "coaxial_stack"], + }, }, "experience": { "rules": [ @@ -122,12 +154,47 @@ class CadExperienceTest(unittest.TestCase): family, features, constraints, summary = STEP_MODULE._semantic_summary( surfaces, [0.0, 0.0, 0.0], [40.0, 20.0, 40.0] ) - self.assertEqual("flanged_hub_adapter_candidate", family) + self.assertEqual("flanged_rotational_part", family) self.assertIn( "coaxial_cylindrical_stack", {item["type"] for item in features} ) self.assertIn("coaxial_stack", {item["id"] for item in constraints}) self.assertEqual("y", summary["dominant_axis"]) + self.assertTrue(summary["normalized_observations"]) + reconstruction = STEP_MODULE._reconstruction_evidence( + surfaces, features, constraints, summary + ) + self.assertIn("overall_long_span", reconstruction["parameter_roles"]) + self.assertTrue(reconstruction["canonical_stages"]) + self.assertEqual( + "canonical_reconstruction_plan_not_recovered_history", + reconstruction["interpretation"], + ) + + def test_conditional_feature_context_does_not_dilute_valid_method(self): + proposal = self.reviewed_proposal() + proposal["evidence_query"]["context_features"] = [ + "base_flange", + "hollow_sleeve", + ] + target = [MODULE.normalize_case(make_case(index)) for index in range(20)] + unrelated = [] + for index in range(80): + payload = make_case(index + 100) + payload["design_ir"]["features"] = [{"type": "base_flange"}] + payload["design_ir"]["constraints"] = [] + unrelated.append(MODULE.normalize_case(payload)) + library = MODULE.build_reviewed_library( + target + unrelated, + [MODULE.normalize_proposals({ + "draft_kind": "llm_generalized_experience_proposals", + "proposals": [proposal], + })[0]], + 20, + 0.8, + {"duplicate_case_count": 0, "rejected_case_count": 0}, + ) + self.assertEqual(1, len(library["experiences"])) def test_single_case_never_promotes(self): case = MODULE.normalize_case(make_case(1)) @@ -247,6 +314,65 @@ class CadExperienceTest(unittest.TestCase): } ) + def test_reconstruction_grammar_promotes_without_instance_values(self): + proposal = { + "id": "reconstruction.flanged_hub_adapter.primary", + "kind": "reconstruction_grammar", + "scope": ["flanged_hub_adapter"], + "evidence_query": { + "required_features": ["base_flange", "hollow_sleeve"], + "required_relations": ["coaxial_stack"], + "context_features": ["base_flange", "hollow_sleeve"], + }, + "guidance": "Establish shared datums before composing the primary envelope.", + "semantic_rationale": "The symbolic stages transfer across dimensional variants.", + "reconstruction_grammar": { + "parameter_roles": [ + "overall_long_span", + "overall_middle_span", + "overall_short_span", + ], + "datum_roles": ["part_center", "primary_axis"], + "feature_roles": ["base_flange", "hollow_sleeve"], + "relation_roles": ["coaxial_stack"], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": ["part_center", "primary_axis"], + }, + { + "id": "construct_primary_envelope", + "operation": "revolve_profile", + "feature_roles": ["base_flange", "hollow_sleeve"], + "reference_roles": ["primary_axis"], + }, + ], + "cardinality_classes": ["plane_repeated", "cylinder_dense"], + "validation_roles": ["closed_solid", "coaxial_stack"], + }, + } + normalized = MODULE.normalize_proposals( + { + "draft_kind": "llm_generalized_experience_proposals", + "proposals": [proposal], + } + ) + cases = [MODULE.normalize_case(make_case(index)) for index in range(20)] + library = MODULE.build_reviewed_library( + cases, + normalized, + 20, + 0.8, + {"duplicate_case_count": 0, "rejected_case_count": 0}, + ) + self.assertEqual([], MODULE.audit_library(library)) + self.assertEqual("reconstruction_grammar", library["experiences"][0]["kind"]) + rendered = json.dumps(library) + self.assertNotIn("91.0", rendered) + self.assertNotIn('"parameters"', rendered) + def test_direct_distill_without_model_draft_is_blocked(self): with self.assertRaisesRegex(SystemExit, "statistical distillation is disabled"): MODULE.main(["distill"]) diff --git a/docs/CNC机床系统对接技术需求清单.docx b/docs/CNC机床系统对接技术需求清单.docx new file mode 100644 index 0000000..21de1c0 Binary files /dev/null and b/docs/CNC机床系统对接技术需求清单.docx differ diff --git a/docs/CNC机床系统对接技术需求清单.pdf b/docs/CNC机床系统对接技术需求清单.pdf new file mode 100644 index 0000000..93e48c6 Binary files /dev/null and b/docs/CNC机床系统对接技术需求清单.pdf differ diff --git a/docs/CNC机床系统对接资料与技术支持需求函.docx b/docs/CNC机床系统对接资料与技术支持需求函.docx new file mode 100644 index 0000000..4a65ab5 Binary files /dev/null and b/docs/CNC机床系统对接资料与技术支持需求函.docx differ diff --git a/docs/CNC机床系统对接资料与技术支持需求函.pdf b/docs/CNC机床系统对接资料与技术支持需求函.pdf new file mode 100644 index 0000000..695e898 Binary files /dev/null and b/docs/CNC机床系统对接资料与技术支持需求函.pdf differ diff --git a/docs/build_cnc_client_rfi.py b/docs/build_cnc_client_rfi.py new file mode 100644 index 0000000..442e266 --- /dev/null +++ b/docs/build_cnc_client_rfi.py @@ -0,0 +1,319 @@ +from pathlib import Path + +from docx import Document +from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT +from docx.enum.text import WD_ALIGN_PARAGRAPH +from docx.oxml import OxmlElement +from docx.oxml.ns import qn +from docx.shared import Inches, Pt, RGBColor + +from build_cnc_first_integration_checklist import ( + AMBER, + BLACK, + BLUE, + GRAY, + GREEN, + LIGHT_BLUE, + PALE_BLUE, + WHITE, + add_callout, + add_text, + keep_row_together, + set_cell_margins, + set_cell_shading, + set_repeat_table_header, + set_table_fixed_width, +) + + +OUT = Path("/Users/jerry/linkhand/CadSet/docs/CNC机床系统对接资料与技术支持需求函.docx") + + +def setup_document(): + doc = Document() + section = doc.sections[0] + section.page_width = Inches(8.5) + section.page_height = Inches(11) + section.top_margin = Inches(0.7) + section.bottom_margin = Inches(0.7) + section.left_margin = Inches(0.72) + section.right_margin = Inches(0.72) + section.header_distance = Inches(0.3) + section.footer_distance = Inches(0.3) + + normal = doc.styles["Normal"] + normal.font.name = "Arial" + normal._element.rPr.rFonts.set(qn("w:eastAsia"), "Arial") + normal.font.size = Pt(10.5) + normal.font.color.rgb = RGBColor.from_string(BLACK) + normal.paragraph_format.space_after = Pt(5) + normal.paragraph_format.line_spacing = 1.12 + + for name, size, before, after in ( + ("Heading 1", 16, 14, 7), + ("Heading 2", 12.5, 10, 5), + ("Heading 3", 11, 7, 3), + ): + style = doc.styles[name] + style.font.name = "Arial" + style._element.rPr.rFonts.set(qn("w:eastAsia"), "Arial") + style.font.size = Pt(size) + style.font.bold = True + style.font.color.rgb = RGBColor.from_string(BLUE) + style.paragraph_format.space_before = Pt(before) + style.paragraph_format.space_after = Pt(after) + style.paragraph_format.keep_with_next = True + + for name in ("List Bullet", "List Number"): + style = doc.styles[name] + style.font.name = "Arial" + style._element.rPr.rFonts.set(qn("w:eastAsia"), "Arial") + style.font.size = Pt(10.5) + + hp = section.header.paragraphs[0] + hp.alignment = WD_ALIGN_PARAGRAPH.RIGHT + add_text(hp, "CNC 机床系统对接|资料与技术支持需求函", size=8.5, color=GRAY) + fp = section.footer.paragraphs[0] + fp.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(fp, "请按实际设备型号填写;不适用项目请标注“不适用”", size=8, color=GRAY) + return doc + + +def formal_matrix(doc, headers, widths, rows, first_col_fill=None, sizes=None): + table = doc.add_table(rows=1, cols=len(headers)) + table.style = "Table Grid" + set_table_fixed_width(table, widths) + for idx, text in enumerate(headers): + cell = table.rows[0].cells[idx] + set_cell_shading(cell, BLUE) + set_cell_margins(cell) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + p = cell.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(p, text, size=9.2, bold=True, color=WHITE) + set_repeat_table_header(table.rows[0]) + for ridx, values in enumerate(rows): + row = table.add_row() + keep_row_together(row) + for idx, value in enumerate(values): + cell = row.cells[idx] + set_cell_margins(cell, top=100, bottom=100) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + if idx == 0 and first_col_fill: + set_cell_shading(cell, first_col_fill) + p = cell.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER if idx == 0 else WD_ALIGN_PARAGRAPH.LEFT + size = sizes[idx] if sizes else 9.2 + add_text(p, value, size=size, bold=(idx == 0)) + return table + + +def add_form_lines(doc, pairs): + table = doc.add_table(rows=0, cols=2) + table.style = "Table Grid" + set_table_fixed_width(table, [3.25, 3.25]) + for left, right in pairs: + row = table.add_row() + keep_row_together(row) + for idx, value in enumerate((left, right)): + cell = row.cells[idx] + set_cell_margins(cell, top=120, bottom=120) + add_text(cell.paragraphs[0], value, size=9.5) + return table + + +def build(): + doc = setup_document() + + p = doc.add_paragraph() + p.paragraph_format.space_before = Pt(12) + p.paragraph_format.space_after = Pt(4) + add_text(p, "CNC 机床系统对接", size=23, bold=True, color=BLUE) + p = doc.add_paragraph() + p.paragraph_format.space_after = Pt(12) + add_text(p, "资料与技术支持需求函", size=20, bold=True, color=BLUE) + + doc.add_heading("一、需求目的", level=1) + p = doc.add_paragraph() + add_text( + p, + "为开展数字零件模型至 CNC 加工程序生成、程序传输、试切验证及加工状态采集等工作," + "现请贵方协助确认实际机床和 CNC 控制系统配置,并提供本函所列技术资料、接口信息、" + "验证样例及联合调试支持。", + ) + p = doc.add_paragraph() + add_text( + p, + "本函中的通信产品和接口名称均为候选项,最终以实际机床型号、CNC 系统版本、已购选件、" + "机床制造商配置及贵方书面确认为准。", + color=GRAY, + ) + + add_callout( + doc, + "回复方式", + "请在对应表格中填写、勾选或标注“不支持/不适用/待确认”,并随回复附相关手册、" + "安装包、许可证说明、样例文件和技术联系人信息。", + PALE_BLUE, + ) + + doc.add_heading("二、建议参与对接的技术人员", level=1) + role_rows = [ + ("机床制造商/集成商", "机械配置、轴与主轴、刀库/夹具、专用 M 代码、PLC/联锁、机床安全和现场调试。", "姓名:________ 电话:________"), + ("三菱 CNC/代理商", "CNC 型号与版本、已购选件、通信软件、SDK、许可证、标准接口和版本兼容。", "姓名:________ 电话:________"), + ("现场工艺/设备人员", "现用 CAM、后处理器、刀具、材料、装夹、坐标、切削参数和已验证程序。", "姓名:________ 电话:________"), + ("网络/信息化人员", "工业网络、IP/端口、账号、证书、远程访问、日志和数据合规。", "姓名:________ 电话:________"), + ] + formal_matrix(doc, ["参与角色", "确认范围", "联系人"], [1.45, 3.70, 1.35], role_rows, first_col_fill=LIGHT_BLUE) + + doc.add_heading("三、设备与 CNC 基础信息", level=1) + equipment_rows = [ + ("机床身份", "制造商、完整型号、序列号、出厂年份、设备编号", "____________________________", "铭牌及整机照片"), + ("CNC 控制系统", "三菱 CNC 系列、完整型号、系统软件版本", "____________________________", "系统信息页面照片"), + ("已安装选件", "高速加工、五轴、刀尖点控制、宏程序、通信等选件", "____________________________", "选件清单/参数页面"), + ("机床类型", "车床/铣床/加工中心/车铣复合/磨床/专机", "____________________________", "设备规格书"), + ("轴配置", "轴名称、正方向、行程、零点、软限位、旋转轴结构", "____________________________", "轴配置图"), + ("主轴配置", "数量、方向、最高转速、功率/扭矩、锥度", "____________________________", "主轴规格"), + ("刀库/刀塔", "类型、容量、刀位规则、刀具尺寸重量限制", "____________________________", "刀库规格"), + ("辅助装置", "探针、自动对刀、第四/第五轴、尾座、卡盘、托盘等", "____________________________", "配置说明"), + ] + formal_matrix( + doc, + ["项目", "请确认内容", "贵方填写", "建议附件"], + [1.15, 2.65, 1.55, 1.15], + equipment_rows, + first_col_fill=LIGHT_BLUE, + sizes=[9.0, 8.8, 8.8, 8.5], + ) + + doc.add_heading("四、请提供的技术资料", level=1) + material_rows = [ + ("P0", "CNC 与机床手册", "CNC 规格/编程/操作/连接设置手册;机床厂操作、维护及专用功能手册。", "□已附 □后补 □不适用"), + ("P0", "代码和程序规则", "实际支持的 G/M/T/S/F 代码、自定义 M 代码、宏变量、固定循环、坐标系、刀补、换刀规则、程序格式限制。", "□已附 □后补 □不适用"), + ("P0", "程序模板", "机床厂认可的程序开头、换刀段、工序切换、异常退出和结束模板。", "□已附 □后补 □不适用"), + ("P0", "验证样例包", "至少 3~10 套已加工成功样例,每套对应 STEP/图纸、毛坯、材料、刀具、装夹、CAM、NC 和检测结果。", "数量:____套"), + ("P0", "现用 CAM/后处理", "CAM 名称和版本、后处理器、后处理配置、程序头尾模板和典型仿真项目。", "□已附 □后补 □无"), + ("P1", "刀具与刀柄", "真实刀具/刀柄清单、刀位号、刀补号、主要尺寸、寿命规则及 STEP/尺寸模型。", "□已附 □后补"), + ("P1", "夹具与工作区", "工作台、卡盘、虎钳、软爪、尾座、旋转轴、探针和防护区域的模型或关键尺寸。", "□已附 □后补"), + ("P1", "材料与工艺", "常用材料牌号、毛坯规格、余量、硬度/热处理、切削参数、冷却方式、公差和粗糙度。", "□已附 □后补"), + ("P2", "数字机床", "机床三维模型、运动学、回转中心、碰撞体、软限位、CAM 机床仿真工程。", "□已附 □后补 □无"), + ] + formal_matrix(doc, ["优先级", "资料名称", "交付内容", "回复"], [0.70, 1.45, 3.30, 1.05], material_rows, first_col_fill=GREEN) + + doc.add_heading("五、程序传输与通信能力确认", level=1) + interface_rows = [ + ("加工程序传输", "NC Explorer", "□已安装 □可新增 □不支持 □待确认", "版本/许可证/手册/网络配置"), + ("加工程序传输", "FTP、共享目录或机床厂文件服务", "□支持 □不支持 □待确认", "IP/端口/目录/账号/文件规则"), + ("加工程序传输", "DNC/MDC/MES 平台", "□已有 □无 □待确认", "产品名称、版本、第三方接口"), + ("加工程序传输", "RS-232C 或 USB", "□支持 □不支持", "传输设置与文件限制"), + ("CNC 通信 SDK", "FCSB1224W000 开发包", "□已授权 □可采购 □不支持 □待确认", "SDK、手册、样例、兼容矩阵"), + ("CNC 通信运行库", "FCSB1224W100", "□已授权 □可采购 □不支持 □待确认", "安装包、版本和部署许可"), + ("只读数据采集", "MTConnect", "□已配置 □可新增 □不支持 □待确认", "适配器/Agent、数据项和端点"), + ("只读数据采集", "OPC UA / NC Machine Tool Connector", "□已配置 □可新增 □不支持 □待确认", "服务器、节点表、证书、许可"), + ("运行监控", "NC Monitor2 或现有监控平台", "□已有 □无 □待确认", "产品版本与开放接口"), + ("PLC/设备访问", "SLMP 或机床厂专用接口", "□开放只读 □开放读写 □不开放 □待确认", "设备地址表、权限和联锁说明"), + ("机床厂专用 API", "机床厂二次开发接口", "□有 □无 □待确认", "API 文档、SDK、样例和授权"), + ] + formal_matrix( + doc, + ["用途", "候选通道", "贵方确认", "请附资料"], + [1.15, 1.80, 1.95, 1.60], + interface_rows, + first_col_fill=LIGHT_BLUE, + sizes=[8.6, 8.6, 8.3, 8.3], + ) + + doc.add_heading("六、接口交付要求", level=1) + interface_delivery_rows = [ + ("兼容性", "适用 CNC 型号、系统版本、所需硬件/选件、安装状态、许可证数量和有效期。", "____________________________"), + ("开发资料", "API/协议手册、数据字典、错误码、示例程序、SDK/运行库安装包及版本说明。", "____________________________"), + ("连接参数", "协议、IP、端口、账号/证书、读写权限、超时、并发、频率和文件大小限制。", "____________________________"), + ("可靠性", "断线检测、重连、续传、重复提交处理、任务 ID、校验、回执和错误处理机制。", "____________________________"), + ("时间与单位", "时区、时间同步、时间戳精度、字段单位、缩放系数和枚举值说明。", "____________________________"), + ("版本支持", "SDK/协议与 CNC 兼容矩阵、升级策略、补丁和技术支持期限。", "____________________________"), + ("日志审计", "连接、文件传输、调用方、操作时间、结果、配置修改及远程访问日志。", "____________________________"), + ] + formal_matrix(doc, ["项目", "交付要求", "贵方回复/附件"], [1.25, 3.85, 1.40], interface_delivery_rows, first_col_fill=LIGHT_BLUE) + + doc.add_heading("七、状态数据项确认", level=1) + p = doc.add_paragraph() + add_text( + p, + "请针对可提供的数据项另附数据字典,并至少标明:接口来源、字段/节点地址、数据类型、单位、" + "刷新周期、时间戳、枚举含义、无效值及报警条件。", + color=GRAY, + ) + data_rows = [ + ("设备状态", "设备 ID、型号/版本、在线/离线、采集状态、最后更新时间", "□可提供 □部分 □不支持"), + ("运行状态", "自动/手动/MDI、运行/暂停/停止/报警/急停、循环启动、门和关键联锁", "□可提供 □部分 □不支持"), + ("程序执行", "当前程序、子程序、行号/序列号、开始结束时间、循环时间、加工计数", "□可提供 □部分 □不支持"), + ("轴与坐标", "机床/工件坐标、剩余移动、进给、倍率、轴负载、旋转轴角度", "□可提供 □部分 □不支持"), + ("主轴", "实际/指令转速、倍率、负载、扭矩/功率、方向、状态、温度", "□可提供 □部分 □不支持"), + ("刀具", "当前刀号、刀位、刀补、寿命、使用次数、刀具报警及刀具表更新", "□可提供 □部分 □不支持"), + ("报警诊断", "报警号、等级、文本、发生/解除时间、历史、CNC 与机床 PLC 报警", "□可提供 □部分 □不支持"), + ("加工质量", "完工状态、实际周期、测头/尺寸结果、报废返工原因", "□可提供 □部分 □不支持"), + ("维护能耗", "运行时长、润滑/冷却、维护计数、能耗等设备已有数据", "□可提供 □部分 □不支持"), + ] + formal_matrix(doc, ["数据类别", "期望数据项", "贵方确认"], [1.20, 4.05, 1.25], data_rows, first_col_fill=LIGHT_BLUE) + + doc.add_heading("八、网络与安全配合", level=1) + security_rows = [ + ("网络拓扑", "机床网段、工业交换机、防火墙/隔离区、可否新增工控机及跨网访问方式。", "□可提供 □待确认"), + ("端口白名单", "协议、源/目的 IP、TCP/UDP 端口、连接方向和网络变更流程。", "□可提供 □待确认"), + ("账号与证书", "角色权限、密码策略、证书签发/更新、密钥保存和账号停用流程。", "□可提供 □待确认"), + ("远程支持", "VPN/堡垒机/厂商远程服务、审批、开放时间、审计和紧急断开。", "□允许 □不允许 □待确认"), + ("备份恢复", "CNC 参数、PLC、刀补、宏变量和接口配置的备份、回退及恢复负责人。", "□可配合 □待确认"), + ("数据合规", "可采集范围、保存期限、图纸/NC 程序保密等级、外发和云端限制。", "□可提供 □待确认"), + ] + formal_matrix(doc, ["项目", "请确认内容", "贵方回复"], [1.25, 4.00, 1.25], security_rows, first_col_fill=LIGHT_BLUE) + + doc.add_heading("九、联合调试与验收支持", level=1) + acceptance_rows = [ + ("资料审查", "型号、手册、代码表、样例、接口和许可证资料齐全;共同确认未知项和责任人。", "□可配合"), + ("离线验证", "使用验证样例生成 NC,完成 CAM/CNC 仿真和现场程序对比。", "□可配合 仿真环境:________"), + ("程序传输", "验证上传、下载、覆盖保护、文件名、校验、断线重试和回执,不启动机床。", "□可配合"), + ("空运行", "在机床锁定/抬刀/单段/低倍率条件下核对坐标、刀具、换刀、行程和安全位置。", "□可配合"), + ("试切", "使用约定毛坯、刀具和装夹加工样件,记录周期、报警、刀具和检测结果。", "□可配合"), + ("状态采集", "验证约定数据项、时间戳、连续采集、断线恢复和报警记录。", "□可配合"), + ("试运行", "限定设备和零件范围运行,统计成功率、人工干预、报警和返工。", "□可配合"), + ] + formal_matrix(doc, ["阶段", "配合与验收内容", "贵方确认"], [1.10, 4.20, 1.20], acceptance_rows, first_col_fill=LIGHT_BLUE) + + doc.add_heading("十、首批交付成果", level=1) + deliverable_rows = [ + ("P0", "设备接口基线表", "每台机床的完整型号、CNC 版本、轴/主轴/刀库、选件、程序传输、可用接口、网络和联系人。", "□可提供"), + ("P0", "机床编程资料包", "CNC/机床手册、G/M 代码、专用 M 代码、程序模板、坐标/刀补/换刀规则。", "□可提供"), + ("P0", "验证样例包", "3~10 套 STEP/图纸、毛坯、刀具、装夹、CAM、NC 和检测结果对应的完整样例。", "□可提供"), + ("P1", "通信接口包", "SDK/运行库或协议、许可、样例、数据字典、节点/地址表、错误码和兼容矩阵。", "□可提供"), + ("P1", "测试验收环境", "离线仿真或测试机、账号/目录、网络窗口、备份、现场人员及验收样件。", "□可提供"), + ("P2", "数字机床资料包", "机床、刀具、刀柄、夹具和毛坯模型;运动学、软限位、碰撞体和 CAM 仿真工程。", "□可提供"), + ] + formal_matrix(doc, ["优先级", "成果名称", "内容", "确认"], [0.70, 1.45, 3.30, 1.05], deliverable_rows, first_col_fill=GREEN) + + doc.add_heading("十一、商务授权与支持信息", level=1) + commercial_rows = [ + ("软件与接口授权", "SDK、运行库、OPC UA/MTConnect、仿真软件、后处理器是否收费及授权方式", "____________________________"), + ("现场服务", "现场调试服务内容、人员、预计工期及费用", "____________________________"), + ("培训", "操作、编程、通信接口、备份恢复和安全联调培训", "____________________________"), + ("维护支持", "服务时间、响应时限、升级路径、远程/现场支持和年度维护", "____________________________"), + ("交付介质", "安装包、许可证、手册、样例代码、版本号、校验值和下载期限", "____________________________"), + ] + formal_matrix(doc, ["项目", "请确认内容", "贵方回复"], [1.35, 3.65, 1.50], commercial_rows, first_col_fill=LIGHT_BLUE) + + doc.add_heading("附:建议资料包目录", level=1) + add_callout( + doc, + "建议压缩包结构", + "01_设备铭牌与系统照片;02_机床与CNC手册;03_GM代码与机床专用说明;04_已验证样例;" + "05_CAM与后处理;06_刀具夹具材料工艺;07_通信软件与接口文档;08_网络与测试环境;" + "09_授权商务信息;10_联系人。", + LIGHT_BLUE, + ) + + OUT.parent.mkdir(parents=True, exist_ok=True) + doc.save(OUT) + print(OUT) + + +if __name__ == "__main__": + build() diff --git a/docs/build_cnc_first_integration_checklist.py b/docs/build_cnc_first_integration_checklist.py new file mode 100644 index 0000000..6dc5fcf --- /dev/null +++ b/docs/build_cnc_first_integration_checklist.py @@ -0,0 +1,567 @@ +from pathlib import Path + +from docx import Document +from docx.enum.section import WD_SECTION +from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT +from docx.enum.text import WD_ALIGN_PARAGRAPH +from docx.oxml import OxmlElement +from docx.oxml.ns import qn +from docx.shared import Inches, Pt, RGBColor + + +OUT = Path("/Users/jerry/linkhand/CadSet/docs/CNC机床系统对接技术需求清单.docx") + +BLUE = "1F4E78" +LIGHT_BLUE = "DCE6F1" +PALE_BLUE = "EEF4F8" +GRAY = "666666" +LIGHT_GRAY = "F2F2F2" +GREEN = "E2F0D9" +AMBER = "FFF2CC" +RED = "FCE4D6" +WHITE = "FFFFFF" +BLACK = "111111" + + +def set_cell_shading(cell, fill): + tc_pr = cell._tc.get_or_add_tcPr() + shd = tc_pr.find(qn("w:shd")) + if shd is None: + shd = OxmlElement("w:shd") + tc_pr.append(shd) + shd.set(qn("w:fill"), fill) + + +def set_cell_margins(cell, top=90, start=110, bottom=90, end=110): + tc = cell._tc + tc_pr = tc.get_or_add_tcPr() + tc_mar = tc_pr.first_child_found_in("w:tcMar") + if tc_mar is None: + tc_mar = OxmlElement("w:tcMar") + tc_pr.append(tc_mar) + for margin, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)): + node = tc_mar.find(qn(f"w:{margin}")) + if node is None: + node = OxmlElement(f"w:{margin}") + tc_mar.append(node) + node.set(qn("w:w"), str(value)) + node.set(qn("w:type"), "dxa") + + +def set_repeat_table_header(row): + tr_pr = row._tr.get_or_add_trPr() + tbl_header = OxmlElement("w:tblHeader") + tbl_header.set(qn("w:val"), "true") + tr_pr.append(tbl_header) + + +def keep_row_together(row): + tr_pr = row._tr.get_or_add_trPr() + cant_split = OxmlElement("w:cantSplit") + tr_pr.append(cant_split) + + +def set_table_fixed_width(table, widths): + table.autofit = False + table.alignment = WD_TABLE_ALIGNMENT.CENTER + tbl_pr = table._tbl.tblPr + tbl_layout = tbl_pr.find(qn("w:tblLayout")) + if tbl_layout is None: + tbl_layout = OxmlElement("w:tblLayout") + tbl_pr.append(tbl_layout) + tbl_layout.set(qn("w:type"), "fixed") + for row in table.rows: + for idx, width in enumerate(widths): + row.cells[idx].width = Inches(width) + tc_pr = row.cells[idx]._tc.get_or_add_tcPr() + tc_w = tc_pr.find(qn("w:tcW")) + if tc_w is None: + tc_w = OxmlElement("w:tcW") + tc_pr.append(tc_w) + tc_w.set(qn("w:w"), str(int(width * 1440))) + tc_w.set(qn("w:type"), "dxa") + + +def set_run(run, size=10.5, bold=False, color=BLACK, font="Arial"): + run.font.name = font + run._element.get_or_add_rPr().rFonts.set(qn("w:eastAsia"), font) + run.font.size = Pt(size) + run.bold = bold + run.font.color.rgb = RGBColor.from_string(color) + + +def add_text(paragraph, text, size=10.5, bold=False, color=BLACK): + run = paragraph.add_run(text) + set_run(run, size=size, bold=bold, color=color) + return run + + +def add_bullet(doc, text, level=0, bold_prefix=None): + p = doc.add_paragraph(style="List Bullet" if level == 0 else "List Bullet 2") + p.paragraph_format.space_after = Pt(3) + p.paragraph_format.line_spacing = 1.12 + if bold_prefix and text.startswith(bold_prefix): + add_text(p, bold_prefix, bold=True) + add_text(p, text[len(bold_prefix):]) + else: + add_text(p, text) + return p + + +def add_checkbox_line(doc, text, note=None): + p = doc.add_paragraph() + p.paragraph_format.left_indent = Inches(0.15) + p.paragraph_format.first_line_indent = Inches(-0.15) + p.paragraph_format.space_after = Pt(3) + add_text(p, "☐ ", size=11.5, bold=True, color=BLUE) + add_text(p, text, bold=True) + if note: + add_text(p, f" {note}", color=GRAY) + + +def add_callout(doc, label, text, fill=PALE_BLUE): + table = doc.add_table(rows=1, cols=1) + set_table_fixed_width(table, [6.5]) + cell = table.cell(0, 0) + set_cell_shading(cell, fill) + set_cell_margins(cell, top=120, start=150, bottom=120, end=150) + p = cell.paragraphs[0] + p.paragraph_format.space_after = Pt(0) + add_text(p, f"{label}:", bold=True, color=BLUE) + add_text(p, text) + doc.add_paragraph().paragraph_format.space_after = Pt(1) + + +def add_requirement_table(doc, rows): + table = doc.add_table(rows=1, cols=4) + table.style = "Table Grid" + widths = [0.72, 1.63, 2.55, 1.60] + set_table_fixed_width(table, widths) + headers = ["优先级", "向对方要什么", "具体内容", "拿到后用于什么"] + for i, text in enumerate(headers): + cell = table.rows[0].cells[i] + set_cell_shading(cell, BLUE) + set_cell_margins(cell) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + p = cell.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(p, text, size=9.5, bold=True, color=WHITE) + set_repeat_table_header(table.rows[0]) + + for priority, ask, details, usage in rows: + row = table.add_row() + keep_row_together(row) + cells = row.cells + values = [priority, ask, details, usage] + fill = GREEN if priority == "必须" else (AMBER if priority == "建议" else WHITE) + for i, value in enumerate(values): + cell = cells[i] + set_cell_margins(cell) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + if i == 0: + set_cell_shading(cell, fill) + p = cell.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER if i == 0 else WD_ALIGN_PARAGRAPH.LEFT + add_text(p, value, size=9.2, bold=(i in (0, 1))) + return table + + +def add_response_table(doc, rows): + table = doc.add_table(rows=1, cols=3) + table.style = "Table Grid" + set_table_fixed_width(table, [2.35, 2.15, 2.0]) + for i, text in enumerate(["请确认", "对方填写/勾选", "附件或联系人"]): + cell = table.rows[0].cells[i] + set_cell_shading(cell, BLUE) + set_cell_margins(cell) + p = cell.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(p, text, size=9.5, bold=True, color=WHITE) + set_repeat_table_header(table.rows[0]) + for item, options in rows: + row = table.add_row() + keep_row_together(row) + cells = row.cells + for cell in cells: + set_cell_margins(cell, top=110, bottom=110) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + add_text(cells[0].paragraphs[0], item, size=9.3, bold=True) + add_text(cells[1].paragraphs[0], options, size=9.3) + add_text(cells[2].paragraphs[0], "________________", size=9.3, color=GRAY) + return table + + +def setup_document(): + doc = Document() + section = doc.sections[0] + section.page_width = Inches(8.5) + section.page_height = Inches(11) + section.top_margin = Inches(0.65) + section.bottom_margin = Inches(0.65) + section.left_margin = Inches(0.72) + section.right_margin = Inches(0.72) + section.header_distance = Inches(0.3) + section.footer_distance = Inches(0.3) + + styles = doc.styles + normal = styles["Normal"] + normal.font.name = "Arial" + normal._element.rPr.rFonts.set(qn("w:eastAsia"), "Arial") + normal.font.size = Pt(10.5) + normal.font.color.rgb = RGBColor.from_string(BLACK) + normal.paragraph_format.space_after = Pt(5) + normal.paragraph_format.line_spacing = 1.12 + + for name, size, before, after in ( + ("Heading 1", 16, 14, 7), + ("Heading 2", 12.5, 10, 5), + ("Heading 3", 11, 7, 3), + ): + style = styles[name] + style.font.name = "Arial" + style._element.rPr.rFonts.set(qn("w:eastAsia"), "Arial") + style.font.size = Pt(size) + style.font.bold = True + style.font.color.rgb = RGBColor.from_string(BLUE) + style.paragraph_format.space_before = Pt(before) + style.paragraph_format.space_after = Pt(after) + style.paragraph_format.keep_with_next = True + + for list_name in ("List Bullet", "List Bullet 2", "List Number"): + style = styles[list_name] + style.font.name = "Arial" + style._element.rPr.rFonts.set(qn("w:eastAsia"), "Arial") + style.font.size = Pt(10.5) + + header = section.header + p = header.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.RIGHT + add_text(p, "CNC 机床首次系统对接|资料与接口清单", size=8.5, color=GRAY) + + footer = section.footer + p = footer.paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(p, "供机床制造商、三菱 CNC 技术人员及项目对接人共同确认", size=8, color=GRAY) + return doc + + +def build(): + doc = setup_document() + + p = doc.add_paragraph() + p.paragraph_format.space_before = Pt(8) + p.paragraph_format.space_after = Pt(3) + add_text(p, "CNC 机床系统对接技术需求清单", size=23, bold=True, color=BLUE) + p = doc.add_paragraph() + p.paragraph_format.space_after = Pt(12) + add_text(p, "目标:把数字零件模型接入实际机床,完成加工程序生成、程序传输、试切验证和加工结果回收。", size=11.5, color=GRAY) + + add_callout( + doc, + "先说结论", + "第一次对接不需要把所有工业接口都拿到。最低闭环只需要四类东西:①准确的机床与 CNC 型号;" + "②机床专用编程资料;③一个可上传 NC 程序的通道;④一批已在该机床验证过的样例。" + "如果还要监控加工状态,再增加一个只读数据接口。", + GREEN, + ) + + doc.add_heading("1. 先找对人:三菱不一定等于机床厂", level=1) + add_bullet(doc, "机床制造商/集成商:掌握机床结构、轴配置、刀库、夹具、机床专用 M 代码、PLC 梯形图和安全联锁。") + add_bullet(doc, "三菱 CNC 或代理商:掌握控制器型号、系统版本、选件、通信软件、SDK、授权和标准接口。") + add_bullet(doc, "现场工艺/操作人员:掌握真实刀具、材料、转速进给、装夹、对刀、零点和已验证加工程序。") + add_callout(doc, "对接要求", "请甲方安排以上三类人员至少各一位。只找其中一方,通常拿不全资料。", AMBER) + + doc.add_heading("2. 第一批必须拿到的材料", level=1) + required_rows = [ + ("必须", "设备身份资料", "机床品牌/完整型号/序列号;三菱 CNC 控制器系列和完整型号;系统软件版本;已安装选件清单。最好提供铭牌、控制柜和系统信息页面照片。", "决定能用哪些手册、接口和软件,避免按错型号开发。"), + ("必须", "机床能力表", "车床/铣床/加工中心;轴数与轴名称;各轴行程;主轴数量、最高转速;最大进给;刀库类型和容量;是否有旋转轴、探针、自动对刀。", "确定模型是否可加工、工序怎么拆、刀路和后处理怎么生成。"), + ("必须", "编程资料", "该型号 CNC 的编程/操作手册;机床厂专用操作手册;支持的 G/M/T/S 代码;自定义 M 代码;宏变量、坐标系、刀补和程序格式说明。", "生成该机床能真正执行的 NC 程序。"), + ("必须", "已验证样例包", "至少 3~10 个已加工成功的样件,每个样件尽量包含 STEP/二维图、NC 程序、材料、毛坯、刀具表、装夹说明、坐标原点及最终检测结果。", "建立正确后处理和参数基线,比只有说明书更有效。"), + ("必须", "程序传输方式", "确认机床当前如何收发加工程序:以太网软件、FTP、共享目录、串口、USB 或其他;提供网络配置方法、账号权限、目录规则和文件名限制。", "打通“生成程序→送入机床”的最小闭环。"), + ("建议", "CAM/后处理资料", "现用 CAM 名称和版本;现有后处理器;后处理配置;机床仿真模型/运动学文件;程序头尾模板。", "优先复用成熟后处理,减少试切风险。"), + ("建议", "刀具与工艺资料", "真实刀具/刀柄清单、刀具编号、长度直径范围;常用材料与毛坯;推荐转速进给、冷却方式、余量、公差和粗糙度。", "把几何模型转换成可执行工艺,而不是只生成外形。"), + ("建议", "备份与恢复资料", "当前 CNC 参数、刀补、宏变量、PLC/梯形图及机床厂数据的备份方式;试验前完整备份;恢复负责人。", "调试异常时可恢复,避免影响生产。"), + ] + add_requirement_table(doc, required_rows) + + doc.add_heading("3. 接口怎么问:不用一上来就说“把 API 都给我”", level=1) + add_callout( + doc, + "正确问法", + "请对方根据实际 CNC 型号勾选已经安装、可以授权或可以新增的通道。我们的优先顺序是:" + "先解决程序文件传输,再解决只读监控,最后才考虑自动写变量或远程控制。", + PALE_BLUE, + ) + + doc.add_heading("3.1 上传和下载加工程序(第一优先级)", level=2) + add_checkbox_line(doc, "NC Explorer", "三菱官方工具,可通过以太网在电脑与多台 CNC 间操作加工程序文件;需确认该控制器是否支持。") + add_checkbox_line(doc, "FTP / 共享目录 / 机床厂文件服务", "如果机床已经开放,提供 IP、端口、目录、账号、字符编码和文件名规则。") + add_checkbox_line(doc, "RS-232C / USB", "可作为初期人工导入或旧机型兜底,但不适合长期自动闭环。") + add_checkbox_line(doc, "机床厂自带 DNC/MDC/MES 软件", "提供产品名称、接口文档和第三方接入方法。") + + doc.add_heading("3.2 读取运行状态和加工结果(第二优先级)", level=2) + add_checkbox_line(doc, "MTConnect", "适合读取运行、报警、状态等数据;通常为只读,安全性高,适合第一阶段监控。") + add_checkbox_line(doc, "OPC UA / NC Machine Tool Connector", "适合统一采集机床数据;需确认 OPC UA 服务器、节点表、证书、许可证和实际开放变量。") + add_checkbox_line(doc, "NC Monitor2 或甲方现有监控平台", "如果只需要人工远程查看,可先使用现成工具;若要系统接入,仍需确认是否提供数据接口。") + add_checkbox_line(doc, "数据库/文件导出", "若没有实时接口,可先通过班次 CSV、报警日志或加工结果文件实现离线回传。") + + doc.add_heading("3.3 读写 CNC 数据或做深度集成(第三优先级)", level=2) + add_checkbox_line(doc, "FCSB1224W000 开发包(SDK)", "三菱 CNC 通信软件的开发版;需索取安装包、许可证、参考手册、头文件/库和 C#/C++ 示例。") + add_checkbox_line(doc, "FCSB1224W100 运行库", "部署自行开发的 Windows 程序时可能需要;版本应与 SDK 和 CNC 兼容。") + add_checkbox_line(doc, "SLMP / PLC 设备访问", "仅在机床厂确实开放 PLC 设备映射时考虑;必须索取设备地址表、读写权限和联锁说明。") + add_checkbox_line(doc, "机床厂专用 API", "若机床厂对三菱 CNC 做过二次开发,应优先获取其 API,而不是绕过机床安全逻辑直接写控制器。") + add_callout( + doc, + "安全边界", + "第一阶段只申请“程序上传”和“状态读取”。不要直接申请自动启动主轴、循环启动、写 PLC 或修改 CNC 参数。" + "远程启动必须经过机床厂安全评审、现场联锁和甲方书面批准。", + RED, + ) + + doc.add_heading("4. 每一种接口要对方同时交付什么", level=1) + api_rows = [ + ("必须", "兼容性确认", "适用 CNC 型号、系统版本、所需硬件/选件、是否已安装、许可证数量和有效期。", "防止拿到软件但现场不能用。"), + ("必须", "开发资料", "API/协议手册、数据字典、错误码、示例程序、SDK/运行库安装包、版本说明。", "完成可维护的接口开发。"), + ("必须", "连接参数", "IP、端口、协议版本、账号/证书、读写权限、超时、并发和频率限制。", "建立测试连接。"), + ("必须", "测试环境", "一台可调试机床或离线仿真环境;测试账号;允许上传的测试目录;测试窗口。", "不在生产机上盲调。"), + ("建议", "现场支持", "机床厂电气/PLC 工程师、三菱 CNC 工程师、操作员的联系人和联合调试时间。", "遇到权限、联锁和型号差异时及时定位。"), + ] + add_requirement_table(doc, api_rows) + + doc.add_heading("5. 给甲方/制造商的回填表", level=1) + p = doc.add_paragraph() + add_text(p, "请对方直接填写或勾选;不确定的项目可写“待三菱/机床厂确认”。", color=GRAY) + response_rows = [ + ("机床制造商、型号、序列号", "填写完整型号"), + ("三菱 CNC 型号与系统版本", "填写/附系统页面照片"), + ("机床类型与轴配置", "车 / 铣 / 加工中心;3/4/5 轴"), + ("现用 CAM 与后处理器", "有 / 无;名称和版本"), + ("现有程序传输方式", "NC Explorer / FTP / DNC / RS-232C / USB / 其他"), + ("可否提供通信 SDK", "FCSB1224W000 / 其他 / 不支持"), + ("可否提供只读数据接口", "MTConnect / OPC UA / 其他 / 不支持"), + ("可否提供测试环境", "离线仿真 / 测试机 / 生产机测试窗口"), + ("可否提供已验证样例包", "数量:____;包含 STEP+NC+刀具+材料:是 / 否"), + ("机床厂技术联系人", "机械 / 电气PLC / 工艺"), + ("三菱或代理商技术联系人", "CNC / 通信软件 / 授权"), + ] + add_response_table(doc, response_rows) + + doc.add_heading("6. 第一次会议照着问的 12 个问题", level=1) + questions = [ + "这台机床的完整型号是什么?三菱 CNC 的完整型号和系统版本是什么?", + "机床是几轴?各轴行程、主轴、刀库和可加工范围是多少?", + "现在工程师用什么 CAM 和后处理器生成程序?能否提供现有后处理?", + "能否给 3~10 套已经加工成功的完整样例,而不只是 NC 文件?", + "机床现在通过什么方式接收和导出加工程序?", + "是否支持 NC Explorer?现场是否已经配置以太网?", + "这个型号是否支持 FCSB1224W000?许可证、SDK 和运行库由谁提供?", + "是否已经有 MTConnect、OPC UA、MDC、MES 或机床监控系统?", + "我们能读取哪些数据:运行状态、当前程序、坐标、刀具、报警、主轴负载、加工结果?", + "第一阶段能否提供测试机或仿真环境,并允许只上传程序、不自动启动?", + "试验前谁负责备份 CNC 参数、PLC、宏变量和刀补?出现问题谁负责恢复?", + "机床厂、三菱、现场工艺三方各由谁负责,何时可以联合调试?", + ] + for i, question in enumerate(questions, 1): + p = doc.add_paragraph(style="List Number") + p.paragraph_format.space_after = Pt(3) + add_text(p, question) + + doc.add_heading("7. 建议甲方首批打包提供的文件夹", level=1) + add_callout( + doc, + "一个压缩包即可", + "01_设备铭牌与系统照片;02_机床与 CNC 手册;03_GM代码和机床专用说明;" + "04_已验证样例;05_CAM与后处理;06_刀具材料工艺;07_通信软件与接口文档;" + "08_网络与测试环境;09_联系人。", + LIGHT_BLUE, + ) + + doc.add_heading("8. CAD、CAM 与机床数字模型资料", level=1) + add_callout( + doc, + "为什么必须补这一部分", + "STEP 只能说明零件几何,不能直接告诉机床如何装夹、选刀和运动。" + "要稳定生成 NC 程序,还需要机床、刀具、夹具、毛坯和坐标系数据。", + PALE_BLUE, + ) + cadcam_rows = [ + ("必须", "零件数据规范", "确认接收 STEP AP203/AP214/AP242 中哪一种;长度单位;坐标方向;模型原点;实体/装配要求;允许的几何修复;二维图、尺寸公差和表面粗糙度如何提供。", "建立统一输入标准,避免比例、方向、基准和公差信息丢失。"), + ("必须", "机床运动学", "各线性轴和旋转轴名称、正方向、行程、零点、回转中心、轴顺序、软限位、主轴方向;五轴机需提供 RTCP/TCP 相关配置。", "建立正确后处理和碰撞仿真。"), + ("必须", "坐标与对刀规则", "机床坐标、工件坐标 G54~G59/扩展坐标、程序零点、对刀点、刀长/刀径补偿号规则、探针和自动对刀流程。", "保证程序中的坐标与现场实际一致。"), + ("必须", "刀具与刀柄模型", "实际刀具、刀柄、延长杆和刀盘的清单及 STEP/尺寸;刀位号、刀补号、最大直径长度重量、相邻刀位限制。", "选刀、计算切削参数并检查刀具/刀柄碰撞。"), + ("必须", "夹具与工作区模型", "工作台、卡盘、虎钳、软爪、尾座、第四/第五轴、探针及主要防护区域的 STEP 或关键尺寸。", "规划装夹、可达性和碰撞检查。"), + ("必须", "毛坯和材料规则", "常用棒料、板料、锻件、铸件规格;材料牌号;毛坯余量;硬度和热处理状态;是否允许自动推荐替代材料。", "生成合理工序、刀路和切削参数。"), + ("建议", "机床仿真模型", "机床厂/CAM 使用的数字机床包、运动学文件、碰撞体、控制器定义和现有仿真项目。", "缩短虚拟调试和后处理验证周期。"), + ] + add_requirement_table(doc, cadcam_rows) + + doc.add_heading("9. 后处理器和 NC 程序规则", level=1) + post_rows = [ + ("必须", "程序格式限制", "程序号格式、文件扩展名、字符编码、行号、注释规则、最大文件大小、程序存储路径、子程序路径及程序调用层级限制。", "确保文件能够被控制器识别和加载。"), + ("必须", "标准代码清单", "实际支持的 G/M/T/S/F 代码、固定循环、极坐标/坐标旋转、刀补、螺纹、刚性攻丝、高速高精功能及版本差异。", "限制程序生成器只能使用现场支持的指令。"), + ("必须", "机床厂专用逻辑", "换刀、开关门、夹紧松开、卡盘/尾座、冷却、排屑、探针、托盘等自定义 M 代码;执行前置条件和互锁。", "避免使用通用 M 代码猜测机床动作。"), + ("必须", "程序头尾模板", "提供一份机床厂认可的开头、换刀段、工序切换、异常退出和结束模板;明确安全位置和模态复位要求。", "统一安全启动与结束逻辑。"), + ("必须", "后处理验收样例", "选择典型 2.5D、孔系、曲面或车削样件;提供 CAM 刀路、输出 NC、仿真结果和现场实际程序的对照。", "验证后处理输出与现场一致。"), + ("建议", "控制器高级选件", "确认高速加工、五轴联动、刀尖点控制、前瞻、样条/NURBS、宏程序等功能是否购买并启用。", "避免生成未授权或不支持的功能。"), + ] + add_requirement_table(doc, post_rows) + + doc.add_heading("10. 需要读取的数据点清单", level=1) + add_callout( + doc, + "让对方逐项标注", + "每个数据点请填写:是否可读、来自哪个接口、字段/节点地址、数据类型、单位、刷新周期、时间戳、" + "枚举含义、无效值和报警条件。不要只回复“支持 OPC UA”或“支持 API”。", + AMBER, + ) + datapoint_rows = [ + ("必须", "设备与连接状态", "设备 ID、CNC 型号、系统版本、在线/离线、数据源状态、采集程序版本、最后更新时间。", "识别设备并判断数据是否可信。"), + ("必须", "运行状态", "自动/手动/MDI、运行/暂停/停止/报警/急停、循环启动状态、单段、选择停止、门和关键联锁状态。", "判断设备是否正在执行及为何停机。"), + ("必须", "程序执行", "当前程序号/文件名、子程序、当前行号或序列号、程序开始结束时间、循环时间、加工计数。", "把加工任务与实际执行记录关联。"), + ("必须", "轴与坐标", "机床坐标、工件坐标、剩余移动量、进给速度、进给倍率、各轴负载、旋转轴角度;单位和坐标系标识。", "监控运动并分析异常。"), + ("必须", "主轴数据", "实际/指令转速、倍率、负载、扭矩或功率、运行方向、主轴状态、温度(若有)。", "分析切削负载和加工状态。"), + ("必须", "刀具数据", "当前刀号、刀位、刀长/刀径补偿、寿命/使用次数、刀具报警、备用刀及刀具表更新时间。", "检查程序选刀并积累刀具寿命数据。"), + ("必须", "报警与诊断", "报警号、等级、文本、发生/解除时间、报警历史、控制器诊断状态;明确是否能读取机床厂 PLC 报警。", "故障追踪和自动告警。"), + ("建议", "加工与质量结果", "完工状态、实际周期、零件计数、测头结果、尺寸检测、报废/返工原因、操作员确认。", "形成加工结果闭环。"), + ("建议", "能耗与维护", "主轴运行时长、开机时间、润滑/冷却状态、过滤器、维护计数及能耗数据(若设备已有)。", "支持后续维护和效率分析。"), + ] + add_requirement_table(doc, datapoint_rows) + + doc.add_heading("11. 通信接口的工程参数", level=1) + comm_rows = [ + ("必须", "方向与权限", "明确每个接口是只读、文件上传下载、数据读写还是远程控制;列出允许和禁止的操作。", "划定安全边界。"), + ("必须", "性能指标", "刷新周期、最小采样间隔、单次/批量读取数量、并发连接数、最大文件大小、传输速率和超时时间。", "评估能否满足监控和任务下发。"), + ("必须", "可靠性机制", "断线检测、自动重连、续传、重复提交处理、任务唯一 ID、校验和、确认回执和错误码。", "避免程序重复上传、丢失或状态误判。"), + ("必须", "时间与单位", "CNC 时间设置、时区、NTP/PTP 支持、时间戳精度;坐标、转速、负载等字段的单位与缩放系数。", "保证多系统数据可对齐。"), + ("必须", "版本管理", "SDK/运行库/协议版本与 CNC 版本兼容矩阵;升级策略;弃用接口;补丁和技术支持期限。", "避免现场升级后接口失效。"), + ("建议", "日志与审计", "连接日志、文件传输记录、调用方、操作时间、操作结果、配置修改和远程访问记录。", "故障定位和责任追踪。"), + ] + add_requirement_table(doc, comm_rows) + + doc.add_heading("12. 网络与信息安全要求", level=1) + security_rows = [ + ("必须", "网络拓扑", "机床所在网段、工业交换机、防火墙/隔离区、是否允许新增工控机、跨网访问方式和负责网络变更的人员。", "确定系统部署位置和连接路径。"), + ("必须", "端口白名单", "逐项提供协议、源/目的 IP、TCP/UDP 端口、连接方向;禁止使用不受控的全网开放规则。", "完成最小权限网络配置。"), + ("必须", "账号与证书", "账号类型、角色权限、密码策略、证书签发/更新、密钥保存、账号停用流程;禁止共享管理员账号。", "安全且可审计地访问设备。"), + ("必须", "远程支持方案", "是否允许 VPN、堡垒机或厂商远程服务;审批流程、开放时间、录屏/审计和紧急断开方法。", "让远程调试可控。"), + ("必须", "备份和变更控制", "CNC 参数、PLC、刀补、宏变量、接口配置的基线备份;变更申请、回退方案、恢复演练和负责人。", "降低调试对生产的风险。"), + ("建议", "数据合规", "允许采集的数据范围、数据保存期限、技术图纸和 NC 程序的保密等级、外发和云端限制。", "明确数据能否用于分析和优化。"), + ] + add_requirement_table(doc, security_rows) + + doc.add_heading("13. 分阶段联调与验收标准", level=1) + acceptance_rows = [ + ("阶段 0", "资料审查", "型号、手册、代码表、样例、接口和许可证齐全;双方确认未知项和责任人。", "输出《设备接口基线表》。"), + ("阶段 1", "离线后处理", "使用已验证样例生成 NC;与现场程序对比;通过 CAM/控制器仿真,无语法错误和明显碰撞。", "后处理器可进入现场试验。"), + ("阶段 2", "程序传输", "上传、下载、覆盖保护、目录、文件名、校验、断线重试和回执测试通过;不启动机床。", "打通安全文件通道。"), + ("阶段 3", "空运行验证", "机床锁定/抬刀/单段/低倍率条件下执行;核对坐标、刀具、换刀、行程和安全位置。", "证明程序与机床配置一致。"), + ("阶段 4", "试切验收", "指定毛坯、刀具和装夹;加工样件;记录周期、报警、刀具和检测结果;尺寸及表面质量达到双方确认标准。", "证明可实际制造。"), + ("阶段 5", "数据回传", "运行状态、程序、坐标、刀具、主轴和报警等约定数据点连续采集;断线恢复和时间戳正确。", "形成加工状态闭环。"), + ("阶段 6", "试运行", "限定设备、班次和零件类型运行;统计成功率、人工干预、报警和返工;完成问题清单后转正式。", "控制上线风险。"), + ] + add_requirement_table(doc, acceptance_rows) + + doc.add_heading("14. 项目责任、商务和交付边界", level=1) + boundary_rows = [ + ("必须", "责任分工", "明确机床厂、三菱/代理商、甲方工艺/设备/网络人员和软件对接方各自负责的资料、配置、调试和验收内容。", "避免接口问题互相推诿。"), + ("必须", "授权和费用", "SDK、运行库、OPC UA/MTConnect、仿真软件、后处理器、现场服务、培训和年度维护分别是否收费。", "形成真实预算。"), + ("必须", "交付介质", "安装包、许可证文件、离线授权方式、手册、样例代码、源代码范围、版本号、校验值和下载有效期。", "保证后续可复现部署。"), + ("必须", "支持机制", "工作时间、响应时限、问题升级路径、远程/现场支持、版本升级和保修期。", "确保问题有闭环负责人。"), + ("建议", "培训与知识转移", "操作、编程、通信 SDK、数据接口、备份恢复和安全联调培训;提供录屏或培训材料。", "减少长期依赖单个厂商工程师。"), + ] + add_requirement_table(doc, boundary_rows) + + doc.add_heading("15. 最终要求对方交付的核心成果", level=1) + core_rows = [ + ("P0", "设备接口基线表", "每台机床一份:完整型号、CNC 版本、轴/主轴/刀库、选件、程序传输方式、可用接口、网络和联系人。", "没有这份表,不进入开发。"), + ("P0", "机床编程资料包", "CNC 手册、机床厂手册、G/M 代码、专用 M 代码、程序模板、坐标/刀补/换刀规则。", "用于生成安全 NC 程序。"), + ("P0", "验证样例包", "至少 3~10 套 STEP/图纸、毛坯、刀具、装夹、CAM、NC 和检测结果一一对应的完整样例。", "用于后处理和工艺验证。"), + ("P1", "通信接口包", "SDK/运行库或协议文档、许可证、样例代码、数据字典、地址/节点表、错误码和兼容矩阵。", "用于程序传输和数据采集。"), + ("P1", "测试与验收环境", "离线仿真或测试机、测试账号/目录、网络窗口、备份、现场人员和验收样件。", "用于安全联调。"), + ("P2", "数字机床资料包", "机床、刀具、刀柄、夹具和毛坯模型;运动学;软限位;碰撞体;CAM 仿真工程。", "用于规模化自动编程和虚拟验证。"), + ] + add_requirement_table(doc, core_rows) + + doc.add_heading("附 A:不同机床类型还要追加确认什么", level=1) + machine_rows = [ + ("车床", "卡盘/夹头、主轴/副主轴、尾座、刀塔、动力刀具、Y/C/B 轴、棒料机、接料器和螺纹循环。"), + ("三轴加工中心", "工作台尺寸、主轴锥度、刀库、换刀位置、探针、第四轴接口、固定循环和高速加工选件。"), + ("四/五轴加工中心", "旋转轴结构、回转中心、轴限位、TCP/RTCP、刀尖点控制、奇异点策略、机床运动学和精确碰撞模型。"), + ("车铣复合", "多通道/多系统、主副主轴同步、刀塔同步、程序等待码、工件交接、通道间变量和完整同步样例。"), + ("磨床/专机", "砂轮修整、补偿、测量循环、专用 PLC 动作、机床厂自定义程序格式和专用工艺数据库。"), + ] + table = doc.add_table(rows=1, cols=2) + table.style = "Table Grid" + set_table_fixed_width(table, [1.35, 5.15]) + for i, text in enumerate(["机床类型", "追加资料"]): + set_cell_shading(table.rows[0].cells[i], BLUE) + set_cell_margins(table.rows[0].cells[i]) + p = table.rows[0].cells[i].paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(p, text, size=9.5, bold=True, color=WHITE) + set_repeat_table_header(table.rows[0]) + for machine_type, extra in machine_rows: + row = table.add_row() + keep_row_together(row) + for cell in row.cells: + set_cell_margins(cell) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + add_text(row.cells[0].paragraphs[0], machine_type, size=9.3, bold=True) + add_text(row.cells[1].paragraphs[0], extra, size=9.3) + + doc.add_heading("附 B:三菱侧可供确认的典型产品名称", level=1) + p = doc.add_paragraph() + add_text( + p, + "以下只是对接时用于询问的候选名称,并不表示每台机床都具备:NC Explorer(程序文件传输)、" + "FCSB1224W000/W100(CNC 通信 SDK/运行库)、MTConnect Data Collector、" + "NC Machine Tool Connector(OPC UA)、NC Monitor2、NC Virtual Simulator、NC Trainer2 plus。", + size=9.5, + color=GRAY, + ) + p = doc.add_paragraph() + p.paragraph_format.space_before = Pt(3) + add_text( + p, + "确认原则:以机床铭牌、CNC 型号、系统版本、已购选件及机床厂书面答复为准。", + size=9.5, + bold=True, + color=BLUE, + ) + + doc.add_heading("附 C:三菱官方参考入口", level=1) + official_sources = [ + ("NC 软件工具总览", "确认 NC Explorer、NC Monitor2、NC Virtual Simulator、NC Trainer2 plus 等工具。", "https://www.mitsubishielectric.com/fa/products/software/cnc/index.html"), + ("NC Explorer", "官方说明其通过以太网在 Windows 资源管理器中操作 CNC 加工程序文件。", "https://us.mitsubishielectric.com/fa/en/products/cnt/computerized-numerical-controllers/software-tools/nc-explorer/"), + ("FCSB1224W000 通信软件手册", "确认 SDK、运行库、支持控制器、开发语言和 API 能力。", "https://dl.mitsubishielectric.com/dl/fa/document/manual/cnc/ib1501209/ib1501209engh.pdf"), + ("MTConnect Data Collector", "确认支持的 CNC、适配器、采集器和系统要求。", "https://www.mitsubishielectric.com/fa/products/cnt/cnc/pmerit/iot/mtconnect_data_collector/"), + ("NC Machine Tool Connector", "确认 OPC UA/MTConnect 数据采集、连接对象和许可要求。", "https://dl.mitsubishielectric.com/dl/fa/document/manual/cnc/ib1501634/ib1501634engd.pdf"), + ("三菱 CNC 手册下载", "按实际 CNC 型号和版本下载规格、编程、操作、连接和设置手册。", "https://www.mitsubishielectric.com/fa/download/search.page?kisyu=%2Fcnc&mode=manual"), + ] + source_table = doc.add_table(rows=1, cols=3) + source_table.style = "Table Grid" + set_table_fixed_width(source_table, [1.55, 2.55, 2.40]) + for i, text in enumerate(["官方入口", "用于确认", "网址"]): + set_cell_shading(source_table.rows[0].cells[i], BLUE) + set_cell_margins(source_table.rows[0].cells[i]) + p = source_table.rows[0].cells[i].paragraphs[0] + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + add_text(p, text, size=9.3, bold=True, color=WHITE) + set_repeat_table_header(source_table.rows[0]) + for name, use, url in official_sources: + row = source_table.add_row() + keep_row_together(row) + for cell in row.cells: + set_cell_margins(cell) + cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER + add_text(row.cells[0].paragraphs[0], name, size=8.7, bold=True) + add_text(row.cells[1].paragraphs[0], use, size=8.7) + add_text(row.cells[2].paragraphs[0], url, size=7.8, color=BLUE) + + OUT.parent.mkdir(parents=True, exist_ok=True) + doc.save(OUT) + print(OUT) + + +if __name__ == "__main__": + build() diff --git a/text-to-cad/models/cad-router-v1/imported_015240/.imported_015240.step.glb b/text-to-cad/models/cad-router-v1/imported_015240/.imported_015240.step.glb new file mode 100644 index 0000000..97eb9fa --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/.imported_015240.step.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c0955754d08c765f8644beafa830274d4da9703aeb41ef8945488e4bea88aa8 +size 362988 diff --git a/text-to-cad/models/cad-router-v1/imported_015240/.source.step.glb b/text-to-cad/models/cad-router-v1/imported_015240/.source.step.glb new file mode 100644 index 0000000..2c24f4f --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/.source.step.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1977a77363edee14a222e72ad6147fc112df8214b8b976bcc759c5bb30746a0 +size 368340 diff --git a/text-to-cad/models/cad-router-v1/imported_015240/cad-task.json b/text-to-cad/models/cad-router-v1/imported_015240/cad-task.json new file mode 100644 index 0000000..66ad945 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/cad-task.json @@ -0,0 +1,149 @@ +{ + "schema_version": "1.0", + "request": "把明确提供的 015240.step 转换成可修改的 model-spec.json,并保留原始几何作为精确基体", + "route": { + "selected_backend": "build123d", + "runner_skill": "cad", + "project": "text-to-cad", + "fallback_order": [ + "simplecadapi", + "cadam" + ], + "workflow_profiles": [], + "operation": "modify", + "source_mode": "exact_step_base", + "modification_mode": "model_spec_parameter_edit" + }, + "source": { + "path": "source.step", + "format": "step", + "backend": "build123d", + "sha256": "55aee6a0ad74435503a1e7f108b32c9f403ae9838933e2be6728fb5b2f5cfbd4", + "immutable_exact_base": true + }, + "artifacts": [ + { + "path": "model-spec.json", + "role": "editable_geometry_contract" + }, + { + "path": "imported_015240.step", + "role": "primary" + }, + { + "path": "imported_015240.stl", + "role": "secondary" + }, + { + "path": "source.step", + "role": "immutable_exact_base" + } + ], + "parameters": {}, + "experience": { + "library": "../../../../cad-experience-library/library.json", + "context_kind": "generalized_cad_experience_query", + "methods_applied": [] + }, + "assumptions": [ + "单位按 STEP 几何和导入工具约定记录为 mm。", + "原 STEP 不包含可可靠恢复的原生草图、约束或特征历史;后续修改通过 model-spec.json 的命名参数和修改特征表达。", + "“选中面厚度增加”按直接建模语义解释为源面 #f22 沿其 +Z 法向外移 1.0 mm。", + "“红圈内的孔缩小”按目标水平板通孔从约 Ø4.8 mm 缩小到 Ø3.6 mm 处理。" + ], + "validation": [ + { + "check": "exact_base_sha256", + "passed": true, + "value": "55aee6a0ad74435503a1e7f108b32c9f403ae9838933e2be6728fb5b2f5cfbd4" + }, + { + "check": "exact_base_geometry_signature", + "passed": true, + "scope": "immutable_source.step", + "size_mm": [ + 26.200001497, + 18.906424738, + 28.2 + ], + "center_mm": [ + 13.100000748, + 7.453212369, + 0.0 + ], + "solid_count": 1, + "face_count": 31, + "edge_count": 87, + "volume_mm3": 2400.466496 + }, + { + "check": "model_spec_parameterized_rebuild", + "passed": true, + "modified_size_mm": [ + 26.200001497, + 18.906424738, + 29.2 + ], + "modified_center_mm": [ + 13.100000748, + 7.453212369, + 0.5 + ], + "solid_count": 1, + "volume_mm3": 2476.533646 + }, + { + "check": "selected_face_f22_extension", + "passed": true, + "source_plane_z_mm": 14.1, + "result_plane_z_mm": 15.1, + "extension_mm": 1.0, + "result_face": "#f27" + }, + { + "check": "circled_hole_resize", + "passed": true, + "center_mm": [ + 5.9, + 11.006425, + 0.0 + ], + "axis": "Z", + "source_diameter_mm": 4.799996, + "result_diameter_mm": 3.6, + "result_face": "#f22" + }, + { + "check": "source_to_modified_cad_diff", + "passed": true, + "geometry_changed": true, + "size_delta_mm": [ + 0.0, + 0.0, + 1.0 + ], + "center_delta_mm": [ + 0.0, + 0.0, + 0.5 + ] + }, + { + "check": "cad_inspect_refs_facts_planes_positioning", + "passed": true + }, + { + "check": "snapshot_review", + "passed": true, + "views": [ + "snapshots/iso_20260723T085707Z.png", + "snapshots/iso_opposite_20260723T085707Z.png", + "snapshots/top_ortho_20260723T085707Z.png", + "snapshots/front_ortho_20260723T085707Z.png" + ] + } + ], + "viewer_links": [ + "http://127.0.0.1:4179/?dir=%2FUsers%2Fjerry%2Flinkhand%2FCadSet%2Ftext-to-cad%2Fmodels%2Fcad-router-v1%2Fimported_015240&file=imported_015240.step" + ] +} diff --git a/text-to-cad/models/cad-router-v1/imported_015240/imported_015240.step b/text-to-cad/models/cad-router-v1/imported_015240/imported_015240.step new file mode 100644 index 0000000..8534c1c --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/imported_015240.step @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcd0dc191e42a59f7a4ef8fc598f40f1f18e12dde12fe87c9fd6dbef3401fcf1 +size 101319 diff --git a/text-to-cad/models/cad-router-v1/imported_015240/imported_015240.stl b/text-to-cad/models/cad-router-v1/imported_015240/imported_015240.stl new file mode 100644 index 0000000..0c803a8 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/imported_015240.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eba8d0d38ae8ee6144b016cec18f1928450b6f7cd5851c5ff3ba8f45b74cd8d9 +size 189284 diff --git a/text-to-cad/models/cad-router-v1/imported_015240/model-spec.json b/text-to-cad/models/cad-router-v1/imported_015240/model-spec.json new file mode 100644 index 0000000..691a828 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/model-spec.json @@ -0,0 +1,110 @@ +{ + "schema_version": "1.0", + "model_spec_kind": "parametric_cad_model", + "model_id": "imported_015240", + "units": "mm", + "reconstruction": { + "mode": "exact_step_base", + "source": { + "path": "source.step", + "sha256": "55aee6a0ad74435503a1e7f108b32c9f403ae9838933e2be6728fb5b2f5cfbd4", + "geometry_signature": { + "size_mm": [ + 26.200001497, + 18.906424738, + 28.2 + ], + "center_mm": [ + 13.100000748, + 7.453212369, + -0.0 + ], + "solid_count": 1, + "face_count": 31, + "edge_count": 87, + "volume_mm3": 2400.466496 + } + }, + "history_recovery": { + "status": "not_present_in_step", + "contract": "Preserve the imported B-Rep exactly as the immutable base; represent later edits as named parametric modifications." + } + }, + "parameters": { + "selected_face_extension": { + "value": 1.0, + "unit": "mm", + "role": "outward_offset_of_source_face_f22", + "source_face": "#f22", + "direction": "+Z" + }, + "target_hole_diameter": { + "value": 3.6, + "unit": "mm", + "role": "diameter_of_circled_horizontal_plate_hole", + "source_diameter_mm": 4.799996, + "axis": "Z" + } + }, + "modifications": [ + { + "id": "extend_selected_face_f22", + "operation": "add_box", + "source_face": "#f22", + "direction": "+Z", + "center": [ + 13.1, + -1.0, + { + "parameter": "selected_face_extension", + "scale": 0.5, + "offset": 14.1 + } + ], + "size": [ + 22.2, + 2.0, + { + "parameter": "selected_face_extension" + } + ], + "enabled": true + }, + { + "id": "fill_original_circled_hole", + "operation": "add_cylinder", + "axis": "z", + "center": [ + 5.9, + 11.006425, + 0.0 + ], + "diameter": 4.799996, + "length": 4.0, + "enabled": true + }, + { + "id": "cut_resized_circled_hole", + "operation": "cut_cylinder", + "axis": "z", + "center": [ + 5.9, + 11.006425, + 0.0 + ], + "diameter": { + "parameter": "target_hole_diameter" + }, + "length": 6.0, + "enabled": true + } + ], + "outputs": { + "step": "imported_015240.step", + "stl": "imported_015240.stl" + }, + "validation": { + "exact_base_required": true, + "geometry_signature_tolerance_mm": 1e-07 + } +} diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshot-job.json b/text-to-cad/models/cad-router-v1/imported_015240/snapshot-job.json new file mode 100644 index 0000000..6d1b7c3 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/snapshot-job.json @@ -0,0 +1,29 @@ +{ + "input": "imported_015240.step", + "mode": "view", + "outputs": [ + { + "path": "snapshots/iso.png", + "camera": "iso" + }, + { + "path": "snapshots/iso_opposite.png", + "camera": { + "direction": [-1, 1, -0.8] + } + }, + { + "path": "snapshots/top_ortho.png", + "camera": "top" + }, + { + "path": "snapshots/front_ortho.png", + "camera": "front" + } + ], + "render": { + "viewLabels": true, + "padding": 0.12, + "sizeProfile": "diagnostic" + } +} diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/front_ortho_20260723T084637Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/front_ortho_20260723T084637Z.png new file mode 100644 index 0000000..33f5c77 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/front_ortho_20260723T084637Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/front_ortho_20260723T085707Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/front_ortho_20260723T085707Z.png new file mode 100644 index 0000000..2da7c97 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/front_ortho_20260723T085707Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_20260723T084637Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_20260723T084637Z.png new file mode 100644 index 0000000..26a85a6 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_20260723T084637Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_20260723T085707Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_20260723T085707Z.png new file mode 100644 index 0000000..7f28e29 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_20260723T085707Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_opposite_20260723T084637Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_opposite_20260723T084637Z.png new file mode 100644 index 0000000..34e305b Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_opposite_20260723T084637Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_opposite_20260723T085707Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_opposite_20260723T085707Z.png new file mode 100644 index 0000000..1fc479e Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/iso_opposite_20260723T085707Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/top_ortho_20260723T084637Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/top_ortho_20260723T084637Z.png new file mode 100644 index 0000000..00653d9 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/top_ortho_20260723T084637Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/snapshots/top_ortho_20260723T085707Z.png b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/top_ortho_20260723T085707Z.png new file mode 100644 index 0000000..01dfc41 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/imported_015240/snapshots/top_ortho_20260723T085707Z.png differ diff --git a/text-to-cad/models/cad-router-v1/imported_015240/source.step b/text-to-cad/models/cad-router-v1/imported_015240/source.step new file mode 100644 index 0000000..9348f15 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/imported_015240/source.step @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55aee6a0ad74435503a1e7f108b32c9f403ae9838933e2be6728fb5b2f5cfbd4 +size 79302 diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/.targeted_015133.step.glb b/text-to-cad/models/cad-router-v1/targeted_015133/.targeted_015133.step.glb new file mode 100644 index 0000000..45059ec --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/.targeted_015133.step.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4389b0447a85c1985cc365b47445a36d7e1a27d65d397c7c92cdcb40756278ce +size 1282124 diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/inspect.json b/text-to-cad/models/cad-router-v1/targeted_015133/inspect.json new file mode 100644 index 0000000..3f1d3fb --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/inspect.json @@ -0,0 +1,372 @@ +{ + "ok": true, + "tokens": [ + { + "line": 1, + "token": "#", + "cadPath": "targeted_015133", + "stepPath": "targeted_015133.step", + "stepHash": "97f9763fe46700d0a23fa052b08c119e7352275b4e850cb8ed8c77f85e29f4c3", + "summary": { + "kind": "part", + "occurrenceCount": 1, + "leafOccurrenceCount": 1, + "shapeCount": 1, + "faceCount": 199, + "edgeCount": 508, + "vertexCount": 0, + "bounds": { + "min": [ + -106.75, + -0.0, + -38.0 + ], + "max": [ + 106.75, + 78.5, + 38.0 + ] + } + }, + "selections": [], + "warnings": [], + "entryFacts": { + "size": [ + 213.5, + 78.5, + 76.0 + ], + "extentAxis": "x", + "center": [ + 0.0, + 39.25, + 0.0 + ], + "diag": 239.8343178112757, + "kind": "part" + }, + "planes": [ + { + "axis": "y", + "coordinate": 0.0, + "normalSign": -1, + "faceCount": 1, + "totalArea": 12646.876863, + "bbox": { + "min": [ + -86.75, + -0.0, + -38.0 + ], + "max": [ + 86.75, + 0.0, + 38.0 + ] + }, + "selectors": [ + "f85" + ] + }, + { + "axis": "y", + "coordinate": 76.0, + "normalSign": 1, + "faceCount": 1, + "totalArea": 12168.786533, + "bbox": { + "min": [ + -86.75, + 76.0, + -38.0 + ], + "max": [ + 86.75, + 76.0, + 38.0 + ] + }, + "selectors": [ + "f59" + ] + }, + { + "axis": "z", + "coordinate": -38.00000000000001, + "normalSign": -1, + "faceCount": 3, + "totalArea": 11242.8, + "bbox": { + "min": [ + -86.75, + 0.0, + -38.0 + ], + "max": [ + 86.75, + 76.0, + -38.0 + ] + }, + "selectors": [ + "f110", + "f86", + "f98" + ] + }, + { + "axis": "z", + "coordinate": 38.00000000000001, + "normalSign": 1, + "faceCount": 3, + "totalArea": 11242.8, + "bbox": { + "min": [ + -86.75, + 0.0, + 38.0 + ], + "max": [ + 86.75, + 76.0, + 38.0 + ] + }, + "selectors": [ + "f60", + "f72", + "f84" + ] + }, + { + "axis": "x", + "coordinate": -106.75000000000001, + "normalSign": -1, + "faceCount": 5, + "totalArea": 5013.75858, + "bbox": { + "min": [ + -106.75, + 0.35, + -37.65 + ], + "max": [ + -106.75, + 75.65, + 37.65 + ] + }, + "selectors": [ + "f140", + "f166", + "f168", + "f170", + "f172" + ] + }, + { + "axis": "x", + "coordinate": 106.75000000000001, + "normalSign": 1, + "faceCount": 5, + "totalArea": 5013.75858, + "bbox": { + "min": [ + 106.75, + 0.35, + -37.65 + ], + "max": [ + 106.75, + 75.65, + 37.65 + ] + }, + "selectors": [ + "f158", + "f160", + "f162", + "f164", + "f55" + ] + } + ], + "entryPositioning": { + "kind": "part", + "bbox": { + "min": [ + -106.75, + -0.0, + -38.0 + ], + "max": [ + 106.75, + 78.5, + 38.0 + ] + }, + "bboxFacts": { + "size": [ + 213.5, + 78.5, + 76.0 + ], + "extentAxis": "x", + "center": [ + 0.0, + 39.25, + 0.0 + ], + "diag": 239.8343178112757 + }, + "majorPlanes": [ + { + "axis": "y", + "coordinate": 0.0, + "normalSign": -1, + "faceCount": 1, + "totalArea": 12646.876863, + "bbox": { + "min": [ + -86.75, + -0.0, + -38.0 + ], + "max": [ + 86.75, + 0.0, + 38.0 + ] + }, + "selectors": [ + "f85" + ] + }, + { + "axis": "y", + "coordinate": 76.0, + "normalSign": 1, + "faceCount": 1, + "totalArea": 12168.786533, + "bbox": { + "min": [ + -86.75, + 76.0, + -38.0 + ], + "max": [ + 86.75, + 76.0, + 38.0 + ] + }, + "selectors": [ + "f59" + ] + }, + { + "axis": "z", + "coordinate": -38.00000000000001, + "normalSign": -1, + "faceCount": 3, + "totalArea": 11242.8, + "bbox": { + "min": [ + -86.75, + 0.0, + -38.0 + ], + "max": [ + 86.75, + 76.0, + -38.0 + ] + }, + "selectors": [ + "f110", + "f86", + "f98" + ] + }, + { + "axis": "z", + "coordinate": 38.00000000000001, + "normalSign": 1, + "faceCount": 3, + "totalArea": 11242.8, + "bbox": { + "min": [ + -86.75, + 0.0, + 38.0 + ], + "max": [ + 86.75, + 76.0, + 38.0 + ] + }, + "selectors": [ + "f60", + "f72", + "f84" + ] + }, + { + "axis": "x", + "coordinate": -106.75000000000001, + "normalSign": -1, + "faceCount": 5, + "totalArea": 5013.75858, + "bbox": { + "min": [ + -106.75, + 0.35, + -37.65 + ], + "max": [ + -106.75, + 75.65, + 37.65 + ] + }, + "selectors": [ + "f140", + "f166", + "f168", + "f170", + "f172" + ] + }, + { + "axis": "x", + "coordinate": 106.75000000000001, + "normalSign": 1, + "faceCount": 5, + "totalArea": 5013.75858, + "bbox": { + "min": [ + 106.75, + 0.35, + -37.65 + ], + "max": [ + 106.75, + 75.65, + 37.65 + ] + }, + "selectors": [ + "f158", + "f160", + "f162", + "f164", + "f55" + ] + } + ] + } + } + ], + "errors": [] +} diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/model-spec.json b/text-to-cad/models/cad-router-v1/targeted_015133/model-spec.json new file mode 100644 index 0000000..0994aac --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/model-spec.json @@ -0,0 +1,43 @@ +{ + "schema_version": "1.0", + "model_spec_kind": "parametric_cad_model", + "model_id": "targeted_015133", + "units": "mm", + "reconstruction": { + "mode": "exact_step_base", + "source": { + "path": "source.step", + "sha256": "97f9763fe46700d0a23fa052b08c119e7352275b4e850cb8ed8c77f85e29f4c3", + "geometry_signature": { + "size_mm": [ + 213.5, + 78.5, + 76.0 + ], + "center_mm": [ + 0.0, + 39.25, + 0.0 + ], + "solid_count": 1, + "face_count": 199, + "edge_count": 488, + "volume_mm3": 1174741.621833 + } + }, + "history_recovery": { + "status": "not_present_in_step", + "contract": "Preserve the imported B-Rep exactly as the immutable base; represent later edits as named parametric modifications." + } + }, + "parameters": {}, + "modifications": [], + "outputs": { + "step": "targeted_015133.step", + "stl": "targeted_015133.stl" + }, + "validation": { + "exact_base_required": true, + "geometry_signature_tolerance_mm": 1e-07 + } +} diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/snapshot-job.json b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot-job.json new file mode 100644 index 0000000..ea70c8d --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot-job.json @@ -0,0 +1,29 @@ +{ + "input": "targeted_015133.step", + "mode": "view", + "outputs": [ + { + "path": "snapshot_iso.png", + "camera": "iso" + }, + { + "path": "snapshot_iso_opposite.png", + "camera": { + "direction": [-1, 1, -0.8] + } + }, + { + "path": "snapshot_top.png", + "camera": "top" + }, + { + "path": "snapshot_front.png", + "camera": "front" + } + ], + "render": { + "viewLabels": true, + "padding": 0.12, + "sizeProfile": "diagnostic" + } +} diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_front_20260723T112032Z.png b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_front_20260723T112032Z.png new file mode 100644 index 0000000..24e5254 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_front_20260723T112032Z.png differ diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_iso_20260723T112032Z.png b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_iso_20260723T112032Z.png new file mode 100644 index 0000000..b0bc3ef Binary files /dev/null and b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_iso_20260723T112032Z.png differ diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_iso_opposite_20260723T112032Z.png b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_iso_opposite_20260723T112032Z.png new file mode 100644 index 0000000..137ae1d Binary files /dev/null and b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_iso_opposite_20260723T112032Z.png differ diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_top_20260723T112032Z.png b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_top_20260723T112032Z.png new file mode 100644 index 0000000..4501702 Binary files /dev/null and b/text-to-cad/models/cad-router-v1/targeted_015133/snapshot_top_20260723T112032Z.png differ diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/source.step b/text-to-cad/models/cad-router-v1/targeted_015133/source.step new file mode 100644 index 0000000..c471002 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/source.step @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97f9763fe46700d0a23fa052b08c119e7352275b4e850cb8ed8c77f85e29f4c3 +size 435569 diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/targeted_015133.step b/text-to-cad/models/cad-router-v1/targeted_015133/targeted_015133.step new file mode 100644 index 0000000..c471002 --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/targeted_015133.step @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97f9763fe46700d0a23fa052b08c119e7352275b4e850cb8ed8c77f85e29f4c3 +size 435569 diff --git a/text-to-cad/models/cad-router-v1/targeted_015133/targeted_015133.stl b/text-to-cad/models/cad-router-v1/targeted_015133/targeted_015133.stl new file mode 100644 index 0000000..c0be17e --- /dev/null +++ b/text-to-cad/models/cad-router-v1/targeted_015133/targeted_015133.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e7c962764b7e5c0e36cf0471ad551aa0a0dfd618c46d7ec4ba7f97ab56741f6 +size 1256084 diff --git a/text-to-cad/skills/cad-router/SKILL.md b/text-to-cad/skills/cad-router/SKILL.md index 9b9abe0..cd577b0 100644 --- a/text-to-cad/skills/cad-router/SKILL.md +++ b/text-to-cad/skills/cad-router/SKILL.md @@ -24,7 +24,11 @@ Choose a backend before modeling, keep its source as the editable authority, and Add explicit signals when known: `--output step`, `--output stl`, `--manufacturing machining|printing|laser-cutting`, `--edit-source path/to/source`, `--existing-model`, `--assembly`, or `--browser-controls`. The router auto-discovers the workspace `cad-experience-library/library.json`, infers an experience family and feature roles from the request, and emits a - backend-neutral `design_plan`. Use `--experience-library`, + backend-neutral `design_plan`. When promoted `reconstruction_grammar` + records match, instantiate their symbolic parameter roles, datum roles, + canonical stages, and validation roles in the new part's + `model-spec.json`; fill values from explicit user dimensions first and + dimensionless distributions second. Use `--experience-library`, `--experience-family`, or repeated `--experience-feature` only to override those defaults. 3. Follow the selected route unless a hard environmental constraint makes it unavailable. If overriding it, record the reason in the task manifest. @@ -49,7 +53,8 @@ Choose a backend before modeling, keep its source as the editable authority, and `--experience-feature`; do not guess across family-scoped experience. 7. Validate against the brief. STEP-first routes must run geometry inspection and snapshot review. Mesh routes must check compilation, bounds, solidity/manifold facts when available, and snapshot review. For experience-informed generation, record which generalized methods were - applied. Validate against the user's brief and held-out targets, never against + applied, including the reconstruction grammar ID and instantiated symbolic + roles. Validate against the user's brief and held-out targets, never against parameters exposed from the same source case. 8. Write a `cad-task.json` manifest using the contract in `references/backend-contract.md`. Hand supported artifacts to `$cad-viewer` and return its live links. diff --git a/text-to-cad/skills/cad-router/references/model-spec.md b/text-to-cad/skills/cad-router/references/model-spec.md index 6d460db..a9cc141 100644 --- a/text-to-cad/skills/cad-router/references/model-spec.md +++ b/text-to-cad/skills/cad-router/references/model-spec.md @@ -68,7 +68,8 @@ constraints, names, or feature history. Therefore: ## Parametric modification layer Supported V1 operations are `add_box`, `cut_box`, `add_cylinder`, and -`cut_cylinder`. Values may be literals or parameter references: +`cut_cylinder`. Values may be literals, direct parameter references, or affine +parameter expressions: ```json { @@ -82,6 +83,21 @@ Supported V1 operations are `add_box`, `cut_box`, `add_cylinder`, and } ``` +Use `scale` and `offset` when a tool center must stay anchored while its size +changes. For example, a positive-Z face at `z = 14.1` can remain the lower face +of an added box while `extension` changes: + +```json +{ + "center": [ + 13.1, + -1.0, + {"parameter": "extension", "scale": 0.5, "offset": 14.1} + ], + "size": [22.2, 2.0, {"parameter": "extension"}] +} +``` + Change a named value and rebuild: ```bash diff --git a/text-to-cad/skills/cad-router/scripts/model_spec.py b/text-to-cad/skills/cad-router/scripts/model_spec.py index 98d5f09..fbd0c9c 100644 --- a/text-to-cad/skills/cad-router/scripts/model_spec.py +++ b/text-to-cad/skills/cad-router/scripts/model_spec.py @@ -111,11 +111,29 @@ def resolve_number(spec: dict[str, Any], value: Any, field: str) -> float: raise ModelSpecError(f"{field} must be numeric") if isinstance(value, (int, float)): result = float(value) - elif isinstance(value, dict) and set(value) == {"parameter"}: - result = _parameter_value(spec, str(value["parameter"])) + elif isinstance(value, dict) and "parameter" in value: + unexpected = set(value) - {"parameter", "scale", "offset"} + if unexpected: + raise ModelSpecError( + f"{field} parameter expression has unsupported keys: " + + ", ".join(sorted(unexpected)) + ) + scale = value.get("scale", 1.0) + offset = value.get("offset", 0.0) + if ( + isinstance(scale, bool) + or not isinstance(scale, (int, float)) + or isinstance(offset, bool) + or not isinstance(offset, (int, float)) + ): + raise ModelSpecError(f"{field} scale and offset must be numeric") + result = ( + _parameter_value(spec, str(value["parameter"])) * float(scale) + + float(offset) + ) else: raise ModelSpecError( - f"{field} must be a number or {{\"parameter\": \"name\"}}" + f"{field} must be a number or a parameter expression" ) if not math.isfinite(result): raise ModelSpecError(f"{field} must be finite") diff --git a/text-to-cad/skills/cad-router/scripts/route.py b/text-to-cad/skills/cad-router/scripts/route.py index 0b9bb67..986fd31 100755 --- a/text-to-cad/skills/cad-router/scripts/route.py +++ b/text-to-cad/skills/cad-router/scripts/route.py @@ -76,6 +76,11 @@ EXPERIENCE_FAMILY_RULES: tuple[tuple[str, tuple[str, ...]], ...] = ( ("shaft", ("shaft", "轴类", "传动轴")), ("flange", ("flange", "法兰")), ("bushing", ("bushing", "sleeve", "轴套", "衬套")), + ("plate", ("plate", "mounting plate", "安装板", "平板")), + ("bracket", ("bracket", "mounting bracket", "支架", "托架")), + ("block", ("block", "rectangular body", "方块", "块体")), + ("manifold", ("manifold", "valve body", "housing", "歧管", "阀体", "壳体")), + ("rotational_part", ("roller", "pulley", "cylindrical body", "滚轮", "带轮")), ) EXPERIENCE_FEATURE_RULES: tuple[tuple[str, tuple[str, ...]], ...] = ( @@ -90,8 +95,90 @@ EXPERIENCE_FEATURE_RULES: tuple[tuple[str, tuple[str, ...]], ...] = ( ("conical_transition", ("cone", "taper", "锥", "斜面过渡")), ("gear_teeth", ("gear", "teeth", "齿轮", "齿")), ("coaxial_cylindrical_stack", ("stepped shaft", "阶梯轴", "同轴", "轴肩")), + ( + "repeated_axial_hole_pattern", + ( + "mounting holes", + "corner holes", + "four holes", + "multiple holes", + "bolt holes", + "安装孔", + "四个孔", + "多个孔", + ), + ), + ("planar_dominant_body", ("plate", "bracket", "flat body", "板", "支架")), + ( + "multi_level_planar_profile", + ("pocket", "recess", "stepped plate", "台阶面", "凹槽", "沉台"), + ), + ( + "rotational_body", + ("cylindrical body", "roller", "pulley", "revolved", "圆柱体", "滚轮", "回转体"), + ), + ( + "stepped_rotational_profile", + ("stepped shaft", "multiple diameters", "shoulder", "阶梯轴", "多段直径", "轴肩"), + ), + ("shaft_like_body", ("shaft", "axle", "rod", "轴", "长杆")), + ( + "flange_like_rotational_body", + ("flange", "flanged", "法兰", "带法兰"), + ), + ( + "cylindrical_feature_network", + ("bore", "passage", "drilled holes", "孔系", "流道", "钻孔"), + ), + ( + "multi_axis_passage", + ("cross hole", "intersecting passage", "multi-axis", "交叉孔", "多轴孔"), + ), ) +# The router vocabulary is request-facing (plate, bracket, flange), while the +# distilled library vocabulary is geometry-facing (patterned_plate, +# plate_or_bracket, flanged_rotational_part). Keep the query semantic by +# expanding a request family into compatible generalized scopes; this never +# uses held-out case geometry or instance parameters. +EXPERIENCE_FAMILY_SCOPE_ALIASES: dict[str, set[str]] = { + "flanged_hub_adapter": { + "flanged_hub_adapter", + "flanged_rotational_part", + "hollow_or_stepped_shaft", + "rotational_part", + }, + "connecting_rod": { + "connecting_rod", + "patterned_prismatic_part", + "plate_or_bracket", + }, + "gear": {"gear", "rotational_part"}, + "shaft": {"shaft", "hollow_or_stepped_shaft", "rotational_part"}, + "flange": {"flange", "flanged_rotational_part", "rotational_part"}, + "bushing": {"bushing", "hollow_or_stepped_shaft", "rotational_part"}, + "plate": {"plate", "patterned_plate", "plate_or_bracket"}, + "bracket": { + "bracket", + "plate_or_bracket", + "patterned_plate", + "patterned_prismatic_part", + }, + "block": {"block", "prismatic_block", "patterned_prismatic_part"}, + "manifold": { + "manifold", + "multi_axis_manifold", + "prismatic_block", + "patterned_prismatic_part", + }, + "rotational_part": { + "rotational_part", + "shaft", + "hollow_or_stepped_shaft", + "flanged_rotational_part", + }, +} + def contains_any(text: str, terms: tuple[str, ...]) -> bool: return any(term in text for term in terms) @@ -358,13 +445,20 @@ def load_experience_library( if item.strip() } normalized_family = family.strip().lower().replace(" ", "_") if family else None + compatible_scopes = ( + EXPERIENCE_FAMILY_SCOPE_ALIASES.get( + normalized_family, {normalized_family} + ) + if normalized_family + else set() + ) selected: list[object] = [] for item in payload.get("experiences", []): if not isinstance(item, dict): continue scope = set(item.get("scope", [])) if "global" not in scope: - if not normalized_family or normalized_family not in scope: + if not compatible_scopes or scope.isdisjoint(compatible_scopes): continue required = set(item.get("when", {}).get("features", [])) if required and not required.issubset(requested_features): @@ -374,6 +468,7 @@ def load_experience_library( "schema_version": "2.0", "context_kind": "generalized_cad_experience_query", "family": normalized_family, + "compatible_scopes": sorted(compatible_scopes), "requested_features": sorted(requested_features), "experiences": selected, "policy": { @@ -526,6 +621,7 @@ def route(args: argparse.Namespace) -> dict[str, object]: "check", "repair", "confidence", + "reconstruction_grammar", ) if key in item } @@ -536,6 +632,17 @@ def route(args: argparse.Namespace) -> dict[str, object]: ) if isinstance(item, dict) ], + "reconstruction_grammars": [ + item["reconstruction_grammar"] + for item in ( + experience_context.get("experiences", []) + if experience_context + else [] + ) + if isinstance(item, dict) + and item.get("kind") == "reconstruction_grammar" + and isinstance(item.get("reconstruction_grammar"), dict) + ], "experience_status": ( "pending_source_classification" if needs_source_classification @@ -575,9 +682,10 @@ def route(args: argparse.Namespace) -> dict[str, object]: if edit_context else [ "Resolve explicit user dimensions and preserve them as authoritative parameters.", + "Instantiate matched reconstruction grammars as symbolic model-spec.json parameters, datums, and canonical feature stages; never copy teacher dimensions.", "Translate requested feature roles and matched generalized methods into backend operations.", "Generate the editable source of truth and STEP-first artifact when requested.", - "Validate topology, dimensions, feature relationships, and export integrity.", + "Validate topology, dimensions, feature relationships, surface mix, and export integrity against the grammar validation roles.", ] ), }, diff --git a/text-to-cad/skills/cad-router/tests/test_model_spec.py b/text-to-cad/skills/cad-router/tests/test_model_spec.py index 86cbeba..6bedd7c 100644 --- a/text-to-cad/skills/cad-router/tests/test_model_spec.py +++ b/text-to-cad/skills/cad-router/tests/test_model_spec.py @@ -17,6 +17,27 @@ MODULE_SPEC.loader.exec_module(MODEL_SPEC) class ModelSpecTests(unittest.TestCase): + def test_affine_parameter_expression(self) -> None: + payload = { + "parameters": { + "extension": { + "value": 2.0, + } + } + } + self.assertEqual( + MODEL_SPEC.resolve_number( + payload, + { + "parameter": "extension", + "scale": 0.5, + "offset": 14.1, + }, + "center[2]", + ), + 15.1, + ) + def test_import_step_exact_base_round_trip(self) -> None: with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) diff --git a/text-to-cad/tests/python/skills/cad-router/test_route.py b/text-to-cad/tests/python/skills/cad-router/test_route.py index 9ac633b..9774dd8 100644 --- a/text-to-cad/tests/python/skills/cad-router/test_route.py +++ b/text-to-cad/tests/python/skills/cad-router/test_route.py @@ -227,6 +227,115 @@ class CadRouterTests(unittest.TestCase): plan["generalized_methods"][0]["id"], ) + def test_design_plan_exposes_promoted_reconstruction_grammar(self) -> None: + payload = { + "schema_version": "2.0", + "library_kind": "generalized_cad_experience", + "policy": { + "instance_parameters_allowed": False, + "absolute_coordinates_allowed": False, + "single_case_promotion_allowed": False, + "llm_semantic_review_required": True, + "draft_evidence_verified": True, + "router_consumable": True, + }, + "experiences": [ + { + "id": "reconstruction.flanged_hub_adapter.primary", + "kind": "reconstruction_grammar", + "scope": ["flanged_hub_adapter"], + "when": {"features": ["base_flange", "hollow_sleeve"]}, + "guidance": "Establish shared datums before composing the envelope.", + "reconstruction_grammar": { + "parameter_roles": ["overall_long_span"], + "datum_roles": ["part_center", "primary_axis"], + "feature_roles": ["base_flange", "hollow_sleeve"], + "relation_roles": [], + "canonical_stages": [ + { + "id": "establish_reference_frame", + "operation": "define_datums", + "feature_roles": [], + "reference_roles": ["part_center", "primary_axis"], + } + ], + "cardinality_classes": ["cylinder_dense"], + "validation_roles": ["closed_solid"], + }, + "support": 30, + "confidence": 0.9, + } + ], + } + with tempfile.TemporaryDirectory() as directory: + library = Path(directory) / "library.json" + library.write_text(json.dumps(payload), encoding="utf-8") + result = route_module.route( + self.parse( + "生成一个带法兰轴套", + "--output", + "step", + "--experience-library", + str(library), + ) + ) + grammars = result["design_plan"]["reconstruction_grammars"] + self.assertEqual(1, len(grammars)) + self.assertEqual(["overall_long_span"], grammars[0]["parameter_roles"]) + + def test_request_family_matches_geometry_facing_distilled_scope(self) -> None: + payload = { + "schema_version": "2.0", + "library_kind": "generalized_cad_experience", + "policy": { + "instance_parameters_allowed": False, + "absolute_coordinates_allowed": False, + "single_case_promotion_allowed": False, + "llm_semantic_review_required": True, + "draft_evidence_verified": True, + "router_consumable": True, + }, + "experiences": [ + { + "id": "motif.patterned_plate.holes", + "kind": "feature_motif", + "scope": ["patterned_plate"], + "when": { + "features": [ + "planar_dominant_body", + "repeated_axial_hole_pattern", + ] + }, + "guidance": "Build the plate first, then place holes from symmetry.", + "support": 40, + "confidence": 0.94, + } + ], + } + with tempfile.TemporaryDirectory() as directory: + library = Path(directory) / "library.json" + library.write_text(json.dumps(payload), encoding="utf-8") + result = route_module.route( + self.parse( + "Generate a mounting plate with four corner holes", + "--output", + "step", + "--experience-library", + str(library), + ) + ) + context = result["experience_context"] + self.assertEqual("plate", context["family"]) + self.assertIn("patterned_plate", context["compatible_scopes"]) + self.assertIn("planar_dominant_body", context["requested_features"]) + self.assertIn( + "repeated_axial_hole_pattern", context["requested_features"] + ) + self.assertEqual( + ["motif.patterned_plate.holes"], + [item["id"] for item in context["experiences"]], + ) + def test_step_edit_uses_source_and_library_not_private_case(self) -> None: result = route_module.route( self.parse(