requirement-mapping.service.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. import { Injectable } from '@angular/core';
  2. import { Observable, forkJoin, of } from 'rxjs';
  3. import { map, catchError } from 'rxjs/operators';
  4. import {
  5. RequirementMapping,
  6. SceneTemplate,
  7. MappingResult,
  8. ColorMappingParams,
  9. SpaceMappingParams,
  10. MaterialMappingParams
  11. } from '../models/requirement-mapping.interface';
  12. import { SceneGenerationService } from './scene-generation.service';
  13. import { ParameterMappingService } from './parameter-mapping.service';
  14. @Injectable({
  15. providedIn: 'root'
  16. })
  17. export class RequirementMappingService {
  18. constructor(
  19. private sceneGenerationService: SceneGenerationService,
  20. private parameterMappingService: ParameterMappingService
  21. ) {}
  22. /**
  23. * 根据分析结果生成完整的需求映射
  24. * @param analysisResult 分析结果
  25. * @param preferredSceneType 首选场景类型(可选)
  26. */
  27. generateRequirementMapping(
  28. analysisResult: any,
  29. preferredSceneType?: SceneTemplate
  30. ): Observable<RequirementMapping> {
  31. // 确定场景类型
  32. const sceneType = preferredSceneType || this.determineOptimalSceneType(analysisResult);
  33. // 并行执行场景生成和参数映射
  34. return forkJoin({
  35. sceneGeneration: this.sceneGenerationService.generateSceneFromAnalysis(analysisResult, sceneType),
  36. parameterMapping: this.parameterMappingService.mapAllParameters(analysisResult)
  37. }).pipe(
  38. map(({ sceneGeneration, parameterMapping }) => {
  39. const requirementMapping: RequirementMapping = {
  40. sceneGeneration: {
  41. baseScene: sceneType,
  42. parameters: sceneGeneration.generatedParams!,
  43. atmospherePreview: sceneGeneration.previewUrl || this.generateDefaultPreview(sceneType)
  44. },
  45. parameterMapping: {
  46. colorParams: parameterMapping.colorParams,
  47. spaceParams: parameterMapping.spaceParams,
  48. materialParams: parameterMapping.materialParams
  49. }
  50. };
  51. return requirementMapping;
  52. }),
  53. catchError(error => {
  54. console.error('需求映射生成失败:', error);
  55. return of(this.getDefaultRequirementMapping());
  56. })
  57. );
  58. }
  59. /**
  60. * 生成多个场景选项供用户选择
  61. * @param analysisResult 分析结果
  62. * @param sceneCount 生成场景数量
  63. */
  64. generateMultipleSceneOptions(
  65. analysisResult: any,
  66. sceneCount: number = 3
  67. ): Observable<RequirementMapping[]> {
  68. const recommendedScenes = this.getRecommendedScenes(analysisResult, sceneCount);
  69. const mappingObservables = recommendedScenes.map(sceneType =>
  70. this.generateRequirementMapping(analysisResult, sceneType)
  71. );
  72. return forkJoin(mappingObservables).pipe(
  73. catchError(error => {
  74. console.error('多场景生成失败:', error);
  75. return of([this.getDefaultRequirementMapping()]);
  76. })
  77. );
  78. }
  79. /**
  80. * 更新现有需求映射的参数
  81. * @param currentMapping 当前映射
  82. * @param updatedParams 更新的参数
  83. */
  84. updateRequirementMapping(
  85. currentMapping: RequirementMapping,
  86. updatedParams: Partial<RequirementMapping>
  87. ): Observable<RequirementMapping> {
  88. try {
  89. const updatedMapping: RequirementMapping = {
  90. sceneGeneration: {
  91. ...currentMapping.sceneGeneration,
  92. ...updatedParams.sceneGeneration
  93. },
  94. parameterMapping: {
  95. ...currentMapping.parameterMapping,
  96. ...updatedParams.parameterMapping
  97. }
  98. };
  99. // 如果场景参数发生变化,重新生成预览图
  100. if (updatedParams.sceneGeneration?.parameters) {
  101. updatedMapping.sceneGeneration.atmospherePreview =
  102. this.generateAtmospherePreview(
  103. updatedMapping.sceneGeneration.parameters,
  104. updatedMapping.sceneGeneration.baseScene
  105. );
  106. }
  107. return of(updatedMapping);
  108. } catch (error) {
  109. console.error('需求映射更新失败:', error);
  110. return of(currentMapping);
  111. }
  112. }
  113. /**
  114. * 验证需求映射的完整性和合理性
  115. * @param mapping 需求映射
  116. */
  117. validateRequirementMapping(mapping: RequirementMapping): Observable<{
  118. isValid: boolean;
  119. issues: string[];
  120. suggestions: string[];
  121. }> {
  122. const issues: string[] = [];
  123. const suggestions: string[] = [];
  124. try {
  125. // 验证场景生成部分
  126. if (!mapping.sceneGeneration.baseScene) {
  127. issues.push('缺少基础场景模板');
  128. }
  129. if (!mapping.sceneGeneration.parameters) {
  130. issues.push('缺少场景参数');
  131. } else {
  132. // 验证场景参数的合理性
  133. const params = mapping.sceneGeneration.parameters;
  134. if (params.lighting.primaryLight.intensity < 0 || params.lighting.primaryLight.intensity > 100) {
  135. issues.push('主光源强度超出合理范围 (0-100)');
  136. }
  137. if (params.lighting.primaryLight.temperature < 1000 || params.lighting.primaryLight.temperature > 10000) {
  138. issues.push('色温值超出合理范围 (1000-10000K)');
  139. }
  140. if (params.composition.depth < 0 || params.composition.depth > 100) {
  141. issues.push('景深值超出合理范围 (0-100)');
  142. }
  143. }
  144. // 验证参数映射部分
  145. if (!mapping.parameterMapping.colorParams.primaryColors.length) {
  146. issues.push('缺少主要颜色定义');
  147. }
  148. if (!mapping.parameterMapping.spaceParams.layout.zones.length) {
  149. issues.push('缺少空间功能区域定义');
  150. }
  151. if (!mapping.parameterMapping.materialParams.surfaceMaterials.length) {
  152. issues.push('缺少主要材质定义');
  153. }
  154. // 生成优化建议
  155. if (mapping.parameterMapping.colorParams.saturation > 80) {
  156. suggestions.push('考虑降低色彩饱和度以获得更舒适的视觉效果');
  157. }
  158. if (mapping.parameterMapping.materialParams.textureScale > 80) {
  159. suggestions.push('高复杂度纹理可能影响渲染性能,建议适当简化');
  160. }
  161. const totalZoneArea = mapping.parameterMapping.spaceParams.layout.zones
  162. .reduce((sum: number, zone: any) => sum + zone.area, 0);
  163. if (totalZoneArea > 100) {
  164. suggestions.push('功能区域总面积超过100%,建议重新分配');
  165. }
  166. return of({
  167. isValid: issues.length === 0,
  168. issues,
  169. suggestions
  170. });
  171. } catch (error) {
  172. return of({
  173. isValid: false,
  174. issues: ['验证过程中发生错误'],
  175. suggestions: []
  176. });
  177. }
  178. }
  179. /**
  180. * 导出需求映射为配置文件
  181. * @param mapping 需求映射
  182. * @param format 导出格式
  183. */
  184. exportRequirementMapping(
  185. mapping: RequirementMapping,
  186. format: 'json' | 'yaml' | 'xml' = 'json'
  187. ): Observable<string> {
  188. try {
  189. let exportContent: string;
  190. switch (format) {
  191. case 'json':
  192. exportContent = JSON.stringify(mapping, null, 2);
  193. break;
  194. case 'yaml':
  195. exportContent = this.convertToYaml(mapping);
  196. break;
  197. case 'xml':
  198. exportContent = this.convertToXml(mapping);
  199. break;
  200. default:
  201. exportContent = JSON.stringify(mapping, null, 2);
  202. }
  203. return of(exportContent);
  204. } catch (error) {
  205. console.error('导出需求映射失败:', error);
  206. return of('');
  207. }
  208. }
  209. // ==================== 私有辅助方法 ====================
  210. /**
  211. * 确定最优场景类型
  212. */
  213. private determineOptimalSceneType(analysisResult: any): SceneTemplate {
  214. // 基于分析结果推断最适合的场景类型
  215. // 检查物体识别结果
  216. if (analysisResult.formAnalysis?.objectRecognition) {
  217. const objects = analysisResult.formAnalysis.objectRecognition.identifiedObjects || [];
  218. // 卧室相关物品
  219. if (objects.some((obj: any) => ['bed', 'pillow', 'nightstand'].includes(obj.category))) {
  220. return analysisResult.enhancedColorAnalysis?.colorPsychology?.primaryMood === 'calm'
  221. ? SceneTemplate.BEDROOM_MINIMAL
  222. : SceneTemplate.BEDROOM_COZY;
  223. }
  224. // 客厅相关物品
  225. if (objects.some((obj: any) => ['sofa', 'coffee table', 'tv'].includes(obj.category))) {
  226. return analysisResult.formAnalysis?.overallAssessment?.styleComplexity > 60
  227. ? SceneTemplate.LIVING_ROOM_CLASSIC
  228. : SceneTemplate.LIVING_ROOM_MODERN;
  229. }
  230. // 厨房相关物品
  231. if (objects.some((obj: any) => ['stove', 'refrigerator', 'sink'].includes(obj.category))) {
  232. return SceneTemplate.KITCHEN_CONTEMPORARY;
  233. }
  234. // 浴室相关物品
  235. if (objects.some((obj: any) => ['toilet', 'shower', 'bathtub'].includes(obj.category))) {
  236. return SceneTemplate.BATHROOM_LUXURY;
  237. }
  238. }
  239. // 基于色彩和风格分析
  240. if (analysisResult.enhancedColorAnalysis?.colorPsychology) {
  241. const mood = analysisResult.enhancedColorAnalysis.colorPsychology.primaryMood;
  242. switch (mood) {
  243. case 'sophisticated':
  244. case 'luxurious':
  245. return SceneTemplate.LIVING_ROOM_CLASSIC;
  246. case 'calm':
  247. case 'minimal':
  248. return SceneTemplate.BEDROOM_MINIMAL;
  249. case 'energetic':
  250. case 'modern':
  251. return SceneTemplate.LIVING_ROOM_MODERN;
  252. default:
  253. return SceneTemplate.LIVING_ROOM_MODERN;
  254. }
  255. }
  256. // 默认返回现代客厅
  257. return SceneTemplate.LIVING_ROOM_MODERN;
  258. }
  259. /**
  260. * 获取推荐场景列表
  261. */
  262. private getRecommendedScenes(analysisResult: any, count: number): SceneTemplate[] {
  263. const primaryScene = this.determineOptimalSceneType(analysisResult);
  264. const allScenes = Object.values(SceneTemplate);
  265. // 确保主推荐场景在第一位
  266. const recommendedScenes = [primaryScene];
  267. // 添加其他相关场景
  268. const otherScenes = allScenes.filter(scene => scene !== primaryScene);
  269. // 基于分析结果的相似度排序其他场景
  270. const sortedOtherScenes = this.sortScenesBySimilarity(otherScenes, analysisResult);
  271. // 取前 count-1 个场景
  272. recommendedScenes.push(...sortedOtherScenes.slice(0, count - 1));
  273. return recommendedScenes;
  274. }
  275. /**
  276. * 根据相似度排序场景
  277. */
  278. private sortScenesBySimilarity(scenes: SceneTemplate[], analysisResult: any): SceneTemplate[] {
  279. // 简化的相似度计算,实际项目中可以实现更复杂的算法
  280. return scenes.sort((a, b) => {
  281. const scoreA = this.calculateSceneSimilarityScore(a, analysisResult);
  282. const scoreB = this.calculateSceneSimilarityScore(b, analysisResult);
  283. return scoreB - scoreA;
  284. });
  285. }
  286. /**
  287. * 计算场景相似度分数
  288. */
  289. private calculateSceneSimilarityScore(scene: SceneTemplate, analysisResult: any): number {
  290. let score = 0;
  291. // 基于色彩心理学匹配
  292. if (analysisResult.enhancedColorAnalysis?.colorPsychology) {
  293. const mood = analysisResult.enhancedColorAnalysis.colorPsychology.primaryMood;
  294. const moodSceneMap: {[key: string]: SceneTemplate[]} = {
  295. 'calm': [SceneTemplate.BEDROOM_MINIMAL, SceneTemplate.BATHROOM_MINIMAL],
  296. 'sophisticated': [SceneTemplate.LIVING_ROOM_CLASSIC, SceneTemplate.OFFICE_TRADITIONAL],
  297. 'modern': [SceneTemplate.LIVING_ROOM_MODERN, SceneTemplate.KITCHEN_CONTEMPORARY],
  298. 'cozy': [SceneTemplate.BEDROOM_COZY, SceneTemplate.DINING_CASUAL]
  299. };
  300. if (moodSceneMap[mood]?.includes(scene)) {
  301. score += 30;
  302. }
  303. }
  304. // 基于材质匹配
  305. if (analysisResult.textureAnalysis?.materialClassification) {
  306. const primaryMaterial = analysisResult.textureAnalysis.materialClassification.primaryMaterial?.category;
  307. const materialSceneMap: {[key: string]: SceneTemplate[]} = {
  308. 'wood': [SceneTemplate.LIVING_ROOM_CLASSIC, SceneTemplate.BEDROOM_COZY],
  309. 'metal': [SceneTemplate.KITCHEN_CONTEMPORARY, SceneTemplate.OFFICE_MODERN],
  310. 'ceramic': [SceneTemplate.BATHROOM_LUXURY, SceneTemplate.KITCHEN_CONTEMPORARY],
  311. 'fabric': [SceneTemplate.BEDROOM_COZY, SceneTemplate.LIVING_ROOM_CLASSIC]
  312. };
  313. if (materialSceneMap[primaryMaterial]?.includes(scene)) {
  314. score += 20;
  315. }
  316. }
  317. return score;
  318. }
  319. /**
  320. * 生成氛围感预览图
  321. */
  322. private generateAtmospherePreview(params: any, sceneType: SceneTemplate): string {
  323. // 这里应该调用实际的预览图生成服务
  324. // 目前返回模拟的URL
  325. const baseUrl = document.baseURI+'/assets/previews/atmosphere/';
  326. const sceneId = sceneType.toString();
  327. const timestamp = Date.now();
  328. return `${baseUrl}${sceneId}_${timestamp}.jpg`;
  329. }
  330. /**
  331. * 生成默认预览图
  332. */
  333. private generateDefaultPreview(sceneType: SceneTemplate): string {
  334. return document.baseURI+`/assets/previews/default/${sceneType}.jpg`;
  335. }
  336. /**
  337. * 获取默认需求映射
  338. */
  339. private getDefaultRequirementMapping(): RequirementMapping {
  340. return {
  341. sceneGeneration: {
  342. baseScene: SceneTemplate.LIVING_ROOM_MODERN,
  343. parameters: this.sceneGenerationService.getSceneTemplate(SceneTemplate.LIVING_ROOM_MODERN),
  344. atmospherePreview: this.generateDefaultPreview(SceneTemplate.LIVING_ROOM_MODERN)
  345. },
  346. parameterMapping: {
  347. colorParams: {
  348. primaryColors: [
  349. { originalColor: '#FFFFFF', mappedColor: '#FFFFFF', weight: 40, usage: 'primary' },
  350. { originalColor: '#F5F5F5', mappedColor: '#F5F5F5', weight: 30, usage: 'secondary' },
  351. { originalColor: '#E0E0E0', mappedColor: '#E0E0E0', weight: 30, usage: 'background' }
  352. ],
  353. colorHarmony: 'monochromatic',
  354. saturation: 50,
  355. brightness: 70,
  356. contrast: 50,
  357. temperature: 'neutral'
  358. },
  359. spaceParams: {
  360. dimensions: { width: 400, height: 280, depth: 400, unit: 'meter' },
  361. layout: {
  362. type: 'open',
  363. flow: 'linear',
  364. zones: []
  365. },
  366. scale: {
  367. furniture: 50,
  368. ceiling: 60,
  369. openness: 70
  370. }
  371. },
  372. materialParams: {
  373. surfaceMaterials: [],
  374. textureScale: 50,
  375. reflectivity: 30,
  376. roughness: 40,
  377. metallic: 10
  378. }
  379. }
  380. };
  381. }
  382. /**
  383. * 转换为YAML格式
  384. */
  385. private convertToYaml(mapping: RequirementMapping): string {
  386. // 简化的YAML转换,实际项目中建议使用专门的YAML库
  387. return `# 需求映射配置文件
  388. sceneGeneration:
  389. baseScene: "${mapping.sceneGeneration.baseScene}"
  390. atmospherePreview: "${mapping.sceneGeneration.atmospherePreview}"
  391. parameters:
  392. lighting:
  393. primaryLight:
  394. type: "${mapping.sceneGeneration.parameters.lighting.primaryLight.type}"
  395. intensity: ${mapping.sceneGeneration.parameters.lighting.primaryLight.intensity}
  396. temperature: ${mapping.sceneGeneration.parameters.lighting.primaryLight.temperature}
  397. direction: "${mapping.sceneGeneration.parameters.lighting.primaryLight.direction}"
  398. # ... 其他参数
  399. parameterMapping:
  400. colorParams:
  401. primaryColors: [${mapping.parameterMapping.colorParams.primaryColors.map(c => `"${c}"`).join(', ')}]
  402. colorHarmony: "${mapping.parameterMapping.colorParams.colorHarmony}"
  403. saturation: ${mapping.parameterMapping.colorParams.saturation}
  404. brightness: ${mapping.parameterMapping.colorParams.brightness}
  405. # ... 其他映射参数`;
  406. }
  407. /**
  408. * 转换为XML格式
  409. */
  410. private convertToXml(mapping: RequirementMapping): string {
  411. // 简化的XML转换
  412. return `<?xml version="1.0" encoding="UTF-8"?>
  413. <RequirementMapping>
  414. <SceneGeneration>
  415. <BaseScene>${mapping.sceneGeneration.baseScene}</BaseScene>
  416. <AtmospherePreview>${mapping.sceneGeneration.atmospherePreview}</AtmospherePreview>
  417. <!-- 参数配置 -->
  418. </SceneGeneration>
  419. <ParameterMapping>
  420. <ColorParams>
  421. <PrimaryColors>${mapping.parameterMapping.colorParams.primaryColors.join(',')}</PrimaryColors>
  422. <ColorHarmony>${mapping.parameterMapping.colorParams.colorHarmony}</ColorHarmony>
  423. <SaturationLevel>${mapping.parameterMapping.colorParams.saturation}</SaturationLevel>
  424. <BrightnessLevel>${mapping.parameterMapping.colorParams.brightness}</BrightnessLevel>
  425. </ColorParams>
  426. <!-- 其他映射参数 -->
  427. </ParameterMapping>
  428. </RequirementMapping>`;
  429. }
  430. }