解决合并冲突
This commit is contained in:
@@ -16,28 +16,28 @@ from .runtime_types import CapabilityResult, FeaturePlanNode, HoleSpec, RuntimeD
|
||||
from .operation_contracts import materialized_feature_contracts
|
||||
|
||||
|
||||
_SELECTOR_REQUIRED = frozenset({"fillet", "chamfer"})
|
||||
_SKETCH_ATOM_PREFIXES = ("extrude_", "revolve_")
|
||||
_SELECTOR_REQUIRED = frozenset({"extrude_add_blind_with_hole", "loft_add_with_cap_face", "fillet", "chamfer", "shell"})
|
||||
_SKETCH_ATOM_PREFIXES = ("extrude_", "revolve_", "sweep_")
|
||||
# 开放轮廓(closed=false / role=open)只有"刀具截面补槽口边闭合后作切除"的
|
||||
# 物理意义:仅 extrude 直切类原子支持;add/回转对开放轮廓会造出无意义的封块。
|
||||
_OPEN_PROFILE_ATOMICS = frozenset({"extrude_cut_blind", "extrude_cut_through"})
|
||||
_PRIMARY_ATOMICS = frozenset({
|
||||
"extrude_add_blind", "extrude_add_two_sided", "extrude_cut_blind", "extrude_cut_two_sided",
|
||||
"extrude_add_blind", "extrude_add_blind_with_hole", "extrude_add_two_sided", "extrude_cut_blind", "extrude_cut_two_sided", "extrude_surface",
|
||||
"extrude_cut_through",
|
||||
"revolve_add", "revolve_cut", "hole_blind", "hole_countersink",
|
||||
"revolve_add", "revolve_cut", "revolve_surface", "hole_blind", "hole_countersink",
|
||||
"hole_counterbore", "sphere_add", "box_add", "cylinder_add",
|
||||
})
|
||||
_HOLE_ATOMICS = frozenset({"hole_blind", "hole_countersink", "hole_counterbore", "hole_wizard"})
|
||||
_ACTIVE_BODY_REQUIRED = frozenset({
|
||||
"extrude_cut_blind", "extrude_cut_two_sided", "extrude_cut_through",
|
||||
"revolve_cut", *_HOLE_ATOMICS, "fillet", "chamfer",
|
||||
"loft_add_with_cap_face", "revolve_cut", *_HOLE_ATOMICS, "fillet", "chamfer", "shell",
|
||||
# thread_cut 是 cut 型特征:必须在已有主体(宿主)上做布尔差,不能凭空
|
||||
# 造实体;无宿主时按 active_body 前置阻止而非让 executor 在 None 上崩溃。
|
||||
"thread_cut",
|
||||
})
|
||||
_BODY_MUTATING_ATOMICS = frozenset({
|
||||
"extrude_add_blind", "extrude_add_two_sided", "extrude_cut_blind", "extrude_cut_two_sided",
|
||||
"extrude_cut_through", "loft_add",
|
||||
"extrude_add_blind", "extrude_add_blind_with_hole", "extrude_add_two_sided", "extrude_cut_blind", "extrude_cut_two_sided",
|
||||
"extrude_cut_through", "loft_add", "loft_add_with_cap_face", "sweep_add",
|
||||
"revolve_add", "revolve_cut", "sphere_add", "box_add", "cylinder_add",
|
||||
"thread_add", "thread_cut", "bend_add", "gear_add", "rack_add", *_HOLE_ATOMICS, "fillet", "chamfer",
|
||||
})
|
||||
@@ -163,7 +163,7 @@ def sketch_ids_required_by_contract(cdsl: dict[str, Any]) -> frozenset[str]:
|
||||
atomic_id = str(feature.get("atomic_id") or "")
|
||||
if feature.get("sketch_id") is not None and (contracts.get(atomic_id) or {}).get("requires_sketch"):
|
||||
required.add(str(feature["sketch_id"]))
|
||||
if atomic_id == "loft_add":
|
||||
if atomic_id in {"loft_add", "loft_add_with_cap_face"}:
|
||||
for sketch_id in (feature.get("params") or {}).get("profile_sketch_ids") or ():
|
||||
required.add(str(sketch_id))
|
||||
return frozenset(required)
|
||||
@@ -313,12 +313,13 @@ class CapabilityAnalyzer:
|
||||
sketch_id=node.sketch_id,
|
||||
atomic_id=node.atomic_id,
|
||||
))
|
||||
if node.atomic_id == "loft_add":
|
||||
if node.atomic_id in {"loft_add", "loft_add_with_cap_face"}:
|
||||
profile_ids = params.get("profile_sketch_ids")
|
||||
if not isinstance(profile_ids, list) or len(profile_ids) < 2:
|
||||
minimum_profiles = 1 if node.atomic_id == "loft_add_with_cap_face" else 2
|
||||
if not isinstance(profile_ids, list) or len(profile_ids) < minimum_profiles:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "invalid_loft_profiles",
|
||||
"Loft requires at least two profile sketch ids",
|
||||
f"Loft requires at least {minimum_profiles} profile sketch ids",
|
||||
))
|
||||
elif len({str(sketch_id) for sketch_id in profile_ids}) != len(profile_ids):
|
||||
blockers.append(self._blocker(
|
||||
@@ -361,20 +362,90 @@ class CapabilityAnalyzer:
|
||||
"Loft currently requires exactly one outer profile without holes",
|
||||
sketch_id=sketch_id,
|
||||
))
|
||||
if node.atomic_id.startswith(_SKETCH_ATOM_PREFIXES):
|
||||
# #2 draft:extrudeParams.draft 在 cdsl_schema.json 中被允许,
|
||||
# 但 runtime 的拉伸执行器(build123d Solid.extrude)没有锥形
|
||||
# 拉伸能力,人读契约 profile_schema.json 也未声明该参数。
|
||||
# 若 importer 把 SolidWorks 的 draft_angle_rad 写进 CDSL,
|
||||
# 当前 runtime 会静默产出无拔模角的直壁实体。这里把它从
|
||||
# "静默忽略"改为"显式拒绝"(与 unsupported_extent 同模式)。
|
||||
if params.get("draft"):
|
||||
if node.atomic_id == "sweep_add":
|
||||
path = params.get("path")
|
||||
segment = path.get("segment") if isinstance(path, dict) else None
|
||||
kind = segment.get("type") if isinstance(segment, dict) else None
|
||||
if kind not in {"line", "bspline"}:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "unsupported_draft",
|
||||
"Extrude draft/taper is not implemented; the runtime would silently ignore it",
|
||||
node.feature_id, "unsupported_sweep_path",
|
||||
"Sweep requires one captured line or B-spline path",
|
||||
))
|
||||
elif kind == "line" and not all(
|
||||
isinstance(segment.get(key), list) and len(segment[key]) == 2
|
||||
for key in ("start", "end")
|
||||
):
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "invalid_sweep_path",
|
||||
"Sweep line path requires two-dimensional start and end points",
|
||||
))
|
||||
elif kind == "bspline" and (
|
||||
not isinstance(segment.get("points"), list)
|
||||
or len(segment.get("points") or []) < 3
|
||||
):
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "invalid_sweep_path",
|
||||
"Sweep B-spline path requires at least three interpolation points",
|
||||
))
|
||||
if node.atomic_id == "boolean_bodies":
|
||||
target_ids = params.get("target_feature_ids")
|
||||
tool_ids = params.get("tool_feature_ids")
|
||||
operation = params.get("operation")
|
||||
if operation not in {"union", "subtract", "intersect"}:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "unsupported_boolean_operation",
|
||||
"booleanBodies requires union, subtract or intersect",
|
||||
))
|
||||
for parameter, feature_ids in (("target_feature_ids", target_ids), ("tool_feature_ids", tool_ids)):
|
||||
if not isinstance(feature_ids, list) or not feature_ids:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "missing_boolean_bodies",
|
||||
"booleanBodies requires explicit target and tool feature ids", parameter=parameter,
|
||||
))
|
||||
continue
|
||||
for feature_id in feature_ids:
|
||||
source = nodes_by_id.get(str(feature_id))
|
||||
if source is None:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "boolean_body_unavailable",
|
||||
"booleanBodies source feature does not exist", feature_id=feature_id,
|
||||
))
|
||||
elif source.feature_id not in completed:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "boolean_body_unavailable",
|
||||
"booleanBodies source feature did not become executable", feature_id=feature_id,
|
||||
))
|
||||
if isinstance(target_ids, list) and isinstance(tool_ids, list) and set(target_ids) & set(tool_ids):
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "boolean_body_overlap",
|
||||
"booleanBodies targets and tools must be disjoint",
|
||||
))
|
||||
if node.atomic_id.startswith(_SKETCH_ATOM_PREFIXES):
|
||||
end_condition = params.get("end_condition") or {"type": "blind"}
|
||||
end_type = end_condition.get("type")
|
||||
draft = params.get("draft")
|
||||
if draft is not None:
|
||||
# 目前只将 CADFS 单侧盲向拔模映射到 build123d
|
||||
# Solid.extrude_taper。双向、到面和非实体 profile 的中性面
|
||||
# 语义尚无 CDSL 表达,必须保留为明确能力缺口。
|
||||
if (
|
||||
node.atomic_id not in {"extrude_add_blind", "extrude_cut_blind"}
|
||||
or end_type != "blind"
|
||||
):
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "unsupported_draft_extent",
|
||||
"Draft currently supports only one-sided blind extrusions",
|
||||
))
|
||||
elif not (
|
||||
isinstance(draft, dict)
|
||||
and isinstance(draft.get("angle_deg"), (int, float))
|
||||
and 0 < float(draft["angle_deg"]) < 90
|
||||
and isinstance(draft.get("pull_direction"), bool)
|
||||
):
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "invalid_draft",
|
||||
"Draft requires angle_deg in (0, 90) and boolean pull_direction",
|
||||
))
|
||||
required.append(f"extent:{end_type}")
|
||||
if end_type not in _SUPPORTED_EXTENTS:
|
||||
blockers.append(self._blocker(node.feature_id, "unsupported_extent", "The extent needs a resolved topology selector or is not implemented", extent=end_type))
|
||||
@@ -430,6 +501,13 @@ class CapabilityAnalyzer:
|
||||
))
|
||||
if node.atomic_id in _SELECTOR_REQUIRED and not node.selectors:
|
||||
blockers.append(self._blocker(node.feature_id, "missing_selector", "Dress-up features require an explicit selector"))
|
||||
if node.atomic_id in {"extrude_add_blind_with_hole", "loft_add_with_cap_face"}:
|
||||
face_selectors = [selector for selector in node.selectors if selector.get("kind") == "face"]
|
||||
if len(face_selectors) != 1 or len(node.selectors) != 1:
|
||||
blockers.append(self._blocker(
|
||||
node.feature_id, "invalid_profile_hole_selector",
|
||||
"Derived profile features require exactly one cap-face selector",
|
||||
))
|
||||
if node.atomic_id in _HOLE_ATOMICS:
|
||||
required.append("selector:host_face")
|
||||
if not params.get("host_face"):
|
||||
@@ -529,16 +607,17 @@ class CapabilityAnalyzer:
|
||||
if node.atomic_id in _BODY_MUTATING_ATOMICS:
|
||||
body_available = True
|
||||
body_producers = {
|
||||
"extrude_add_blind", "extrude_add_two_sided", "extrude_cut_blind", "extrude_cut_two_sided",
|
||||
"extrude_cut_through", "loft_add",
|
||||
"extrude_add_blind", "extrude_add_blind_with_hole", "extrude_add_two_sided", "extrude_cut_blind", "extrude_cut_two_sided",
|
||||
"extrude_cut_through", "loft_add", "loft_add_with_cap_face", "sweep_add", "boolean_bodies",
|
||||
"revolve_add", "revolve_cut", "sphere_add", "box_add", "cylinder_add",
|
||||
"thread_add", "bend_add", "gear_add", "rack_add",
|
||||
# thread_cut 与 extrude_cut_blind/revolve_cut 一致:无宿主时由
|
||||
# active_body 前置阻止,文档含该类特征即视为携带可执行几何。
|
||||
"thread_cut",
|
||||
}
|
||||
surface_producers = {"extrude_surface", "revolve_surface"}
|
||||
document_blockers: list[RuntimeDiagnostic] = []
|
||||
if not any(node.atomic_id in body_producers for node in plan):
|
||||
if not any(node.atomic_id in body_producers | surface_producers for node in plan):
|
||||
document_blockers.append(RuntimeDiagnostic(
|
||||
"no_solid_feature", "CDSL contains no feature capable of creating a solid body",
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user