tro_annotate: Lagebezug ueber die TeileId, Ablaufbeschreibung CSV/DXF -> SCL
dxf_registration.derive_from_ids: Zeichnungen aus dxfmakros tragen an jedem Layout-Block das Attribut ID = TeileId des CSV. tro_annotate paart CSV- Separatoren mit den Separator-Bloecken gleicher ID (sonst Scanner) und nimmt den Median-Versatz - eindeutig statt Abstimmung, bei jedem Lauf neu, gilt auch fuer eine verschobene Kopie der Zeichnung. Vorrang vor abgelegtem und abgestimmtem Bezug; --offset bleibt. Parameter id_spread_mm, id_min_share in cfg/parameter.ini [dxf_registration]. Dematic: gemeinsame Zeichnung 0/0, Mechanik-Zeichnung dematic.dxf +466,1/-163549,3 mm. Die Abstimmung rastete vorher auf den Scanner-Bloecken ein (Einfuegepunkt 360 mm neben der CSV-Position) und lag 360 mm daneben. Gesperrte Ausgabedatei (in BricsCAD geoeffnet) gibt eine Meldung statt Traceback. Neu: doc/Workflow_CSV_DXF_to_SCL.md - der ganze Ablauf vom CSV-Export und der Zeichnung bis zur SCL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,7 @@ __all__ = [
|
||||
"RegistrationError",
|
||||
"ANCHOR_BLOCKS",
|
||||
"derive_transform",
|
||||
"derive_from_ids",
|
||||
"verify_transform",
|
||||
"load_transform",
|
||||
"save_transform",
|
||||
@@ -97,6 +98,8 @@ FIT_TOL = get_float(_P, "fit_tol")
|
||||
DEFAULT_MATCH_TOL = get_float(_P, "default_match_tol")
|
||||
MIN_INLIERS = get_int(_P, "min_inliers")
|
||||
ROTATION_WARN_DEG = get_float(_P, "rotation_warn_deg")
|
||||
ID_SPREAD_MM = get_float(_P, "id_spread_mm")
|
||||
ID_MIN_SHARE = get_float(_P, "id_min_share")
|
||||
|
||||
|
||||
class RegistrationError(RuntimeError):
|
||||
@@ -304,6 +307,56 @@ def derive_transform(
|
||||
return transform
|
||||
|
||||
|
||||
def derive_from_ids(
|
||||
csv_by_id: dict[str, tuple[str, float, float]],
|
||||
dxf_by_id: dict[str, tuple[str, float, float]],
|
||||
csv_file: str = "",
|
||||
dxf_file: str = "",
|
||||
) -> Transform | None:
|
||||
"""
|
||||
Lagebezug ueber die TeileId: CSV-Objekt und DXF-Block mit derselben ID.
|
||||
|
||||
Mit dxfmakros gebaute Zeichnungen tragen an jedem Layout-Block das
|
||||
Attribut ID - dieselbe Nummer, die der CSV-Export als TeileId fuehrt. Das
|
||||
ist eine eindeutige Zuordnung, keine Abstimmung ueber Raster, und gilt
|
||||
genauso fuer eine verschobene Kopie der Zeichnung (nur Mechanik als
|
||||
Hintergrund) wie fuer die Zeichnung, aus der das CSV stammt.
|
||||
|
||||
Parameter:
|
||||
csv_by_id: TeileId -> (Rolle, x, y) aus dem CSV (Rolle wie in
|
||||
ANCHOR_BLOCKS: "Separator", "Scanner").
|
||||
dxf_by_id: ID-Attribut -> (Rolle, x, y) der Bloecke im DXF.
|
||||
|
||||
Rueckgabe:
|
||||
Transform (DXF -> CSV, ohne Drehung) oder None, wenn keine Rolle
|
||||
genug uebereinstimmende Paare hat. Die Rollen werden in der
|
||||
Reihenfolge von ANCHOR_BLOCKS versucht: der Separator-Block sitzt
|
||||
fast genau auf der CSV-Position (Dematic: 60 mm), der Scanner-Block
|
||||
360 mm daneben. Ausgedehnte Objekte (Kreisel, Strecken) zaehlen
|
||||
nicht - ihr Einfuegepunkt liegt je nach Lage weit vom CSV-Mittelpunkt.
|
||||
Eine Rolle gilt, wenn mindestens MIN_INLIERS Paare und ID_MIN_SHARE
|
||||
aller Paare hoechstens ID_SPREAD_MM vom Median entfernt liegen.
|
||||
"""
|
||||
for rolle in ANCHOR_BLOCKS:
|
||||
paare = [(c[1] - d[1], c[2] - d[2])
|
||||
for i, c in csv_by_id.items()
|
||||
if c[0] == rolle and (d := dxf_by_id.get(i)) is not None and d[0] == rolle]
|
||||
if len(paare) < MIN_INLIERS:
|
||||
continue
|
||||
mdx = sorted(p[0] for p in paare)[len(paare) // 2]
|
||||
mdy = sorted(p[1] for p in paare)[len(paare) // 2]
|
||||
passend = [p for p in paare if math.hypot(p[0] - mdx, p[1] - mdy) <= ID_SPREAD_MM]
|
||||
if len(passend) < MIN_INLIERS or len(passend) < ID_MIN_SHARE * len(paare):
|
||||
continue
|
||||
return Transform(
|
||||
dx=mdx, dy=mdy, source="ids", csv_file=csv_file, dxf_file=dxf_file,
|
||||
anchor=(f"TeileId = ID-Attribut: {len(paare)} {rolle}-Paare, "
|
||||
f"{len(passend)} davon hoechstens {ID_SPREAD_MM:.0f} mm um den Median"),
|
||||
created=datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def verify_transform(
|
||||
transform: Transform,
|
||||
csv_points: Points,
|
||||
|
||||
Reference in New Issue
Block a user