diff --git a/app/routers/models.py b/app/routers/models.py index 3f1d568..40a9c75 100644 --- a/app/routers/models.py +++ b/app/routers/models.py @@ -560,18 +560,22 @@ def validate_mesh(model_id: int, db: Session = Depends(get_db)): @router.get("/{model_id}/estimate") -def estimate_print(model_id: int, price_per_kg: float = Query(20.0), material_density: float = Query(1.24), db: Session = Depends(get_db)): +def estimate_print(model_id: int, file_id: Optional[int] = Query(None), price_per_kg: float = Query(20.0), material_density: float = Query(1.24), db: Session = Depends(get_db)): model = db.query(Model3D).filter(Model3D.id == model_id).first() if not model: raise HTTPException(status_code=404, detail="Model not found") - primary = next((f for f in model.files if f.is_primary and f.file_type in ('stl', '3mf')), None) - if not primary: + if file_id: + target = next((f for f in model.files if f.id == file_id and f.file_type in ('stl', '3mf')), None) + else: + target = next((f for f in model.files if f.is_primary and f.file_type in ('stl', '3mf')), None) + + if not target: raise HTTPException(status_code=404, detail="No 3D file found") try: import trimesh - mesh = trimesh.load(primary.file_path, force='mesh') + mesh = trimesh.load(target.file_path, force='mesh') volume_cm3 = abs(float(mesh.volume)) / 1000.0 grams = volume_cm3 * material_density cost = (grams / 1000.0) * price_per_kg @@ -579,7 +583,11 @@ def estimate_print(model_id: int, price_per_kg: float = Query(20.0), material_de hours = seconds // 3600 mins = (seconds % 3600) // 60 + bounds = mesh.bounds + dims = bounds[1] - bounds[0] return { + "file_id": target.id, + "part_name": target.part_name or target.filename, "volume_cm3": round(volume_cm3, 2), "grams": round(grams, 2), "cost": round(cost, 2), @@ -587,6 +595,9 @@ def estimate_print(model_id: int, price_per_kg: float = Query(20.0), material_de "estimated_seconds": seconds, "price_per_kg": price_per_kg, "material_density": material_density, + "width_mm": round(float(dims[0]), 1), + "depth_mm": round(float(dims[1]), 1), + "height_mm": round(float(dims[2]), 1), } except Exception as e: raise HTTPException(status_code=500, detail=f"Estimation failed: {str(e)}") diff --git a/static/detail.html b/static/detail.html index 16dce5e..3742151 100644 --- a/static/detail.html +++ b/static/detail.html @@ -171,6 +171,17 @@
+ +No hay partes para estimar.
'; + return; + } + + container.innerHTML = 'Calculando placas...
'; + + const PLATE_SIZE = 220; // mm (standard bed size) + let totalCost = 0; + let totalTime = 0; + let totalGrams = 0; + + const results = []; + for (const part of parts) { + try { + const data = await apiGet(`/models/${getModelId()}/estimate?file_id=${part.id}`); + totalCost += data.cost; + totalTime += data.estimated_seconds; + totalGrams += data.grams; + + // Determine fit color + const maxDim = Math.max(data.width_mm || 0, data.depth_mm || 0); + const fitColor = maxDim > PLATE_SIZE ? 'bg-red-500' : maxDim > PLATE_SIZE * 0.8 ? 'bg-yellow-500' : 'bg-cyan-500'; + const fitText = maxDim > PLATE_SIZE ? 'No cabe en placa estandar' : 'Cabe en placa'; + + // Scale for visual (200px = 220mm) + const scale = 200 / PLATE_SIZE; + const rectW = Math.min((data.width_mm || 10) * scale, 200); + const rectH = Math.min((data.depth_mm || 10) * scale, 200); + + results.push({ + ...data, + part, + fitColor, + fitText, + rectW, + rectH, + }); + } catch (err) { + results.push({ part, error: err.message }); + } + } + + const totalHours = Math.floor(totalTime / 3600); + const totalMins = Math.floor((totalTime % 3600) / 60); + + container.innerHTML = ` +Total estimado (${results.length} placa${results.length > 1 ? 's' : ''})
+