PrintVault: GCode 3D path viewer with layer color gradient
Deploy HouseHub / deploy (push) Successful in 2s
Deploy HouseHub / deploy (push) Successful in 2s
- New API endpoint gcode_paths: parses G0/G1 moves, returns segments (max 30k) - Three.js LineSegments renderer: extrusion moves colored by Z height Blue(low) → Cyan → Green → Yellow → Red(high), travel moves dimmed - Legend overlay showing color scale - GCode files now show viewer + metadata cards (layout unchanged) - PHP syntax validated, API endpoint tested before deploy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b756517ab5
commit
811d186b46
@@ -238,6 +238,51 @@ if ($action === 'models') {
|
||||
}
|
||||
}
|
||||
|
||||
// ── GCODE PATHS ────────────────────────────────────────────────────────────────
|
||||
if ($action === 'gcode_paths') {
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$max = min((int)($_GET['max'] ?? 30000), 80000);
|
||||
$s = $pdo->prepare("SELECT filename, file_type FROM pf_pv_models WHERE id=?"); $s->execute([$id]);
|
||||
$row = $s->fetch(); if (!$row) pvErr('Introuvable', 404);
|
||||
$ext = strtolower($row['file_type']);
|
||||
if (!in_array($ext, ['gcode','gco','g'])) pvErr('Pas un fichier GCode');
|
||||
$path = PV_MODEL_DIR . $row['filename'];
|
||||
if (!file_exists($path)) pvErr('Fichier introuvable', 404);
|
||||
|
||||
$segments = [];
|
||||
$x=0.0; $y=0.0; $z=0.0; $e=0.0; $lastE=0.0;
|
||||
$minZ=PHP_FLOAT_MAX; $maxZ=0.0;
|
||||
|
||||
$handle = fopen($path, 'r');
|
||||
while (!feof($handle) && count($segments) < $max) {
|
||||
$line = trim(fgets($handle));
|
||||
if (($p = strpos($line, ';')) !== false) $line = substr($line, 0, $p);
|
||||
$line = trim($line);
|
||||
if (!$line) continue;
|
||||
$cmd = strtoupper(strtok($line, ' '));
|
||||
if ($cmd === 'G92') {
|
||||
if (preg_match('/E([-\d.]+)/i', $line, $m)) $lastE = (float)$m[1];
|
||||
continue;
|
||||
}
|
||||
if ($cmd !== 'G0' && $cmd !== 'G1') continue;
|
||||
$nx=$x; $ny=$y; $nz=$z; $ne=$e;
|
||||
if (preg_match('/X([-\d.]+)/i', $line, $m)) $nx = (float)$m[1];
|
||||
if (preg_match('/Y([-\d.]+)/i', $line, $m)) $ny = (float)$m[1];
|
||||
if (preg_match('/Z([-\d.]+)/i', $line, $m)) $nz = (float)$m[1];
|
||||
if (preg_match('/E([-\d.]+)/i', $line, $m)) $ne = (float)$m[1];
|
||||
$extruding = ($cmd === 'G1' && $ne > $lastE) ? 1 : 0;
|
||||
if ($nx !== $x || $ny !== $y || $nz !== $z) {
|
||||
$segments[] = [$x, $y, $z, $nx, $ny, $nz, $extruding];
|
||||
$minZ = min($minZ, $nz);
|
||||
$maxZ = max($maxZ, $nz);
|
||||
}
|
||||
$x=$nx; $y=$ny; $z=$nz;
|
||||
if ($ne !== $e) { $lastE = $e = $ne; }
|
||||
}
|
||||
fclose($handle);
|
||||
pvOk(['segments' => $segments, 'min_z' => $minZ === PHP_FLOAT_MAX ? 0 : $minZ, 'max_z' => $maxZ, 'total' => count($segments)]);
|
||||
}
|
||||
|
||||
// ── FILE SERVE ─────────────────────────────────────────────────────────────────
|
||||
if ($action === 'file') {
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
|
||||
@@ -146,6 +146,20 @@
|
||||
font-size: .72rem; color: var(--text-muted); line-height: 1.3;
|
||||
}
|
||||
|
||||
/* GCode legend */
|
||||
.pv-gcode-viewer-legend {
|
||||
position: absolute; bottom: 2.5rem; left: 1rem;
|
||||
background: rgba(15,23,42,.8); backdrop-filter: blur(4px);
|
||||
border: 1px solid rgba(255,255,255,.1);
|
||||
border-radius: 8px; padding: .5rem .75rem;
|
||||
display: flex; flex-direction: column; gap: .3rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
.pv-legend-item {
|
||||
font-size: .72rem; color: #94a3b8;
|
||||
display: flex; align-items: center; gap: .5rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.pv-detail-body { grid-template-columns: 1fr; grid-template-rows: 55vh 1fr; }
|
||||
|
||||
Reference in New Issue
Block a user