File size: 5,145 Bytes
ce82348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# src/layout_generator/assembler.py

"""

Шаг пайплайна для физической сборки 3D-сцены в непрерывном пространстве.

"""
import logging
import os
from typing import Any, List

import numpy as np
import scene_synthesizer as synth
import trimesh

from dsynth.assets.ss_assets import DefaultShelf
from dsynth.scene_gen.arrangements import add_objects_to_shelf_v2, set_shelf

from .base import BaseStep, LayoutContext

logger: logging.Logger = logging.getLogger(__name__)

class AssembleSceneStep(BaseStep):
    """Шаг физического рендера 3D-сцены на основе расстановки из тензорного поля."""
    
    def process(self, context: LayoutContext) -> LayoutContext:
        """Сборка 3D-моделей по заданным координатам."""
        if not context.is_gen or not hasattr(context, 'placed_fixtures'):
            return context
            
        logger.info("🛠️ Сборка 3D-сцены и физическая выкладка инвентаря...")
        scene: synth.Scene = synth.Scene()
        actual_shelf_idx: int = 0

        for fixture in context.placed_fixtures:
            actual_shelf_idx += 1
            shelf_name: str = fixture.name
            zone_id, shelf_id = shelf_name.split('.')
            shelf_cfg: Any = context.fixed_zones[zone_id][shelf_id]
            shelf_asset_name: Any = shelf_cfg.shelf_asset

            asset_obj: Any = context.product_assets_lib.get(shelf_asset_name)
            if asset_obj is None and shelf_asset_name:
                asset_obj = context.product_assets_lib.get(f"fixtures.{shelf_asset_name}")

            shelf: Any = DefaultShelf if asset_obj is None else asset_obj.ss_asset
            
            # Извлекаем идеальные координаты и угол, посчитанные тензорным полем
            safe_x: float = fixture.x
            safe_y: float = fixture.y
            rotation_deg: int = getattr(fixture, '_rotation_deg', 0)

            # Ставим оборудование на сцену
            support_data: Any = set_shelf(
                scene, shelf, safe_x, safe_y, rotation_deg,
                f'SHELF_{actual_shelf_idx}_{shelf_name}', f'support_SHELF_{actual_shelf_idx}_{shelf_name}'
            )

            # Выкладка товаров
            if shelf_name in context.product_filling:
                placement_data: List[List[str]] = context.product_filling[shelf_name]
                if placement_data:
                    try:
                        add_objects_to_shelf_v2(
                            scene, actual_shelf_idx, placement_data, context.product_assets_lib,
                            support_data, shelf_cfg.x_gap, shelf_cfg.y_gap, shelf_cfg.delta_x, shelf_cfg.delta_y,
                            shelf_cfg.start_point_x, shelf_cfg.start_point_y, shelf_cfg.filling_type,
                            seed=context.current_seed, noise_std_x=shelf_cfg.noise_std_x, noise_std_y=shelf_cfg.noise_std_y,
                            rotation_lower=shelf_cfg.rotation_lower, rotation_upper=shelf_cfg.rotation_upper
                        )
                    except Exception as e:
                        logger.error(f"Ошибка выкладки на полку {shelf_name}: {e}")

        # Динамическая генерация пола по габаритам расставленной мебели
        margin: float = 2.0
        t: float = 0.2
        
        if context.placed_fixtures:
            all_x: List[float] = []
            all_y: List[float] = []
            for f in context.placed_fixtures:
                poly, _ = f.get_polygon()
                all_x.extend(poly[:, 0])
                all_y.extend(poly[:, 1])
            min_x, max_x = min(all_x), max(all_x)
            min_y, max_y = min(all_y), max(all_y)
        else:
            min_x, max_x = 0.0, float(context.size_n)
            min_y, max_y = 0.0, float(context.size_m)

        cx: float = (max_x + min_x) / 2.0
        cy: float = (max_y + min_y) / 2.0
        w_x: float = (max_x - min_x) + margin * 2
        w_y: float = (max_y - min_y) + margin * 2

        temp_filename: str = f"temp_scene_{os.getpid()}.glb"
        scene.export(temp_filename)
        final_scene: trimesh.Scene = trimesh.load(temp_filename, force='scene')
        
        if os.path.exists(temp_filename):
            os.remove(temp_filename)

        floor: trimesh.Trimesh = trimesh.creation.box(extents=[w_x, w_y, t])
        floor.apply_translation([cx, cy, -t/2])
        floor.visual.face_colors = [180, 180, 180, 255]
        final_scene.add_geometry(floor, node_name="floor")

        R: np.ndarray = trimesh.transformations.rotation_matrix(-np.pi / 2, [1, 0, 0])
        final_scene.apply_transform(R)

        context.final_scene = final_scene
        context.actual_shelf_idx = actual_shelf_idx
        
        return context