You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.3 KiB

Sistema de Vistas - Integración Completa

📐 Conceptos clave

Cada Section tiene múltiples Views (vistas) que representan diferentes ángulos o perspectivas visuales de esa sección.

Section (ej: Salón)
├── View: Front (frontal)
├── View: Side (lateral)
└── View: Top (cenital)

🏗️ Estructura

Section (actualizada)

interface Section {
    id: SectionID;
    name: I18nString;
    attrs: Attribute[];
    
    // ✅ NUEVO: Vistas disponibles (SIEMPRE presente)
    views: Record<ViewID, SectionView>;
    
    // ✅ NUEVO: Vista por defecto (SIEMPRE presente)
    defaultView: ViewID;
    
    availability: SectionAvailability;
}

SectionView

interface SectionView {
    id: ViewID;                        // 'vw:front', 'vw:side', 'vw:top'
    name: I18nString;
    description?: I18nString;
    visualConfig: SectionVisualConfig; // Configuración ESPECÍFICA de esta vista
    order?: number;
    icon?: string;
}

📝 Ejemplo completo

import type { Section, SectionView } from '@/types';

const VIEW_FRONT : ViewID = 'vw:front';
const VIEW_SIDE  : ViewID = 'vw:side';
const VIEW_TOP   : ViewID = 'vw:top';

const seccionSalon: Section = {
    id: 'sc:salon',
    name: { es: 'Salón', en: 'Living room' },
    description: { es: 'Salón - comedor principal', en: 'Main living - dining room' },
    
    attrs: [
        {
            id: 'at:suelo',
            name: { es: 'Suelo', en: 'Flooring' },
            type: 'dynamic',
            dataType: 'reference',
            defaultValue: 'op:suelo_ceramica',
            options: [
                { optionId: 'op:suelo_ceramica' },
                { optionId: 'op:suelo_parquet' }
            ],
            display: { uiVisible: true, affectsVisual: true, affectsPrice: true }
        },
        {
            id: 'at:pared',
            name: { es: 'Color paredes', en: 'Wall color' },
            type: 'dynamic',
            dataType: 'reference',
            defaultValue: 'op:pared_blanco',
            options: [
                { optionId: 'op:pared_blanco' },
                { optionId: 'op:pared_gris' }
            ],
            display: { uiVisible: true, affectsVisual: true }
        }
    ],
    
    // ✅ VISTAS: Diferentes ángulos de la misma sección
    views: {
        [VIEW_FRONT]: {
            id: VIEW_FRONT,
            name: { es: 'Vista frontal', en: 'Front view' },
            description: { es: 'Vista de frente del salón', en: 'Front view of living room' },
            visualConfig: {
                strategy: 'static_image',
                // Template incluye {view} para diferenciar ángulos
                imageUrlTemplate: '{basePath}/{section}/{view}/{at_suelo}/{at_pared}.jpg',
                basePath: '/renders/vivienda',
                globalAttrDependencies: ['at:calidad'],
                sectionAttrDependencies: ['at:suelo', 'at:pared']
            },
            order: 1,
            icon: 'camera-front'
        },
        
        [VIEW_SIDE]: {
            id: VIEW_SIDE,
            name: { es: 'Vista lateral', en: 'Side view' },
            description: { es: 'Vista lateral del salón', en: 'Side view of living room' },
            visualConfig: {
                strategy: 'static_image',
                imageUrlTemplate: '{basePath}/{section}/{view}/{at_suelo}.jpg',
                basePath: '/renders/vivienda',
                globalAttrDependencies: ['at:calidad'],
                // Solo el suelo afecta esta vista
                sectionAttrDependencies: ['at:suelo']
            },
            order: 2,
            icon: 'camera-side'
        },
        
        [VIEW_TOP]: {
            id: VIEW_TOP,
            name: { es: 'Vista cenital', en: 'Top view' },
            description: { es: 'Vista desde arriba', en: 'Top-down view' },
            visualConfig: {
                strategy: 'static_image',
                imageUrlTemplate: '{basePath}/{section}/{view}/{at_suelo}/{at_muebles}.jpg',
                basePath: '/renders/vivienda',
                globalAttrDependencies: ['at:calidad'],
                // Vista cenital muestra suelo y distribución de muebles
                sectionAttrDependencies: ['at:suelo', 'at:muebles']
            },
            order: 3,
            icon: 'camera-top'
        }
    },
    
    // ✅ Vista por defecto (la primera que se muestra al usuario)
    defaultView: VIEW_FRONT,
    
    availability: { mode: 'required' }
};

🔄 Generación de URLs

Placeholders en imageUrlTemplate

El template soporta estos placeholders:

Placeholder Reemplazado por Ejemplo
{basePath} basePath de la config /renders/vivienda
{section} ID de sección sin prefijo salon (de sc:salon)
{view} ID de vista sin prefijo front (de vw:front)
{at_<id>} Valor del atributo normalizado parquet (de at:suelo = op:suelo_parquet)

