feat: add Orcaslicer and docker

This commit is contained in:
2026-01-27 22:02:49 +01:00
parent 00e62fc558
commit 7dc6741808
21 changed files with 1986 additions and 1123 deletions

View File

@@ -1,37 +1,69 @@
<mat-card>
<mat-card-title>3D Print Cost Calculator</mat-card-title>
<div class="calculator-container">
<mat-card>
<mat-card-header>
<mat-card-title>3D Print Quote Calculator</mat-card-title>
<mat-card-subtitle>Bambu Lab A1 Estimation</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div class="upload-section">
<input type="file" (change)="onFileSelected($event)" accept=".stl" #fileInput style="display: none;">
<button mat-raised-button color="primary" (click)="fileInput.click()">
{{ file ? file.name : 'Select STL File' }}
</button>
<button mat-raised-button color="accent"
[disabled]="!file || loading"
(click)="uploadAndCalculate()">
Calculate Quote
</button>
</div>
<input
type="file"
accept=".stl"
(change)="onFileSelected($event)"
/>
<div *ngIf="loading" class="spinner-container">
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
<p>Slicing model... this may take a minute...</p>
</div>
<button
mat-raised-button
color="primary"
(click)="uploadAndCalculate()"
[disabled]="!file || loading"
>
{{ loading ? 'Calculating...' : 'Calculate' }}
</button>
<div *ngIf="error" class="error-message">
{{ error }}
</div>
<mat-progress-spinner
*ngIf="loading"
diameter="30"
mode="indeterminate"
></mat-progress-spinner>
<div *ngIf="results" class="results-section">
<div class="total-price">
<h3>Total Estimate: € {{ results.cost.total | number:'1.2-2' }}</h3>
</div>
<p *ngIf="error" class="error">{{ error }}</p>
<div class="details-grid">
<div class="detail-item">
<span class="label">Print Time:</span>
<span class="value">{{ results.print_time_formatted }}</span>
</div>
<div class="detail-item">
<span class="label">Material Used:</span>
<span class="value">{{ results.material_grams | number:'1.1-1' }} g</span>
</div>
</div>
<div *ngIf="results">
<p>Volume STL: {{ results.stl_volume_mm3 }} mm³</p>
<p>Superficie: {{ results.surface_area_mm2 }} mm²</p>
<p>Volume pareti: {{ results.wall_volume_mm3 }} mm³</p>
<p>Volume infill: {{ results.infill_volume_mm3 }} mm³</p>
<p>Volume stampa: {{ results.print_volume_mm3 }} mm³</p>
<p>Peso stimato: {{ results.weight_g }} g</p>
<p>Costo stimato: CHF {{ results.cost_chf }}</p>
<p>Tempo stimato: {{ results.time_min }} min</p>
</div>
</mat-card>
<h4>Cost Breakdown</h4>
<ul class="breakdown-list">
<li>
<span>Material</span>
<span>€ {{ results.cost.material | number:'1.2-2' }}</span>
</li>
<li>
<span>Machine Time</span>
<span>€ {{ results.cost.machine | number:'1.2-2' }}</span>
</li>
<li>
<span>Energy</span>
<span>€ {{ results.cost.energy | number:'1.2-2' }}</span>
</li>
<li>
<span>Service/Markup</span>
<span>€ {{ results.cost.markup | number:'1.2-2' }}</span>
</li>
</ul>
</div>
</mat-card-content>
</mat-card>
</div>

View File

@@ -4,10 +4,22 @@ import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
interface QuoteResponse {
printer: string;
print_time_formatted: string;
material_grams: number;
cost: {
material: number;
machine: number;
energy: number;
markup: number;
total: number;
};
}
@Component({
selector: 'app-calculator',
standalone: true,
@@ -15,7 +27,6 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
CommonModule,
FormsModule,
MatCardModule,
MatFormFieldModule,
MatButtonModule,
MatProgressSpinnerModule
],
@@ -24,7 +35,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
})
export class CalculatorComponent {
file: File | null = null;
results: any = null;
results: QuoteResponse | null = null;
error = '';
loading = false;
@@ -41,22 +52,26 @@ export class CalculatorComponent {
uploadAndCalculate(): void {
if (!this.file) {
this.error = 'Seleziona un file STL prima di procedere.';
this.error = 'Please select a file first.';
return;
}
const formData = new FormData();
formData.append('file', this.file);
this.loading = true;
this.http.post<any>('http://localhost:8000/calculate/stl', formData)
this.error = '';
this.results = null;
this.http.post<QuoteResponse>('http://localhost:8000/calculate/stl', formData)
.subscribe({
next: res => {
this.results = res;
this.loading = false;
},
error: err => {
this.error = err.error?.detail || err.message;
console.error(err);
this.error = err.error?.detail || "An error occurred during calculation.";
this.loading = false;
}
});
}
}
}