File size: 2,168 Bytes
1993d5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
"""Geometry Normalizer: Normalizes coordinates, scales, frames, and removes degenerate geometries."""

from __future__ import annotations

import logging
import numpy as np
from typing import Dict, List, Tuple

logger = logging.getLogger(__name__)


class GeometryNormalizer:
    """Standardizes point naming, coordinate scaling, centering, and precision."""

    def normalize_coordinates(
        self,
        coords: Dict[str, List[float]],
        target_span: float = 8.0,
        is_3d: bool = False,
    ) -> Dict[str, List[float]]:
        if not coords:
            return {}

        cleaned: Dict[str, List[float]] = {}
        for pid, pt in coords.items():
            cleaned[pid] = [
                0.0 if abs(val) < 1e-6 else float(np.round(val, 6))
                for val in pt
            ]

        # Calculate bounding box
        all_pts = np.array(list(cleaned.values()))
        if len(all_pts) == 0:
            return cleaned

        mins = np.min(all_pts, axis=0)
        maxs = np.max(all_pts, axis=0)
        spans = maxs - mins
        max_span = float(np.max(spans))

        # If geometry is valid and non-degenerate, scale into comfortable viewing range
        if max_span > 1e-4:
            scale = target_span / max_span
            center = (mins + maxs) / 2.0

            normalized: Dict[str, List[float]] = {}
            for pid, pt in cleaned.items():
                pt_arr = np.array(pt)
                norm_pt = (pt_arr - center) * scale
                if not is_3d:
                    # In 2D, pin z to 0 and center in 2D plane
                    normalized[pid] = [
                        float(np.round(norm_pt[0], 4)),
                        float(np.round(norm_pt[1], 4)),
                        0.0,
                    ]
                else:
                    # In 3D, keep z >= 0 ground orientation when appropriate
                    normalized[pid] = [
                        float(np.round(norm_pt[0], 4)),
                        float(np.round(norm_pt[1], 4)),
                        float(np.round(norm_pt[2], 4)),
                    ]
            return normalized

        return cleaned