Ejemplo de resolución

Estado:

const state = {
    'at:suelo': 'op:suelo_parquet',
    'at:pared': 'op:pared_gris',
    'at:calidad': 'op:calidad_premium'
};

Template:

{basePath}/{section}/{view}/{at_suelo}/{at_pared}.jpg

URL resultante para vista frontal:

/renders/vivienda/salon/front/parquet/gris.jpg

URL resultante para vista lateral:

/renders/vivienda/salon/side/parquet.jpg

🎨 Uso en el UI

// Obtener sección
const salon = apartamento.sections['sc:salon'];

// Listar todas las vistas disponibles
const vistasFront = Object.values(salon.views)
    .sort((a, b) => (a.order || 0) - (b.order || 0));

console.log(vistasFront);
// [
//   { id: 'vw:front', name: { es: 'Vista frontal' }, ... },
//   { id: 'vw:side', name: { es: 'Vista lateral' }, ... },
//   { id: 'vw:top', name: { es: 'Vista cenital' }, ... }
// ]

// Vista activa (por defecto)
const vistaActual = salon.views[salon.defaultView];

// Cambiar vista
const nuevaVista = salon.views['vw:side'];

// Generar URL de la vista actual
const urlImagen = generarImagenUrl(
    salon.id,
    vistaActual.id,
    vistaActual.visualConfig,
    state
);

🏭 Estrategias de renderizado por vista

Cada vista puede tener su propia estrategia:

views: {
    'vw:front': {
        id: 'vw:front',
        name: { es: 'Frontal', en: 'Front' },
        visualConfig: {
            strategy: 'static_image',  // ← Imagen estática
            imageUrlTemplate: '...'
        }
    },
    'vw:360': {
        id: 'vw:360',
        name: { es: 'Vista 360º', en: '360º view' },
        visualConfig: {
            strategy: 'three_d',  // ← Modelo 3D interactivo
            threeDConfig: {
                modelUrl: '/models/salon.glb',
                textureAttrs: [
                    { attrId: 'at:suelo', scope: 'section' },
                    { attrId: 'at:pared', scope: 'section' }
                ]
            }
        }
    },
    'vw:ar': {
        id: 'vw:ar',
        name: { es: 'Realidad Aumentada', en: 'AR' },
        visualConfig: {
            strategy: 'api_generated',  // ← API externa
            apiConfig: {
                endpoint: 'https://ar.example.com/generate',
                method: 'POST',
                timeout: 10000
            }
        }
    }
}

📱 Vistas contextuales

Las vistas pueden mostrarse condicionalmente:

{
    id: 'vw:detail_plumbing',
    name: { es: 'Detalle fontanería', en: 'Plumbing detail' },
    visualConfig: {
        strategy: 'static_image',
        imageUrlTemplate: '{basePath}/{section}/detail/{at_sanitario}.jpg'
    },
    // Nota: No hay campo 'visible' aquí porque las vistas 
    // siempre están disponibles. El UI decide si mostrarlas.
    order: 10  // Ordenar al final
}

🔑 Claves de diseño

1. Cada sección DEBE tener al menos una vista

// ❌ MAL
views: {}

// ✅ BIEN
views: {
    'vw:default': {
        id: 'vw:default',
        name: { es: 'Vista principal', en: 'Main view' },
        visualConfig: { /* ... */ }
    }
}

2. defaultView DEBE existir en views

// ❌ MAL
views: { 'vw:front': { /* ... */ } },
defaultView: 'vw:side'  // ← No existe!

// ✅ BIEN
views: { 
    'vw:front': { /* ... */ },
    'vw:side': { /* ... */ }
},
defaultView: 'vw:front'  // ← Existe

3. Los atributos que afectan CUALQUIER vista deben estar en dependencies

// Vista frontal usa: suelo, pared
// Vista lateral usa: suelo
// Vista cenital usa: suelo, muebles

// En la sección, marca affectsVisual: true para todos
attrs: [
    { id: 'at:suelo', display: { affectsVisual: true } },   // ✅ Afecta 3 vistas
    { id: 'at:pared', display: { affectsVisual: true } },   // ✅ Afecta 1 vista
    { id: 'at:muebles', display: { affectsVisual: true } }  // ✅ Afecta 1 vista
]

🎯 Ventajas del sistema de vistas

✅ Flexibilidad: Cada vista puede tener diferente estrategia de renderizado
✅ Optimización: Solo cargar assets de la vista activa
✅ Claridad: Separación explícita de ángulos/perspectivas
✅ Escalabilidad: Añadir nuevas vistas sin modificar la estructura
✅ UX: Usuario puede cambiar de vista según necesidad

📚 Recursos

Powered by TurnKey Linux.