dev #5
41
frontend/src/app/core/constants/colors.const.ts
Normal file
41
frontend/src/app/core/constants/colors.const.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
export interface ColorOption {
|
||||
label: string;
|
||||
value: string;
|
||||
hex: string;
|
||||
outOfStock?: boolean;
|
||||
}
|
||||
|
||||
export interface ColorCategory {
|
||||
name: string; // 'Glossy' | 'Matte'
|
||||
colors: ColorOption[];
|
||||
}
|
||||
|
||||
export const PRODUCT_COLORS: ColorCategory[] = [
|
||||
{
|
||||
name: 'Lucidi', // Glossy
|
||||
colors: [
|
||||
{ label: 'Black', value: 'Black', hex: '#1a1a1a' }, // Not pure black for visibility
|
||||
{ label: 'White', value: 'White', hex: '#f5f5f5' },
|
||||
{ label: 'Red', value: 'Red', hex: '#d32f2f', outOfStock: true },
|
||||
{ label: 'Blue', value: 'Blue', hex: '#1976d2' },
|
||||
{ label: 'Green', value: 'Green', hex: '#388e3c' },
|
||||
{ label: 'Yellow', value: 'Yellow', hex: '#fbc02d' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Opachi', // Matte
|
||||
colors: [
|
||||
{ label: 'Matte Black', value: 'Matte Black', hex: '#2c2c2c' }, // Lighter charcoal for matte
|
||||
{ label: 'Matte White', value: 'Matte White', hex: '#e0e0e0' },
|
||||
{ label: 'Matte Gray', value: 'Matte Gray', hex: '#757575' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export function getColorHex(value: string): string {
|
||||
for (const cat of PRODUCT_COLORS) {
|
||||
const found = cat.colors.find(c => c.value === value);
|
||||
if (found) return found.hex;
|
||||
}
|
||||
return '#facf0a'; // Default Brand Color if not found
|
||||
}
|
||||
@@ -72,11 +72,14 @@ export class CalculatorPageComponent {
|
||||
|
||||
details += `- File:\n`;
|
||||
req.items.forEach(item => {
|
||||
details += ` * ${item.file.name} (Qtà: ${item.quantity})\n`;
|
||||
details += ` * ${item.file.name} (Qtà: ${item.quantity}`;
|
||||
if (item.color) {
|
||||
details += `, Colore: ${item.color}`;
|
||||
}
|
||||
details += `)\n`;
|
||||
});
|
||||
|
||||
if (req.mode === 'advanced') {
|
||||
if (req.color) details += `- Colore: ${req.color}\n`;
|
||||
if (req.infillDensity) details += `- Infill: ${req.infillDensity}%\n`;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
<div class="section">
|
||||
@if (selectedFile()) {
|
||||
<div class="viewer-wrapper">
|
||||
<app-stl-viewer [file]="selectedFile()"></app-stl-viewer>
|
||||
<app-stl-viewer
|
||||
[file]="selectedFile()"
|
||||
[color]="getSelectedFileColor()">
|
||||
</app-stl-viewer>
|
||||
<!-- Close button removed as requested -->
|
||||
</div>
|
||||
}
|
||||
@@ -29,15 +32,25 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="qty-group">
|
||||
<label>Qtà</label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
[value]="item.quantity"
|
||||
(change)="updateItemQuantity(i, $event)"
|
||||
class="qty-input"
|
||||
(click)="$event.stopPropagation()">
|
||||
<div class="card-controls">
|
||||
<div class="qty-group">
|
||||
<label>QTÀ</label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
[value]="item.quantity"
|
||||
(change)="updateItemQuantity(i, $event)"
|
||||
class="qty-input"
|
||||
(click)="$event.stopPropagation()">
|
||||
</div>
|
||||
|
||||
<div class="color-group">
|
||||
<label>COLORE</label>
|
||||
<app-color-selector
|
||||
[selectedColor]="item.color"
|
||||
(colorSelected)="updateItemColor(i, $event)">
|
||||
</app-color-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-remove" (click)="removeItem(i); $event.stopPropagation()" title="Remove file">
|
||||
@@ -81,12 +94,6 @@
|
||||
|
||||
@if (mode() === 'advanced') {
|
||||
<div class="grid">
|
||||
<app-select
|
||||
formControlName="color"
|
||||
[label]="'CALC.COLOR' | translate"
|
||||
[options]="colors"
|
||||
></app-select>
|
||||
|
||||
<app-select
|
||||
formControlName="infillPattern"
|
||||
[label]="'CALC.PATTERN' | translate"
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
/* Grid Layout for Files */
|
||||
.items-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-3);
|
||||
grid-template-columns: 1fr 1fr; /* Force 2 columns on mobile */
|
||||
gap: var(--space-2); /* Tighten gap for mobile */
|
||||
margin-top: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
|
||||
@media(min-width: 640px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
}
|
||||
|
||||
.file-card {
|
||||
padding: var(--space-3);
|
||||
padding: var(--space-2); /* Reduced from space-3 */
|
||||
background: var(--color-neutral-100);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
@@ -35,7 +35,9 @@
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
gap: 4px; /* Reduced gap */
|
||||
position: relative; /* For absolute positioning of remove btn */
|
||||
min-width: 0; /* Allow flex item to shrink below content size if needed */
|
||||
|
||||
&:hover { border-color: var(--color-neutral-300); }
|
||||
&.active {
|
||||
@@ -47,11 +49,13 @@
|
||||
|
||||
.card-header {
|
||||
overflow: hidden;
|
||||
padding-right: 25px; /* Adjusted */
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 500;
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.8rem; /* Smaller font */
|
||||
color: var(--color-text);
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
@@ -61,34 +65,64 @@
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.qty-group {
|
||||
.card-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
label { font-size: 0.75rem; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
||||
align-items: flex-end; /* Align bottom of input and color circle */
|
||||
gap: 16px; /* Space between Qty and Color */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.qty-group, .color-group {
|
||||
display: flex;
|
||||
flex-direction: column; /* Stack label and input */
|
||||
align-items: flex-start;
|
||||
gap: 0px;
|
||||
|
||||
label {
|
||||
font-size: 0.6rem;
|
||||
color: var(--color-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.color-group {
|
||||
align-items: flex-start; /* Align label left */
|
||||
/* margin-right removed */
|
||||
|
||||
/* Override margin in selector for this context */
|
||||
::ng-deep .color-selector-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.qty-input {
|
||||
width: 40px;
|
||||
padding: 2px 4px;
|
||||
width: 36px; /* Slightly smaller */
|
||||
padding: 1px 2px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.85rem;
|
||||
background: white;
|
||||
height: 24px; /* Explicit height to match color circle somewhat */
|
||||
&:focus { outline: none; border-color: var(--color-brand); }
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent; // var(--color-border);
|
||||
background: transparent; // white;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text-muted);
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
@@ -96,12 +130,11 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-danger-100);
|
||||
color: var(--color-danger-500);
|
||||
border-color: var(--color-danger-200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,17 +7,20 @@ import { AppSelectComponent } from '../../../../shared/components/app-select/app
|
||||
import { AppDropzoneComponent } from '../../../../shared/components/app-dropzone/app-dropzone.component';
|
||||
import { AppButtonComponent } from '../../../../shared/components/app-button/app-button.component';
|
||||
import { StlViewerComponent } from '../../../../shared/components/stl-viewer/stl-viewer.component';
|
||||
import { ColorSelectorComponent } from '../../../../shared/components/color-selector/color-selector.component';
|
||||
import { QuoteRequest } from '../../services/quote-estimator.service';
|
||||
import { getColorHex } from '../../../../core/constants/colors.const';
|
||||
|
||||
interface FormItem {
|
||||
file: File;
|
||||
quantity: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-upload-form',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule, TranslateModule, AppInputComponent, AppSelectComponent, AppDropzoneComponent, AppButtonComponent, StlViewerComponent],
|
||||
imports: [CommonModule, ReactiveFormsModule, TranslateModule, AppInputComponent, AppSelectComponent, AppDropzoneComponent, AppButtonComponent, StlViewerComponent, ColorSelectorComponent],
|
||||
templateUrl: './upload-form.component.html',
|
||||
styleUrl: './upload-form.component.scss'
|
||||
})
|
||||
@@ -44,15 +47,6 @@ export class UploadFormComponent {
|
||||
{ label: 'Alta definizione', value: 'High' }
|
||||
];
|
||||
|
||||
colors = [
|
||||
{ label: 'Black', value: 'Black' },
|
||||
{ label: 'White', value: 'White' },
|
||||
{ label: 'Gray', value: 'Gray' },
|
||||
{ label: 'Red', value: 'Red' },
|
||||
{ label: 'Blue', value: 'Blue' },
|
||||
{ label: 'Green', value: 'Green' },
|
||||
{ label: 'Yellow', value: 'Yellow' }
|
||||
];
|
||||
infillPatterns = [
|
||||
{ label: 'Grid', value: 'grid' },
|
||||
{ label: 'Gyroid', value: 'gyroid' },
|
||||
@@ -69,7 +63,7 @@ export class UploadFormComponent {
|
||||
quality: ['Standard', Validators.required],
|
||||
notes: [''],
|
||||
// Advanced fields
|
||||
color: ['Black'],
|
||||
// Color removed from global form
|
||||
infillDensity: [20, [Validators.min(0), Validators.max(100)]],
|
||||
infillPattern: ['grid'],
|
||||
supportEnabled: [false]
|
||||
@@ -85,7 +79,8 @@ export class UploadFormComponent {
|
||||
if (file.size > MAX_SIZE) {
|
||||
hasError = true;
|
||||
} else {
|
||||
validItems.push({ file, quantity: 1 });
|
||||
// Default color is Black
|
||||
validItems.push({ file, quantity: 1, color: 'Black' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +124,18 @@ export class UploadFormComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get color of currently selected file
|
||||
getSelectedFileColor(): string {
|
||||
const file = this.selectedFile();
|
||||
if (!file) return '#facf0a'; // Default
|
||||
|
||||
const item = this.items().find(i => i.file === file);
|
||||
if (item) {
|
||||
return getColorHex(item.color);
|
||||
}
|
||||
return '#facf0a';
|
||||
}
|
||||
|
||||
updateItemQuantity(index: number, event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
let val = parseInt(input.value, 10);
|
||||
@@ -141,6 +148,14 @@ export class UploadFormComponent {
|
||||
});
|
||||
}
|
||||
|
||||
updateItemColor(index: number, newColor: string) {
|
||||
this.items.update(current => {
|
||||
const updated = [...current];
|
||||
updated[index] = { ...updated[index], color: newColor };
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
|
||||
removeItem(index: number) {
|
||||
this.items.update(current => {
|
||||
const updated = [...current];
|
||||
@@ -155,7 +170,7 @@ export class UploadFormComponent {
|
||||
onSubmit() {
|
||||
if (this.form.valid && this.items().length > 0) {
|
||||
this.submitRequest.emit({
|
||||
items: this.items(), // Pass the items array
|
||||
items: this.items(), // Pass the items array including colors
|
||||
...this.form.value,
|
||||
mode: this.mode()
|
||||
});
|
||||
|
||||
@@ -5,11 +5,11 @@ import { map, catchError } from 'rxjs/operators';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
|
||||
export interface QuoteRequest {
|
||||
items: { file: File, quantity: number }[];
|
||||
items: { file: File, quantity: number, color?: string }[];
|
||||
material: string;
|
||||
quality: string;
|
||||
notes?: string;
|
||||
color?: string;
|
||||
// color removed from global scope
|
||||
infillDensity?: number;
|
||||
infillPattern?: string;
|
||||
supportEnabled?: boolean;
|
||||
@@ -69,8 +69,11 @@ export class QuoteEstimatorService {
|
||||
formData.append('machine', 'bambu_a1');
|
||||
formData.append('filament', this.mapMaterial(request.material));
|
||||
formData.append('quality', this.mapQuality(request.quality));
|
||||
|
||||
// Send color for both modes if present, defaulting to Black
|
||||
formData.append('material_color', item.color || 'Black');
|
||||
|
||||
if (request.mode === 'advanced') {
|
||||
if (request.color) formData.append('material_color', request.color);
|
||||
if (request.infillDensity) formData.append('infill_density', request.infillDensity.toString());
|
||||
if (request.infillPattern) formData.append('infill_pattern', request.infillPattern);
|
||||
if (request.supportEnabled) formData.append('support_enabled', 'true');
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<div class="color-selector-container">
|
||||
@if (isOpen()) {
|
||||
<div class="backdrop" (click)="close()"></div>
|
||||
}
|
||||
|
||||
<div
|
||||
class="color-circle trigger"
|
||||
[style.background-color]="getCurrentHex()"
|
||||
[title]="selectedColor()"
|
||||
(click)="toggleOpen()">
|
||||
</div>
|
||||
|
||||
@if (isOpen()) {
|
||||
<div class="color-popup">
|
||||
@for (category of categories; track category.name) {
|
||||
<div class="category">
|
||||
<div class="category-name">{{ category.name }}</div>
|
||||
<div class="colors-grid">
|
||||
@for (color of category.colors; track color.value) {
|
||||
<div
|
||||
class="color-item"
|
||||
(click)="selectColor(color)"
|
||||
[class.disabled]="color.outOfStock">
|
||||
|
||||
<div class="selection-ring"
|
||||
[class.active]="selectedColor() === color.value"
|
||||
[class.out-of-stock]="color.outOfStock">
|
||||
<div class="color-circle small" [style.background-color]="color.hex"></div>
|
||||
</div>
|
||||
|
||||
<span class="color-name">{{ color.label }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,136 @@
|
||||
.color-selector-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
// margin-left: 10px; // Handled by parent now
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 999;
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.color-circle {
|
||||
width: 20px; /* Reduced from 24px */
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #ddd;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
|
||||
&.trigger:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
&.small {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.color-popup {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px); // Explicit gap
|
||||
left: -2px; // Align left edge with slight offset
|
||||
background: white;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px; /* Slightly tighter radius */
|
||||
padding: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
z-index: 1000;
|
||||
width: 230px; /* Increased size */
|
||||
|
||||
// Little triangle arrow
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 8px; // Align arrow to left side near trigger
|
||||
border-width: 0 6px 6px 6px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent white transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.category {
|
||||
margin-bottom: 12px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: #888;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.colors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr); /* 3 columns for better alignment */
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
|
||||
&:hover .selection-ring {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
&:hover .selection-ring { transform: none; }
|
||||
}
|
||||
}
|
||||
|
||||
.selection-ring {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.2s;
|
||||
padding: 2px; /* Space for ring */
|
||||
|
||||
&.active {
|
||||
border-color: var(--color-brand, #facf0a);
|
||||
box-shadow: 0 0 0 1px var(--color-brand, #facf0a);
|
||||
}
|
||||
|
||||
&.out-of-stock::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: #cc0000;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.color-name {
|
||||
font-size: 0.65rem;
|
||||
color: #444;
|
||||
line-height: 1.1;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Component, input, output, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PRODUCT_COLORS, getColorHex, ColorCategory, ColorOption } from '../../../core/constants/colors.const';
|
||||
|
||||
@Component({
|
||||
selector: 'app-color-selector',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TranslateModule],
|
||||
templateUrl: './color-selector.component.html',
|
||||
styleUrl: './color-selector.component.scss'
|
||||
})
|
||||
export class ColorSelectorComponent {
|
||||
selectedColor = input<string>('Black');
|
||||
colorSelected = output<string>();
|
||||
|
||||
isOpen = signal(false);
|
||||
|
||||
categories: ColorCategory[] = PRODUCT_COLORS;
|
||||
|
||||
toggleOpen() {
|
||||
this.isOpen.update(v => !v);
|
||||
}
|
||||
|
||||
selectColor(color: ColorOption) {
|
||||
if (color.outOfStock) return;
|
||||
|
||||
this.colorSelected.emit(color.value);
|
||||
this.isOpen.set(false);
|
||||
}
|
||||
|
||||
// Helper to find hex for the current selected value
|
||||
getCurrentHex(): string {
|
||||
return getColorHex(this.selectedColor());
|
||||
}
|
||||
|
||||
close() {
|
||||
this.isOpen.set(false);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
})
|
||||
export class StlViewerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
@Input() file: File | null = null;
|
||||
@Input() color: string = '#facf0a'; // Default Brand Color
|
||||
|
||||
@ViewChild('rendererContainer', { static: true }) rendererContainer!: ElementRef;
|
||||
|
||||
private scene!: THREE.Scene;
|
||||
@@ -34,6 +36,12 @@ export class StlViewerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
if (changes['file'] && this.file) {
|
||||
this.loadFile(this.file);
|
||||
}
|
||||
|
||||
if (changes['color'] && this.currentMesh && !changes['file']) {
|
||||
// Update existing mesh color if only color changed
|
||||
const mat = this.currentMesh.material as THREE.MeshPhongMaterial;
|
||||
mat.color.set(this.color);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -99,7 +107,7 @@ export class StlViewerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
}
|
||||
|
||||
const material = new THREE.MeshPhongMaterial({
|
||||
color: 0xFACF0A, // Brand color
|
||||
color: this.color,
|
||||
specular: 0x111111,
|
||||
shininess: 200
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user