Spaces:
Paused
Paused
| # aduc_framework/engineers/planner_3d.py | |
| # | |
| # Copyright (C) August 4, 2025 Carlos Rodrigues dos Santos | |
| # | |
| # Versão 4.1.0 (DNA Seguro por Cena) | |
| # | |
| # - Remove o acúmulo global de keyframes e a divisão por índices. | |
| # - Atualiza cada cena diretamente com os keyframes gerados pelo Painter. | |
| # - Garante que o DNA final nunca fique “bagunçado”. | |
| # - Foco na simplicidade: Planner orquestra, Painter gera, DNA organiza. | |
| import logging | |
| import json | |
| from typing import List, Dict, Any, Generator | |
| from .deformes3D import deformes3d_engine_singleton | |
| from ..types import KeyframeGenerationJob | |
| logger = logging.getLogger(__name__) | |
| class Planner3D: | |
| """ | |
| Gerente de projeto para a fase de keyframes. | |
| Prepara e despacha ordens de serviço para o Painter (Deformes3D), | |
| e injeta os keyframes diretamente no DNA cena a cena. | |
| """ | |
| def generate_keyframes_from_dna( | |
| self, | |
| generation_state: Dict[str, Any], | |
| initial_chat_history: List[Dict[str, Any]] | |
| ) -> Generator[Dict[str, Any], None, None]: | |
| painter = deformes3d_engine_singleton | |
| chat_history = initial_chat_history.copy() | |
| chat_history.append({ | |
| "role": "Planner3D", | |
| "content": "Roteiro recebido. Iniciando criação de keyframes..." | |
| }) | |
| yield {"chat": chat_history, "gallery": [], "dna": generation_state} | |
| # 1. Extrai dados globais | |
| scenes_list = generation_state.get("scenes", []) | |
| ref_image_paths = [ref['caminho'] for ref in generation_state.get("midias_referencia", [])] | |
| global_prompt = generation_state.get("prompt_geral", "") | |
| pre_prod_params = generation_state.get("parametros_geracao", {}).get("pre_producao", {}) | |
| if not scenes_list or not ref_image_paths: | |
| logger.warning("Planner3D: Nenhuma cena ou imagem de referência encontrada no DNA.") | |
| yield {"chat": chat_history, "status": "keyframes_complete"} | |
| return | |
| # Mapeamento fixo de imagens de referência | |
| ref_id_to_path_map = {i: path for i, path in enumerate(ref_image_paths)} | |
| available_ref_ids = list(ref_id_to_path_map.keys()) | |
| # 2. Loop de cenas | |
| for scene_idx, scene_data in enumerate(scenes_list): | |
| scene_id = scene_data.get("id") | |
| chat_history.append({ | |
| "role": "Planner3D", | |
| "content": f"Desenhando a Cena {scene_id}/{len(scenes_list)}..." | |
| }) | |
| yield {"chat": chat_history} | |
| storyboard_da_cena = [ato.get("resumo_ato", "") for ato in scene_data.get("atos", [])] | |
| if not storyboard_da_cena: | |
| logger.info(f"Planner3D: Cena {scene_id} sem atos, pulando.") | |
| continue | |
| # 3. Cria ordem de serviço | |
| job = KeyframeGenerationJob( | |
| storyboard=storyboard_da_cena, | |
| global_prompt=global_prompt, | |
| ref_image_paths=ref_image_paths, | |
| ref_id_to_path_map=ref_id_to_path_map, | |
| available_ref_ids=available_ref_ids, | |
| keyframe_prefix=f"scene_{scene_id:02d}", | |
| params=pre_prod_params | |
| ) | |
| # 4. Delega para o Painter | |
| scene_keyframes = painter.generate_keyframes_from_job(job=job) | |
| # 5. Injeta diretamente no DNA | |
| scene_data["keyframes"] = scene_keyframes | |
| if scene_keyframes: | |
| chat_history.append({ | |
| "role": "Planner3D", | |
| "content": f"Cena {scene_id} concluída ({len(scene_keyframes)} keyframes)." | |
| }) | |
| yield {"chat": chat_history} | |
| logger.info("Planner3D: Geração de todos os keyframes concluída.") | |
| yield { | |
| "chat": chat_history, | |
| "gallery": [kf["caminho_pixel"] for scene in scenes_list for kf in scene.get("keyframes", [])], | |
| "status": "keyframes_complete", | |
| "dna": generation_state | |
| } | |
| planner_3d_singleton = Planner3D() | |