pattern-analysis.service.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. import { Injectable } from '@angular/core';
  2. import { Observable, of } from 'rxjs';
  3. // 纹理分析结果接口
  4. export interface PatternAnalysisResult {
  5. patternRecognition: PatternRecognition;
  6. repetitionAnalysis: RepetitionAnalysis;
  7. textureClassification: TextureClassification;
  8. visualRhythm: VisualRhythm;
  9. designRecommendations: string[];
  10. }
  11. // 图案识别
  12. export interface PatternRecognition {
  13. primaryPatterns: Array<{
  14. type: 'geometric' | 'organic' | 'abstract' | 'floral' | 'striped' | 'checkered' | 'dotted' | 'textured';
  15. confidence: number; // 0-100
  16. coverage: number; // 0-100
  17. characteristics: string[];
  18. }>;
  19. patternComplexity: {
  20. level: 'simple' | 'moderate' | 'complex' | 'very-complex';
  21. score: number; // 0-100
  22. elements: number; // 图案元素数量
  23. };
  24. patternScale: {
  25. size: 'micro' | 'small' | 'medium' | 'large' | 'macro';
  26. uniformity: number; // 0-100
  27. variation: number; // 0-100
  28. };
  29. }
  30. // 重复性分析
  31. export interface RepetitionAnalysis {
  32. repetitionType: {
  33. primary: 'regular' | 'irregular' | 'random' | 'semi-regular';
  34. pattern: 'grid' | 'brick' | 'hexagonal' | 'radial' | 'linear' | 'organic';
  35. consistency: number; // 0-100
  36. };
  37. spacing: {
  38. horizontal: number; // 像素或相对单位
  39. vertical: number;
  40. uniformity: number; // 0-100
  41. rhythm: 'tight' | 'moderate' | 'loose' | 'varied';
  42. };
  43. symmetry: {
  44. type: 'bilateral' | 'radial' | 'translational' | 'rotational' | 'none';
  45. strength: number; // 0-100
  46. axes: number; // 对称轴数量
  47. };
  48. tiling: {
  49. seamless: boolean;
  50. quality: number; // 0-100
  51. edgeHandling: 'perfect' | 'good' | 'fair' | 'poor';
  52. };
  53. }
  54. // 纹理分类
  55. export interface TextureClassification {
  56. textureFamily: {
  57. primary: 'natural' | 'artificial' | 'hybrid';
  58. subcategory: string;
  59. confidence: number; // 0-100
  60. };
  61. surfaceCharacter: {
  62. tactileQuality: 'smooth' | 'rough' | 'bumpy' | 'ridged' | 'woven' | 'carved';
  63. visualDepth: number; // 0-100
  64. dimensionality: '2D' | '2.5D' | '3D';
  65. };
  66. materialSuggestion: {
  67. likelyMaterials: string[];
  68. fabricationMethod: string[];
  69. applicationSuitability: string[];
  70. };
  71. }
  72. // 视觉节奏
  73. export interface VisualRhythm {
  74. rhythmType: {
  75. primary: 'regular' | 'alternating' | 'flowing' | 'progressive' | 'random';
  76. intensity: number; // 0-100
  77. tempo: 'slow' | 'moderate' | 'fast' | 'varied';
  78. };
  79. movement: {
  80. direction: 'horizontal' | 'vertical' | 'diagonal' | 'radial' | 'multi-directional';
  81. flow: number; // 0-100
  82. energy: number; // 0-100
  83. };
  84. emphasis: {
  85. focalPoints: number;
  86. contrast: number; // 0-100
  87. hierarchy: number; // 0-100
  88. };
  89. harmony: {
  90. overall: number; // 0-100
  91. balance: number; // 0-100
  92. unity: number; // 0-100
  93. };
  94. }
  95. @Injectable({
  96. providedIn: 'root'
  97. })
  98. export class PatternAnalysisService {
  99. constructor() { }
  100. /**
  101. * 分析图片中的纹理图案
  102. * @param imageFile 图片文件
  103. */
  104. analyzeImagePattern(imageFile: File): Observable<PatternAnalysisResult> {
  105. return of(this.simulatePatternAnalysis(imageFile));
  106. }
  107. /**
  108. * 分析URL图片的纹理图案
  109. * @param imageUrl 图片URL
  110. */
  111. analyzePatternFromUrl(imageUrl: string): Observable<PatternAnalysisResult> {
  112. return of(this.simulatePatternAnalysisFromUrl(imageUrl));
  113. }
  114. /**
  115. * 模拟纹理图案分析
  116. */
  117. private simulatePatternAnalysis(imageFile: File): PatternAnalysisResult {
  118. const fileName = imageFile.name.toLowerCase();
  119. return this.generatePatternAnalysis(fileName);
  120. }
  121. private simulatePatternAnalysisFromUrl(imageUrl: string): PatternAnalysisResult {
  122. const urlLower = imageUrl.toLowerCase();
  123. return this.generatePatternAnalysis(urlLower);
  124. }
  125. private generatePatternAnalysis(context: string): PatternAnalysisResult {
  126. const patternType = this.inferPatternType(context);
  127. return {
  128. patternRecognition: this.generatePatternRecognition(patternType, context),
  129. repetitionAnalysis: this.generateRepetitionAnalysis(patternType, context),
  130. textureClassification: this.generateTextureClassification(patternType, context),
  131. visualRhythm: this.generateVisualRhythm(patternType, context),
  132. designRecommendations: this.generateDesignRecommendations(patternType, context)
  133. };
  134. }
  135. private inferPatternType(context: string): string {
  136. if (context.includes('geometric') || context.includes('grid')) return 'geometric';
  137. if (context.includes('floral') || context.includes('flower')) return 'floral';
  138. if (context.includes('stripe') || context.includes('line')) return 'striped';
  139. if (context.includes('dot') || context.includes('circle')) return 'dotted';
  140. if (context.includes('check') || context.includes('square')) return 'checkered';
  141. if (context.includes('organic') || context.includes('natural')) return 'organic';
  142. if (context.includes('abstract')) return 'abstract';
  143. return 'textured'; // 默认纹理类型
  144. }
  145. private generatePatternRecognition(patternType: string, context: string): PatternRecognition {
  146. const isComplex = context.includes('complex') || context.includes('detailed');
  147. const isLarge = context.includes('large') || context.includes('big');
  148. const basePattern = this.getBasePatternData(patternType);
  149. return {
  150. primaryPatterns: [
  151. {
  152. type: patternType as any,
  153. confidence: 85 + Math.random() * 10,
  154. coverage: 70 + Math.random() * 25,
  155. characteristics: basePattern.characteristics
  156. }
  157. ],
  158. patternComplexity: {
  159. level: isComplex ? 'complex' : basePattern.complexity.level,
  160. score: isComplex ? 80 + Math.random() * 15 : basePattern.complexity.score,
  161. elements: isComplex ? 15 + Math.floor(Math.random() * 10) : basePattern.complexity.elements
  162. },
  163. patternScale: {
  164. size: isLarge ? 'large' : basePattern.scale.size,
  165. uniformity: basePattern.scale.uniformity,
  166. variation: basePattern.scale.variation
  167. }
  168. };
  169. }
  170. private getBasePatternData(patternType: string): any {
  171. const patterns: { [key: string]: any } = {
  172. geometric: {
  173. characteristics: ['规则', '对称', '数学性', '现代'],
  174. complexity: { level: 'moderate', score: 60, elements: 8 },
  175. scale: { size: 'medium', uniformity: 85, variation: 20 }
  176. },
  177. floral: {
  178. characteristics: ['自然', '有机', '装饰性', '传统'],
  179. complexity: { level: 'complex', score: 75, elements: 12 },
  180. scale: { size: 'medium', uniformity: 60, variation: 40 }
  181. },
  182. striped: {
  183. characteristics: ['线性', '方向性', '简洁', '经典'],
  184. complexity: { level: 'simple', score: 30, elements: 3 },
  185. scale: { size: 'medium', uniformity: 90, variation: 15 }
  186. },
  187. dotted: {
  188. characteristics: ['点状', '规律', '轻盈', '现代'],
  189. complexity: { level: 'simple', score: 35, elements: 4 },
  190. scale: { size: 'small', uniformity: 80, variation: 25 }
  191. },
  192. checkered: {
  193. characteristics: ['格子', '对比', '经典', '结构化'],
  194. complexity: { level: 'simple', score: 40, elements: 2 },
  195. scale: { size: 'medium', uniformity: 95, variation: 10 }
  196. },
  197. organic: {
  198. characteristics: ['自然', '流动', '不规则', '和谐'],
  199. complexity: { level: 'moderate', score: 65, elements: 10 },
  200. scale: { size: 'varied', uniformity: 45, variation: 60 }
  201. },
  202. abstract: {
  203. characteristics: ['抽象', '艺术性', '创新', '表现力'],
  204. complexity: { level: 'complex', score: 80, elements: 15 },
  205. scale: { size: 'varied', uniformity: 40, variation: 70 }
  206. },
  207. textured: {
  208. characteristics: ['质感', '触觉', '深度', '材质感'],
  209. complexity: { level: 'moderate', score: 55, elements: 6 },
  210. scale: { size: 'small', uniformity: 70, variation: 35 }
  211. }
  212. };
  213. return patterns[patternType] || patterns['textured'];
  214. }
  215. private generateRepetitionAnalysis(patternType: string, context: string): RepetitionAnalysis {
  216. const isRegular = patternType === 'geometric' || patternType === 'striped' || patternType === 'checkered';
  217. const isSeamless = context.includes('seamless') || context.includes('tile');
  218. return {
  219. repetitionType: {
  220. primary: isRegular ? 'regular' : 'semi-regular',
  221. pattern: this.getRepetitionPattern(patternType),
  222. consistency: isRegular ? 85 + Math.random() * 10 : 60 + Math.random() * 25
  223. },
  224. spacing: {
  225. horizontal: 50 + Math.random() * 100,
  226. vertical: 50 + Math.random() * 100,
  227. uniformity: isRegular ? 85 + Math.random() * 10 : 55 + Math.random() * 30,
  228. rhythm: this.getSpacingRhythm(patternType)
  229. },
  230. symmetry: {
  231. type: this.getSymmetryType(patternType),
  232. strength: this.getSymmetryStrength(patternType),
  233. axes: this.getSymmetryAxes(patternType)
  234. },
  235. tiling: {
  236. seamless: isSeamless,
  237. quality: isSeamless ? 90 + Math.random() * 8 : 60 + Math.random() * 30,
  238. edgeHandling: isSeamless ? 'perfect' : 'good'
  239. }
  240. };
  241. }
  242. private generateTextureClassification(patternType: string, context: string): TextureClassification {
  243. const isNatural = patternType === 'organic' || patternType === 'floral' || context.includes('natural');
  244. return {
  245. textureFamily: {
  246. primary: isNatural ? 'natural' : 'artificial',
  247. subcategory: this.getTextureSubcategory(patternType),
  248. confidence: 80 + Math.random() * 15
  249. },
  250. surfaceCharacter: {
  251. tactileQuality: this.getTactileQuality(patternType),
  252. visualDepth: this.getVisualDepth(patternType),
  253. dimensionality: this.getDimensionality(patternType)
  254. },
  255. materialSuggestion: {
  256. likelyMaterials: this.getLikelyMaterials(patternType),
  257. fabricationMethod: this.getFabricationMethods(patternType),
  258. applicationSuitability: this.getApplicationSuitability(patternType)
  259. }
  260. };
  261. }
  262. private generateVisualRhythm(patternType: string, context: string): VisualRhythm {
  263. const isDynamic = context.includes('dynamic') || context.includes('movement');
  264. return {
  265. rhythmType: {
  266. primary: this.getRhythmType(patternType),
  267. intensity: this.getRhythmIntensity(patternType, isDynamic),
  268. tempo: this.getRhythmTempo(patternType)
  269. },
  270. movement: {
  271. direction: this.getMovementDirection(patternType),
  272. flow: this.getFlowValue(patternType),
  273. energy: isDynamic ? 80 + Math.random() * 15 : 50 + Math.random() * 30
  274. },
  275. emphasis: {
  276. focalPoints: this.getFocalPoints(patternType),
  277. contrast: this.getContrastValue(patternType),
  278. hierarchy: this.getHierarchyValue(patternType)
  279. },
  280. harmony: {
  281. overall: 70 + Math.random() * 25,
  282. balance: this.getBalanceValue(patternType),
  283. unity: this.getUnityValue(patternType)
  284. }
  285. };
  286. }
  287. private generateDesignRecommendations(patternType: string, context: string): string[] {
  288. const recommendations = [];
  289. // 基于图案类型的建议
  290. switch (patternType) {
  291. case 'geometric':
  292. recommendations.push('保持几何图案的精确性和一致性');
  293. recommendations.push('考虑使用对比色增强视觉效果');
  294. break;
  295. case 'floral':
  296. recommendations.push('平衡花卉图案的复杂度和可读性');
  297. recommendations.push('注意色彩搭配的自然和谐');
  298. break;
  299. case 'striped':
  300. recommendations.push('控制条纹的宽度比例');
  301. recommendations.push('注意条纹方向对空间感的影响');
  302. break;
  303. case 'organic':
  304. recommendations.push('保持有机图案的自然流动感');
  305. recommendations.push('避免过度规则化');
  306. break;
  307. }
  308. // 基于上下文的建议
  309. if (context.includes('interior')) {
  310. recommendations.push('考虑图案对室内空间感的影响');
  311. }
  312. if (context.includes('fabric')) {
  313. recommendations.push('注意图案的可缝制性和耐用性');
  314. }
  315. if (context.includes('wallpaper')) {
  316. recommendations.push('确保图案的无缝拼接效果');
  317. }
  318. return recommendations.length > 0 ? recommendations : ['保持图案设计的整体协调性'];
  319. }
  320. // 辅助方法
  321. private getRepetitionPattern(patternType: string): 'grid' | 'brick' | 'hexagonal' | 'radial' | 'linear' | 'organic' {
  322. const patterns: { [key: string]: 'grid' | 'brick' | 'hexagonal' | 'radial' | 'linear' | 'organic' } = {
  323. geometric: 'grid', striped: 'linear', checkered: 'grid',
  324. dotted: 'grid', floral: 'organic', organic: 'organic',
  325. abstract: 'radial', textured: 'brick'
  326. };
  327. return patterns[patternType] || 'grid';
  328. }
  329. private getSpacingRhythm(patternType: string): 'tight' | 'moderate' | 'loose' | 'varied' {
  330. const rhythms: { [key: string]: 'tight' | 'moderate' | 'loose' | 'varied' } = {
  331. geometric: 'moderate', striped: 'tight', checkered: 'moderate',
  332. dotted: 'moderate', floral: 'varied', organic: 'varied',
  333. abstract: 'varied', textured: 'tight'
  334. };
  335. return rhythms[patternType] || 'moderate';
  336. }
  337. private getSymmetryType(patternType: string): 'bilateral' | 'radial' | 'translational' | 'rotational' | 'none' {
  338. const types: { [key: string]: 'bilateral' | 'radial' | 'translational' | 'rotational' | 'none' } = {
  339. geometric: 'bilateral', striped: 'translational', checkered: 'bilateral',
  340. dotted: 'translational', floral: 'radial', organic: 'none',
  341. abstract: 'rotational', textured: 'translational'
  342. };
  343. return types[patternType] || 'bilateral';
  344. }
  345. private getSymmetryStrength(patternType: string): number {
  346. const strengths: { [key: string]: number } = {
  347. geometric: 90, striped: 85, checkered: 95, dotted: 80,
  348. floral: 60, organic: 30, abstract: 50, textured: 70
  349. };
  350. return (strengths[patternType] || 70) + Math.random() * 10;
  351. }
  352. private getSymmetryAxes(patternType: string): number {
  353. const axes: { [key: string]: number } = {
  354. geometric: 2, striped: 1, checkered: 2, dotted: 4,
  355. floral: 4, organic: 0, abstract: 3, textured: 1
  356. };
  357. return axes[patternType] || 2;
  358. }
  359. private getTextureSubcategory(patternType: string): string {
  360. const subcategories: { [key: string]: string } = {
  361. geometric: '几何纹理', floral: '植物纹理', striped: '线性纹理',
  362. dotted: '点状纹理', checkered: '格子纹理', organic: '有机纹理',
  363. abstract: '抽象纹理', textured: '表面纹理'
  364. };
  365. return subcategories[patternType] || '混合纹理';
  366. }
  367. private getTactileQuality(patternType: string): 'smooth' | 'rough' | 'bumpy' | 'ridged' | 'woven' | 'carved' {
  368. const qualities: { [key: string]: 'smooth' | 'rough' | 'bumpy' | 'ridged' | 'woven' | 'carved' } = {
  369. geometric: 'smooth', floral: 'carved', striped: 'ridged',
  370. dotted: 'bumpy', checkered: 'woven', organic: 'rough',
  371. abstract: 'rough', textured: 'rough'
  372. };
  373. return qualities[patternType] || 'smooth';
  374. }
  375. private getVisualDepth(patternType: string): number {
  376. const depths: { [key: string]: number } = {
  377. geometric: 40, floral: 70, striped: 30, dotted: 50,
  378. checkered: 35, organic: 80, abstract: 85, textured: 90
  379. };
  380. return depths[patternType] || 50;
  381. }
  382. private getDimensionality(patternType: string): '2D' | '2.5D' | '3D' {
  383. const dims: { [key: string]: '2D' | '2.5D' | '3D' } = {
  384. geometric: '2D', floral: '2.5D', striped: '2D', dotted: '2D',
  385. checkered: '2D', organic: '3D', abstract: '2.5D', textured: '3D'
  386. };
  387. return dims[patternType] || '2D';
  388. }
  389. private getLikelyMaterials(patternType: string): string[] {
  390. const materials: { [key: string]: string[] } = {
  391. geometric: ['金属', '塑料', '陶瓷'],
  392. floral: ['织物', '壁纸', '陶瓷'],
  393. striped: ['织物', '木材', '金属'],
  394. dotted: ['织物', '塑料', '纸张'],
  395. checkered: ['织物', '瓷砖', '纸张'],
  396. organic: ['木材', '石材', '皮革'],
  397. abstract: ['画布', '金属', '玻璃'],
  398. textured: ['石材', '混凝土', '皮革']
  399. };
  400. return materials[patternType] || ['通用材料'];
  401. }
  402. private getFabricationMethods(patternType: string): string[] {
  403. const methods: { [key: string]: string[] } = {
  404. geometric: ['激光切割', '数控加工', '模具成型'],
  405. floral: ['印刷', '刺绣', '雕刻'],
  406. striped: ['编织', '印刷', '切割'],
  407. dotted: ['打孔', '印刷', '压花'],
  408. checkered: ['编织', '印刷', '拼接'],
  409. organic: ['手工雕刻', '铸造', '3D打印'],
  410. abstract: ['手绘', '喷涂', '数字印刷'],
  411. textured: ['压花', '喷砂', '化学蚀刻']
  412. };
  413. return methods[patternType] || ['常规加工'];
  414. }
  415. private getApplicationSuitability(patternType: string): string[] {
  416. const applications: { [key: string]: string[] } = {
  417. geometric: ['现代室内', '办公空间', '科技产品'],
  418. floral: ['家居装饰', '服装设计', '传统空间'],
  419. striped: ['服装', '室内装饰', '包装设计'],
  420. dotted: ['儿童用品', '休闲服装', '装饰品'],
  421. checkered: ['经典服装', '餐厅装饰', '游戏用品'],
  422. organic: ['自然风格室内', '艺术品', '高端产品'],
  423. abstract: ['艺术空间', '创意产品', '展示设计'],
  424. textured: ['建筑外观', '工业设计', '触觉产品']
  425. };
  426. return applications[patternType] || ['通用应用'];
  427. }
  428. private getRhythmType(patternType: string): 'regular' | 'alternating' | 'flowing' | 'progressive' | 'random' {
  429. const types: { [key: string]: 'regular' | 'alternating' | 'flowing' | 'progressive' | 'random' } = {
  430. geometric: 'regular', floral: 'flowing', striped: 'alternating',
  431. dotted: 'regular', checkered: 'alternating', organic: 'flowing',
  432. abstract: 'progressive', textured: 'random'
  433. };
  434. return types[patternType] || 'regular';
  435. }
  436. private getRhythmIntensity(patternType: string, isDynamic: boolean): number {
  437. const base: { [key: string]: number } = {
  438. geometric: 60, floral: 70, striped: 80, dotted: 50,
  439. checkered: 75, organic: 65, abstract: 85, textured: 55
  440. };
  441. const intensity = base[patternType] || 60;
  442. return isDynamic ? Math.min(95, intensity + 20) : intensity;
  443. }
  444. private getRhythmTempo(patternType: string): 'slow' | 'moderate' | 'fast' | 'varied' {
  445. const tempos: { [key: string]: 'slow' | 'moderate' | 'fast' | 'varied' } = {
  446. geometric: 'moderate', floral: 'slow', striped: 'fast',
  447. dotted: 'moderate', checkered: 'fast', organic: 'varied',
  448. abstract: 'varied', textured: 'slow'
  449. };
  450. return tempos[patternType] || 'moderate';
  451. }
  452. private getMovementDirection(patternType: string): 'horizontal' | 'vertical' | 'diagonal' | 'radial' | 'multi-directional' {
  453. const directions: { [key: string]: 'horizontal' | 'vertical' | 'diagonal' | 'radial' | 'multi-directional' } = {
  454. geometric: 'multi-directional', floral: 'radial', striped: 'horizontal',
  455. dotted: 'multi-directional', checkered: 'multi-directional', organic: 'radial',
  456. abstract: 'multi-directional', textured: 'multi-directional'
  457. };
  458. return directions[patternType] || 'multi-directional';
  459. }
  460. private getFlowValue(patternType: string): number {
  461. const flows: { [key: string]: number } = {
  462. geometric: 50, floral: 80, striped: 70, dotted: 40,
  463. checkered: 45, organic: 90, abstract: 75, textured: 60
  464. };
  465. return (flows[patternType] || 60) + Math.random() * 15;
  466. }
  467. private getFocalPoints(patternType: string): number {
  468. const points: { [key: string]: number } = {
  469. geometric: 4, floral: 6, striped: 2, dotted: 8,
  470. checkered: 4, organic: 3, abstract: 5, textured: 2
  471. };
  472. return points[patternType] || 4;
  473. }
  474. private getContrastValue(patternType: string): number {
  475. const contrasts: { [key: string]: number } = {
  476. geometric: 80, floral: 60, striped: 90, dotted: 70,
  477. checkered: 95, organic: 50, abstract: 85, textured: 65
  478. };
  479. return (contrasts[patternType] || 70) + Math.random() * 15;
  480. }
  481. private getHierarchyValue(patternType: string): number {
  482. const hierarchies: { [key: string]: number } = {
  483. geometric: 85, floral: 70, striped: 60, dotted: 55,
  484. checkered: 75, organic: 65, abstract: 80, textured: 50
  485. };
  486. return (hierarchies[patternType] || 65) + Math.random() * 15;
  487. }
  488. private getBalanceValue(patternType: string): number {
  489. const balances: { [key: string]: number } = {
  490. geometric: 90, floral: 75, striped: 85, dotted: 80,
  491. checkered: 95, organic: 60, abstract: 70, textured: 65
  492. };
  493. return (balances[patternType] || 75) + Math.random() * 15;
  494. }
  495. private getUnityValue(patternType: string): number {
  496. const unities: { [key: string]: number } = {
  497. geometric: 85, floral: 80, striped: 90, dotted: 75,
  498. checkered: 90, organic: 70, abstract: 65, textured: 70
  499. };
  500. return (unities[patternType] || 75) + Math.random() * 15;
  501. }
  502. }