import { Injectable } from '@angular/core'; import { Observable, forkJoin, of } from 'rxjs'; import { map, catchError } from 'rxjs/operators'; import { RequirementMapping, SceneTemplate, MappingResult, ColorMappingParams, SpaceMappingParams, MaterialMappingParams } from '../models/requirement-mapping.interface'; import { SceneGenerationService } from './scene-generation.service'; import { ParameterMappingService } from './parameter-mapping.service'; @Injectable({ providedIn: 'root' }) export class RequirementMappingService { constructor( private sceneGenerationService: SceneGenerationService, private parameterMappingService: ParameterMappingService ) {} /** * 根据分析结果生成完整的需求映射 * @param analysisResult 分析结果 * @param preferredSceneType 首选场景类型(可选) */ generateRequirementMapping( analysisResult: any, preferredSceneType?: SceneTemplate ): Observable { // 确定场景类型 const sceneType = preferredSceneType || this.determineOptimalSceneType(analysisResult); // 并行执行场景生成和参数映射 return forkJoin({ sceneGeneration: this.sceneGenerationService.generateSceneFromAnalysis(analysisResult, sceneType), parameterMapping: this.parameterMappingService.mapAllParameters(analysisResult) }).pipe( map(({ sceneGeneration, parameterMapping }) => { const requirementMapping: RequirementMapping = { sceneGeneration: { baseScene: sceneType, parameters: sceneGeneration.generatedParams!, atmospherePreview: sceneGeneration.previewUrl || this.generateDefaultPreview(sceneType) }, parameterMapping: { colorParams: parameterMapping.colorParams, spaceParams: parameterMapping.spaceParams, materialParams: parameterMapping.materialParams } }; return requirementMapping; }), catchError(error => { console.error('需求映射生成失败:', error); return of(this.getDefaultRequirementMapping()); }) ); } /** * 生成多个场景选项供用户选择 * @param analysisResult 分析结果 * @param sceneCount 生成场景数量 */ generateMultipleSceneOptions( analysisResult: any, sceneCount: number = 3 ): Observable { const recommendedScenes = this.getRecommendedScenes(analysisResult, sceneCount); const mappingObservables = recommendedScenes.map(sceneType => this.generateRequirementMapping(analysisResult, sceneType) ); return forkJoin(mappingObservables).pipe( catchError(error => { console.error('多场景生成失败:', error); return of([this.getDefaultRequirementMapping()]); }) ); } /** * 更新现有需求映射的参数 * @param currentMapping 当前映射 * @param updatedParams 更新的参数 */ updateRequirementMapping( currentMapping: RequirementMapping, updatedParams: Partial ): Observable { try { const updatedMapping: RequirementMapping = { sceneGeneration: { ...currentMapping.sceneGeneration, ...updatedParams.sceneGeneration }, parameterMapping: { ...currentMapping.parameterMapping, ...updatedParams.parameterMapping } }; // 如果场景参数发生变化,重新生成预览图 if (updatedParams.sceneGeneration?.parameters) { updatedMapping.sceneGeneration.atmospherePreview = this.generateAtmospherePreview( updatedMapping.sceneGeneration.parameters, updatedMapping.sceneGeneration.baseScene ); } return of(updatedMapping); } catch (error) { console.error('需求映射更新失败:', error); return of(currentMapping); } } /** * 验证需求映射的完整性和合理性 * @param mapping 需求映射 */ validateRequirementMapping(mapping: RequirementMapping): Observable<{ isValid: boolean; issues: string[]; suggestions: string[]; }> { const issues: string[] = []; const suggestions: string[] = []; try { // 验证场景生成部分 if (!mapping.sceneGeneration.baseScene) { issues.push('缺少基础场景模板'); } if (!mapping.sceneGeneration.parameters) { issues.push('缺少场景参数'); } else { // 验证场景参数的合理性 const params = mapping.sceneGeneration.parameters; if (params.lighting.primaryLight.intensity < 0 || params.lighting.primaryLight.intensity > 100) { issues.push('主光源强度超出合理范围 (0-100)'); } if (params.lighting.primaryLight.temperature < 1000 || params.lighting.primaryLight.temperature > 10000) { issues.push('色温值超出合理范围 (1000-10000K)'); } if (params.composition.depth < 0 || params.composition.depth > 100) { issues.push('景深值超出合理范围 (0-100)'); } } // 验证参数映射部分 if (!mapping.parameterMapping.colorParams.primaryColors.length) { issues.push('缺少主要颜色定义'); } if (!mapping.parameterMapping.spaceParams.layout.zones.length) { issues.push('缺少空间功能区域定义'); } if (!mapping.parameterMapping.materialParams.surfaceMaterials.length) { issues.push('缺少主要材质定义'); } // 生成优化建议 if (mapping.parameterMapping.colorParams.saturation > 80) { suggestions.push('考虑降低色彩饱和度以获得更舒适的视觉效果'); } if (mapping.parameterMapping.materialParams.textureScale > 80) { suggestions.push('高复杂度纹理可能影响渲染性能,建议适当简化'); } const totalZoneArea = mapping.parameterMapping.spaceParams.layout.zones .reduce((sum: number, zone: any) => sum + zone.area, 0); if (totalZoneArea > 100) { suggestions.push('功能区域总面积超过100%,建议重新分配'); } return of({ isValid: issues.length === 0, issues, suggestions }); } catch (error) { return of({ isValid: false, issues: ['验证过程中发生错误'], suggestions: [] }); } } /** * 导出需求映射为配置文件 * @param mapping 需求映射 * @param format 导出格式 */ exportRequirementMapping( mapping: RequirementMapping, format: 'json' | 'yaml' | 'xml' = 'json' ): Observable { try { let exportContent: string; switch (format) { case 'json': exportContent = JSON.stringify(mapping, null, 2); break; case 'yaml': exportContent = this.convertToYaml(mapping); break; case 'xml': exportContent = this.convertToXml(mapping); break; default: exportContent = JSON.stringify(mapping, null, 2); } return of(exportContent); } catch (error) { console.error('导出需求映射失败:', error); return of(''); } } // ==================== 私有辅助方法 ==================== /** * 确定最优场景类型 */ private determineOptimalSceneType(analysisResult: any): SceneTemplate { // 基于分析结果推断最适合的场景类型 // 检查物体识别结果 if (analysisResult.formAnalysis?.objectRecognition) { const objects = analysisResult.formAnalysis.objectRecognition.identifiedObjects || []; // 卧室相关物品 if (objects.some((obj: any) => ['bed', 'pillow', 'nightstand'].includes(obj.category))) { return analysisResult.enhancedColorAnalysis?.colorPsychology?.primaryMood === 'calm' ? SceneTemplate.BEDROOM_MINIMAL : SceneTemplate.BEDROOM_COZY; } // 客厅相关物品 if (objects.some((obj: any) => ['sofa', 'coffee table', 'tv'].includes(obj.category))) { return analysisResult.formAnalysis?.overallAssessment?.styleComplexity > 60 ? SceneTemplate.LIVING_ROOM_CLASSIC : SceneTemplate.LIVING_ROOM_MODERN; } // 厨房相关物品 if (objects.some((obj: any) => ['stove', 'refrigerator', 'sink'].includes(obj.category))) { return SceneTemplate.KITCHEN_CONTEMPORARY; } // 浴室相关物品 if (objects.some((obj: any) => ['toilet', 'shower', 'bathtub'].includes(obj.category))) { return SceneTemplate.BATHROOM_LUXURY; } } // 基于色彩和风格分析 if (analysisResult.enhancedColorAnalysis?.colorPsychology) { const mood = analysisResult.enhancedColorAnalysis.colorPsychology.primaryMood; switch (mood) { case 'sophisticated': case 'luxurious': return SceneTemplate.LIVING_ROOM_CLASSIC; case 'calm': case 'minimal': return SceneTemplate.BEDROOM_MINIMAL; case 'energetic': case 'modern': return SceneTemplate.LIVING_ROOM_MODERN; default: return SceneTemplate.LIVING_ROOM_MODERN; } } // 默认返回现代客厅 return SceneTemplate.LIVING_ROOM_MODERN; } /** * 获取推荐场景列表 */ private getRecommendedScenes(analysisResult: any, count: number): SceneTemplate[] { const primaryScene = this.determineOptimalSceneType(analysisResult); const allScenes = Object.values(SceneTemplate); // 确保主推荐场景在第一位 const recommendedScenes = [primaryScene]; // 添加其他相关场景 const otherScenes = allScenes.filter(scene => scene !== primaryScene); // 基于分析结果的相似度排序其他场景 const sortedOtherScenes = this.sortScenesBySimilarity(otherScenes, analysisResult); // 取前 count-1 个场景 recommendedScenes.push(...sortedOtherScenes.slice(0, count - 1)); return recommendedScenes; } /** * 根据相似度排序场景 */ private sortScenesBySimilarity(scenes: SceneTemplate[], analysisResult: any): SceneTemplate[] { // 简化的相似度计算,实际项目中可以实现更复杂的算法 return scenes.sort((a, b) => { const scoreA = this.calculateSceneSimilarityScore(a, analysisResult); const scoreB = this.calculateSceneSimilarityScore(b, analysisResult); return scoreB - scoreA; }); } /** * 计算场景相似度分数 */ private calculateSceneSimilarityScore(scene: SceneTemplate, analysisResult: any): number { let score = 0; // 基于色彩心理学匹配 if (analysisResult.enhancedColorAnalysis?.colorPsychology) { const mood = analysisResult.enhancedColorAnalysis.colorPsychology.primaryMood; const moodSceneMap: {[key: string]: SceneTemplate[]} = { 'calm': [SceneTemplate.BEDROOM_MINIMAL, SceneTemplate.BATHROOM_MINIMAL], 'sophisticated': [SceneTemplate.LIVING_ROOM_CLASSIC, SceneTemplate.OFFICE_TRADITIONAL], 'modern': [SceneTemplate.LIVING_ROOM_MODERN, SceneTemplate.KITCHEN_CONTEMPORARY], 'cozy': [SceneTemplate.BEDROOM_COZY, SceneTemplate.DINING_CASUAL] }; if (moodSceneMap[mood]?.includes(scene)) { score += 30; } } // 基于材质匹配 if (analysisResult.textureAnalysis?.materialClassification) { const primaryMaterial = analysisResult.textureAnalysis.materialClassification.primaryMaterial?.category; const materialSceneMap: {[key: string]: SceneTemplate[]} = { 'wood': [SceneTemplate.LIVING_ROOM_CLASSIC, SceneTemplate.BEDROOM_COZY], 'metal': [SceneTemplate.KITCHEN_CONTEMPORARY, SceneTemplate.OFFICE_MODERN], 'ceramic': [SceneTemplate.BATHROOM_LUXURY, SceneTemplate.KITCHEN_CONTEMPORARY], 'fabric': [SceneTemplate.BEDROOM_COZY, SceneTemplate.LIVING_ROOM_CLASSIC] }; if (materialSceneMap[primaryMaterial]?.includes(scene)) { score += 20; } } return score; } /** * 生成氛围感预览图 */ private generateAtmospherePreview(params: any, sceneType: SceneTemplate): string { // 这里应该调用实际的预览图生成服务 // 目前返回模拟的URL const baseUrl = document.baseURI+'/assets/previews/atmosphere/'; const sceneId = sceneType.toString(); const timestamp = Date.now(); return `${baseUrl}${sceneId}_${timestamp}.jpg`; } /** * 生成默认预览图 */ private generateDefaultPreview(sceneType: SceneTemplate): string { return document.baseURI+`/assets/previews/default/${sceneType}.jpg`; } /** * 获取默认需求映射 */ private getDefaultRequirementMapping(): RequirementMapping { return { sceneGeneration: { baseScene: SceneTemplate.LIVING_ROOM_MODERN, parameters: this.sceneGenerationService.getSceneTemplate(SceneTemplate.LIVING_ROOM_MODERN), atmospherePreview: this.generateDefaultPreview(SceneTemplate.LIVING_ROOM_MODERN) }, parameterMapping: { colorParams: { primaryColors: [ { originalColor: '#FFFFFF', mappedColor: '#FFFFFF', weight: 40, usage: 'primary' }, { originalColor: '#F5F5F5', mappedColor: '#F5F5F5', weight: 30, usage: 'secondary' }, { originalColor: '#E0E0E0', mappedColor: '#E0E0E0', weight: 30, usage: 'background' } ], colorHarmony: 'monochromatic', saturation: 50, brightness: 70, contrast: 50, temperature: 'neutral' }, spaceParams: { dimensions: { width: 400, height: 280, depth: 400, unit: 'meter' }, layout: { type: 'open', flow: 'linear', zones: [] }, scale: { furniture: 50, ceiling: 60, openness: 70 } }, materialParams: { surfaceMaterials: [], textureScale: 50, reflectivity: 30, roughness: 40, metallic: 10 } } }; } /** * 转换为YAML格式 */ private convertToYaml(mapping: RequirementMapping): string { // 简化的YAML转换,实际项目中建议使用专门的YAML库 return `# 需求映射配置文件 sceneGeneration: baseScene: "${mapping.sceneGeneration.baseScene}" atmospherePreview: "${mapping.sceneGeneration.atmospherePreview}" parameters: lighting: primaryLight: type: "${mapping.sceneGeneration.parameters.lighting.primaryLight.type}" intensity: ${mapping.sceneGeneration.parameters.lighting.primaryLight.intensity} temperature: ${mapping.sceneGeneration.parameters.lighting.primaryLight.temperature} direction: "${mapping.sceneGeneration.parameters.lighting.primaryLight.direction}" # ... 其他参数 parameterMapping: colorParams: primaryColors: [${mapping.parameterMapping.colorParams.primaryColors.map(c => `"${c}"`).join(', ')}] colorHarmony: "${mapping.parameterMapping.colorParams.colorHarmony}" saturation: ${mapping.parameterMapping.colorParams.saturation} brightness: ${mapping.parameterMapping.colorParams.brightness} # ... 其他映射参数`; } /** * 转换为XML格式 */ private convertToXml(mapping: RequirementMapping): string { // 简化的XML转换 return ` ${mapping.sceneGeneration.baseScene} ${mapping.sceneGeneration.atmospherePreview} ${mapping.parameterMapping.colorParams.primaryColors.join(',')} ${mapping.parameterMapping.colorParams.colorHarmony} ${mapping.parameterMapping.colorParams.saturation} ${mapping.parameterMapping.colorParams.brightness} `; } }