Add particle type selection, size control, and enhance particle property handling
This commit is contained in:
parent
1e5862bae6
commit
d4363b3a8a
|
|
@ -1,12 +1,36 @@
|
||||||
<div class="card-div">
|
<div class="card-div">
|
||||||
<mat-card class="color-picker-card">
|
<mat-card class="particle-card">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>Particle Color</mat-card-title>
|
<mat-card-title>Particle Properties</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<div class="color-picker">
|
<div class="particle-properties">
|
||||||
<input type="color" [(ngModel)]="selectedColor">
|
<div class="property-row">
|
||||||
<span>Selected Color: {{ selectedColor }}</span>
|
<div class="color-picker">
|
||||||
|
<input type="color" [(ngModel)]="selectedColor">
|
||||||
|
<span>Current color: {{ selectedColor }}</span>
|
||||||
|
</div>
|
||||||
|
<mat-form-field appearance="fill" class="type-field">
|
||||||
|
<mat-label>Select Particle Type</mat-label>
|
||||||
|
<input type="text"
|
||||||
|
placeholder="Search for a particle type"
|
||||||
|
matInput
|
||||||
|
[formControl]="particleTypeControl"
|
||||||
|
[matAutocomplete]="auto">
|
||||||
|
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
|
||||||
|
<mat-option *ngFor="let type of filteredParticleTypes | async" [value]="type">
|
||||||
|
{{ type }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="size-slider">
|
||||||
|
<mat-slider min="0.1" max="4" step="0.1" class="full-width">
|
||||||
|
<input matSliderThumb [(ngModel)]="selectedSize">
|
||||||
|
</mat-slider>
|
||||||
|
<span>Size: {{ selectedSize }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,37 @@
|
||||||
.color-picker-card {
|
.particle-card {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.particle-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.property-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-field {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 20ch;
|
||||||
|
max-width: 40ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker {
|
.color-picker {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker input[type="color"] {
|
.color-picker input[type="color"] {
|
||||||
width: 50px;
|
width: 40px;
|
||||||
height: 50px;
|
height: 40px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -23,6 +44,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-slider {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
import {Component} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
||||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {ParticleManagerService} from '../../services/particle-manager.service';
|
import {ParticleManagerService} from '../../services/particle-manager.service';
|
||||||
|
import {Particle} from '../../models/particle.model';
|
||||||
|
import {MatSliderModule} from '@angular/material/slider';
|
||||||
|
import {MatSelectModule} from '@angular/material/select';
|
||||||
|
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||||
|
import {MatInputModule} from '@angular/material/input';
|
||||||
|
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {map, startWith} from 'rxjs/operators';
|
||||||
|
import {AsyncPipe, NgForOf} from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-particle',
|
selector: 'app-particle',
|
||||||
|
|
@ -11,16 +20,55 @@ import {ParticleManagerService} from '../../services/particle-manager.service';
|
||||||
MatCardHeader,
|
MatCardHeader,
|
||||||
MatCardTitle,
|
MatCardTitle,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
MatSliderModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatAutocompleteModule,
|
||||||
|
NgForOf,
|
||||||
|
AsyncPipe
|
||||||
],
|
],
|
||||||
templateUrl: './particle.component.html',
|
templateUrl: './particle.component.html',
|
||||||
styleUrl: './particle.component.scss'
|
styleUrl: './particle.component.scss'
|
||||||
})
|
})
|
||||||
export class ParticleComponent {
|
export class ParticleComponent implements OnInit {
|
||||||
|
// Available particle types from the enum
|
||||||
|
particleTypes = Object.values(Particle);
|
||||||
|
|
||||||
|
// Form control for the particle type dropdown with filtering
|
||||||
|
particleTypeControl = new FormControl();
|
||||||
|
filteredParticleTypes: Observable<string[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private particleManagerService: ParticleManagerService,
|
private particleManagerService: ParticleManagerService,
|
||||||
) {
|
) {
|
||||||
|
this.filteredParticleTypes = this.particleTypeControl.valueChanges.pipe(
|
||||||
|
startWith(''),
|
||||||
|
map(value => this._filterParticleTypes(value || ''))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
// Initialize the particle type control with the current value
|
||||||
|
this.particleTypeControl.setValue(this.selectedParticle);
|
||||||
|
|
||||||
|
// Update the selected particle when the control value changes
|
||||||
|
this.particleTypeControl.valueChanges.subscribe(value => {
|
||||||
|
if (value && Object.values(Particle).includes(value)) {
|
||||||
|
this.selectedParticle = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterParticleTypes(value: string): string[] {
|
||||||
|
const filterValue = value.toLowerCase();
|
||||||
|
return this.particleTypes.filter(type => type.toLowerCase().includes(filterValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display function for the autocomplete
|
||||||
|
displayFn(particle: string): string {
|
||||||
|
return particle ? particle : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,4 +85,31 @@ export class ParticleComponent {
|
||||||
this.particleManagerService.setSelectedColor(color);
|
this.particleManagerService.setSelectedColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected particle type
|
||||||
|
*/
|
||||||
|
public get selectedParticle(): Particle {
|
||||||
|
return this.particleManagerService.particle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected particle type
|
||||||
|
*/
|
||||||
|
public set selectedParticle(particle: Particle) {
|
||||||
|
this.particleManagerService.particle = particle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected particle size
|
||||||
|
*/
|
||||||
|
public get selectedSize(): number {
|
||||||
|
return this.particleManagerService.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected particle size
|
||||||
|
*/
|
||||||
|
public set selectedSize(size: number) {
|
||||||
|
this.particleManagerService.size = size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export class ParticleManagerService {
|
||||||
*/
|
*/
|
||||||
addParticle(x: number, y: number, z: number): void {
|
addParticle(x: number, y: number, z: number): void {
|
||||||
// Create a visual representation of the particle
|
// Create a visual representation of the particle
|
||||||
const particleGeometry = new THREE.SphereGeometry(0.03, 16, 16);
|
const particleGeometry = new THREE.SphereGeometry(0.03 * this.selectedSize, 16, 16);
|
||||||
const particleMaterial = new THREE.MeshBasicMaterial({color: this.selectedColor});
|
const particleMaterial = new THREE.MeshBasicMaterial({color: this.selectedColor});
|
||||||
const particleMesh = new THREE.Mesh(particleGeometry, particleMaterial);
|
const particleMesh = new THREE.Mesh(particleGeometry, particleMaterial);
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ export class ParticleManagerService {
|
||||||
if (!this.particleData.frames[frameId]) return;
|
if (!this.particleData.frames[frameId]) return;
|
||||||
|
|
||||||
for (const particleInfo of this.particleData.frames[frameId]) {
|
for (const particleInfo of this.particleData.frames[frameId]) {
|
||||||
const particleGeometry = new THREE.SphereGeometry(0.03, 16, 16);
|
const particleGeometry = new THREE.SphereGeometry(0.03 * (particleInfo.size ?? 1), 16, 16);
|
||||||
|
|
||||||
const color = this.getColor(particleInfo);
|
const color = this.getColor(particleInfo);
|
||||||
const particleMaterial = new THREE.MeshBasicMaterial({color});
|
const particleMaterial = new THREE.MeshBasicMaterial({color});
|
||||||
|
|
@ -123,7 +123,7 @@ export class ParticleManagerService {
|
||||||
const particleInfo = this.particleData.frames[frameId][index];
|
const particleInfo = this.particleData.frames[frameId][index];
|
||||||
const color = this.getColor(particleInfo);
|
const color = this.getColor(particleInfo);
|
||||||
const particleMaterial = new THREE.MeshBasicMaterial({color});
|
const particleMaterial = new THREE.MeshBasicMaterial({color});
|
||||||
const particleGeometry = new THREE.SphereGeometry(0.03, 16, 16);
|
const particleGeometry = new THREE.SphereGeometry(0.03 * (particleInfo.size ?? 1), 16, 16);
|
||||||
const particleMesh = new THREE.Mesh(particleGeometry, particleMaterial);
|
const particleMesh = new THREE.Mesh(particleGeometry, particleMaterial);
|
||||||
particleMesh.position.set(particleInfo.x, particleInfo.y, particleInfo.z);
|
particleMesh.position.set(particleInfo.x, particleInfo.y, particleInfo.z);
|
||||||
this.rendererService.scene.add(particleMesh);
|
this.rendererService.scene.add(particleMesh);
|
||||||
|
|
@ -135,17 +135,6 @@ export class ParticleManagerService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getColor(particleInfo: ParticleInfo) {
|
|
||||||
if (particleInfo.color) {
|
|
||||||
const r = parseInt(particleInfo.color.substring(0, 2), 16) / 255;
|
|
||||||
const g = parseInt(particleInfo.color.substring(2, 4), 16) / 255;
|
|
||||||
const b = parseInt(particleInfo.color.substring(4, 6), 16) / 255;
|
|
||||||
return new THREE.Color(r, g, b);
|
|
||||||
} else {
|
|
||||||
return new THREE.Color(255, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private animatePulse(mesh: THREE.Mesh, cycles: number, onComplete: () => void): void {
|
private animatePulse(mesh: THREE.Mesh, cycles: number, onComplete: () => void): void {
|
||||||
const duration = 300;
|
const duration = 300;
|
||||||
const maxScale = 0.08 / 0.03;
|
const maxScale = 0.08 / 0.03;
|
||||||
|
|
@ -167,6 +156,17 @@ export class ParticleManagerService {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getColor(particleInfo: ParticleInfo) {
|
||||||
|
if (particleInfo.color) {
|
||||||
|
const r = parseInt(particleInfo.color.substring(0, 2), 16) / 255;
|
||||||
|
const g = parseInt(particleInfo.color.substring(2, 4), 16) / 255;
|
||||||
|
const b = parseInt(particleInfo.color.substring(4, 6), 16) / 255;
|
||||||
|
return new THREE.Color(r, g, b);
|
||||||
|
} else {
|
||||||
|
return new THREE.Color(255, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the selected color for new particles
|
* Sets the selected color for new particles
|
||||||
*/
|
*/
|
||||||
|
|
@ -181,6 +181,22 @@ export class ParticleManagerService {
|
||||||
return this.selectedColor;
|
return this.selectedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get particle(): Particle {
|
||||||
|
return this.selectedParticle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set particle(selectedParticle: Particle) {
|
||||||
|
this.selectedParticle = selectedParticle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get size(): number {
|
||||||
|
return this.selectedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set size(selectedSize: number) {
|
||||||
|
this.selectedSize = selectedSize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the particle data
|
* Gets the particle data
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user