缩放后修复,合并有问题

This commit is contained in:
2026-04-13 08:20:00 +08:00
parent a3f8a31432
commit dad17dbadd
11 changed files with 285 additions and 122 deletions

View File

@@ -22,6 +22,12 @@
<div class="form-text">{{ _('Adjust the Y-axis compilation offset for combined files on the build plate.') }}</div>
</div>
<div class="mb-3">
<label for="proxy_skip_size_mb" class="form-label">{{ _('Proxy Skip Size (MB)') }}</label>
<input type="number" class="form-control" name="proxy_skip_size_mb" id="proxy_skip_size_mb" value="{{ configs.get('proxy_skip_size_mb', '5.0') }}" step="0.1" min="0">
<div class="form-text">{{ _('Files smaller than this will not generate a simplified proxy.') }}</div>
</div>
<h5 class="mt-4">{{ _('Default Plater Settings') }}</h5>
<hr>

View File

@@ -40,11 +40,16 @@
<tbody>
{% for file in files %}
<tr id="file-row-{{ file.id }}" data-status="{{ file.status }}">
<td class="ps-4 text-muted"><i class="bi bi-clock me-1"></i>{{ file.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</td>
<td class="ps-4 text-muted">
<i class="bi bi-clock me-1"></i>
<span class="local-time" data-utc="{{ file.created_at.isoformat() }}">{{ file.created_at.strftime('%Y-%m-%d %H:%M:%S') }}</span>
</td>
<td class="fw-medium">{{ file.original_filename }}</td>
<td id="status-{{ file.id }}">
{% if file.status == 'waiting' %}
<span class="badge bg-info text-dark rounded-pill fw-normal px-2" title="{{ _('Waiting in queue for slicing') }}"><i class="bi bi-hourglass-split me-1"></i>{{ _('Waiting') }}...</span>
{% elif file.status == 'simplifying' %}
<span class="badge bg-secondary text-dark rounded-pill fw-normal px-2" style="background-color: #e2e3e5 !important;"><i class="bi bi-funnel bi-spin me-1"></i>{{ _('Simplifying') }}...</span>
{% elif file.status == 'uploaded' %}
<span class="badge bg-secondary text-light rounded-pill fw-normal px-2"><i class="bi bi-cloud-check me-1"></i>{{ _('Uploaded') }}</span>
{% elif file.status == 'merging' %}
@@ -86,6 +91,25 @@
<script>
document.addEventListener('DOMContentLoaded', function() {
// Convert UTC to local time
document.querySelectorAll('.local-time').forEach(el => {
let utcStr = el.getAttribute('data-utc');
if (!utcStr) return;
if (!utcStr.endsWith('Z') && !utcStr.includes('+')) {
utcStr += 'Z';
}
const localDate = new Date(utcStr);
if (!isNaN(localDate)) {
const year = localDate.getFullYear();
const month = String(localDate.getMonth() + 1).padStart(2, '0');
const day = String(localDate.getDate()).padStart(2, '0');
const hours = String(localDate.getHours()).padStart(2, '0');
const minutes = String(localDate.getMinutes()).padStart(2, '0');
const seconds = String(localDate.getSeconds()).padStart(2, '0');
el.textContent = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
});
const checkInterval = 1000;
let pollTimer = null;
@@ -105,6 +129,7 @@ document.addEventListener('DOMContentLoaded', function() {
const actionsTd = document.getElementById('actions-container-' + id);
if (status === 'waiting') statusTd.innerHTML = '<span class="badge bg-info text-dark rounded-pill fw-normal px-2"><i class="bi bi-hourglass-split me-1"></i>{{ _("Waiting") }}...</span>';
else if (status === 'simplifying') statusTd.innerHTML = '<span class="badge bg-secondary text-dark rounded-pill fw-normal px-2" style="background-color: #e2e3e5 !important;"><i class="bi bi-funnel bi-spin me-1"></i>{{ _("Simplifying") }}...</span>';
else if (status === 'uploaded') statusTd.innerHTML = '<span class="badge bg-secondary text-light rounded-pill fw-normal px-2"><i class="bi bi-cloud-check me-1"></i>{{ _("Uploaded") }}</span>';
else if (status === 'merging') statusTd.innerHTML = '<span class="badge bg-primary text-light rounded-pill fw-normal px-2"><i class="bi bi-intersect me-1"></i>{{ _("Merging") }}...</span>';
else if (status === 'slicing') statusTd.innerHTML = '<span class="badge bg-warning text-dark rounded-pill fw-normal px-2"><i class="bi bi-gear-wide-connected bi-spin me-1"></i>{{ _("Slicing") }}...</span>';
@@ -128,7 +153,7 @@ document.addEventListener('DOMContentLoaded', function() {
actionsTd.innerHTML = actionsHtml;
}
if (status === 'waiting' || status === 'slicing' || status === 'merging') {
if (status === 'waiting' || status === 'slicing' || status === 'merging' || status === 'simplifying') {
hasPending = true;
}
}

View File

@@ -480,14 +480,59 @@ function clearPlate() {
}
function addModelToPlate(btnElement, fileId, url, name, status) {
const iconSpan = btnElement.querySelector('i');
const originalClass = iconSpan.className;
iconSpan.className = 'spinner-border spinner-border-sm text-primary';
btnElement.disabled = true;
let matrixData = btnElement ? btnElement.getAttribute('data-matrix') : null;
if (matrixData && matrixData.includes('"is_composite"')) {
try {
let comp = JSON.parse(matrixData);
if (comp.is_composite && comp.parts) {
if (btnElement) {
const iconSpan = btnElement.querySelector('i');
const originalClass = iconSpan.className;
iconSpan.className = 'spinner-border spinner-border-sm text-primary';
btnElement.disabled = true;
let totalParts = comp.parts.length;
let loadedCount = 0;
comp.parts.forEach(part => {
loadSTL(part.file_id, part.url, part.name, 'uploaded', JSON.stringify(part.raw_matrix), () => {
loadedCount++;
if (loadedCount === totalParts) {
iconSpan.className = originalClass;
btnElement.disabled = false;
}
});
});
} else {
comp.parts.forEach(part => {
loadSTL(part.file_id, part.url, part.name, 'uploaded', JSON.stringify(part.raw_matrix));
});
}
return;
}
} catch (e) {
console.error(e);
}
}
if (btnElement) {
const iconSpan = btnElement.querySelector('i');
const originalClass = iconSpan.className;
iconSpan.className = 'spinner-border spinner-border-sm text-primary';
btnElement.disabled = true;
loadSTL(fileId, url, name, status, matrixData, () => {
iconSpan.className = originalClass;
btnElement.disabled = false;
});
} else {
loadSTL(fileId, url, name, status, matrixData);
}
}
function loadSTL(fileId, url, name, status, matrixData, callback) {
const loader = new THREE.STLLoader();
loader.load(url, function (geometry) {
// By default STLs center or are offset, let's normalize slightly to be on top of the plate
geometry.computeBoundingBox();
const center = geometry.boundingBox.getCenter(new THREE.Vector3());
const minZ = geometry.boundingBox.min.z;
@@ -502,8 +547,7 @@ function addModelToPlate(btnElement, fileId, url, name, status) {
geomTrans: new THREE.Matrix4().makeTranslation(-center.x, -center.y, -minZ)
};
let matrixData = btnElement.getAttribute('data-matrix');
if (matrixData && matrixData.trim() !== '' && matrixData !== 'None') {
if (matrixData && matrixData.trim() !== '' && matrixData !== 'None' && !matrixData.includes('"is_composite"')) {
try {
let mArray = JSON.parse(matrixData);
let savedMatrix = new THREE.Matrix4().fromArray(mArray);
@@ -514,7 +558,6 @@ function addModelToPlate(btnElement, fileId, url, name, status) {
mesh.position.y = (Math.random() - 0.5) * 50;
}
} else {
// Random slight offset so they don't exactly stack
mesh.position.x = (Math.random() - 0.5) * 50;
mesh.position.y = (Math.random() - 0.5) * 50;
}
@@ -522,13 +565,11 @@ function addModelToPlate(btnElement, fileId, url, name, status) {
scene.add(mesh);
loadedModels.push(mesh);
selectModel(mesh);
iconSpan.className = originalClass;
btnElement.disabled = false;
if (callback) callback();
}, undefined, function (error) {
console.error(error);
iconSpan.className = originalClass;
btnElement.disabled = false;
if (callback) callback();
alert("{{ _('Error loading STL model file.') }}");
});
}
@@ -581,6 +622,8 @@ function animate() {
}
function mergeAndSlice() {
selectModel(null); // Detach any active model to bake transformProxy world coordinates into its local matrix properties
if (loadedModels.length === 0) {
alert("{{ _('Please add at least one model to the build plate.') }}");
return;
@@ -591,14 +634,27 @@ function mergeAndSlice() {
return;
}
let isEdit = (loadedModels.length === 1 && String(loadedModels[0].userData.fileId) === String(initialAddId));
let isEdit = false;
let targetFileId = null;
if (window.isCompositeEdit) {
isEdit = true;
targetFileId = initialAddId;
} else if (loadedModels.length === 1 && String(loadedModels[0].userData.fileId) === String(initialAddId)) {
isEdit = true;
targetFileId = initialAddId;
}
if (isEdit) {
const singleModel = loadedModels[0];
if (singleModel.userData.status === 'sliced') {
// Just checking if we want to warn
if (loadedModels.length === 1 && loadedModels[0].userData.status === 'sliced') {
if (!confirm("{{ _('This model has already been sliced. The existing GCode will be overwritten. Continue?') }}")) {
return;
}
} else if (window.isCompositeEdit) {
if (!confirm("{{ _('You are editing a composite model. The existing composite will be updated and re-sliced. Continue?') }}")) {
return;
}
}
}
@@ -613,7 +669,7 @@ function mergeAndSlice() {
return {
file_id: m.userData.fileId,
matrix: mat.elements, // Array of 16 numbers used for slicing
raw_matrix: m.matrix.elements // Local visual properties
raw_matrix: m.matrixWorld.elements // Use world matrix explicitly just in case
};
});
@@ -636,7 +692,7 @@ function mergeAndSlice() {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ pieces: pieces, quality: quality, infill: infill, support: support, support_pattern: supportPattern, is_edit: isEdit })
body: JSON.stringify({ pieces: pieces, quality: quality, infill: infill, support: support, support_pattern: supportPattern, is_edit: isEdit, target_file_id: targetFileId })
})
.then(response => response.json())
.then(data => {
@@ -671,6 +727,10 @@ document.addEventListener('DOMContentLoaded', () => {
if (addId) {
const btn = document.getElementById('add-model-btn-' + addId);
if (btn) {
let matrixData = btn.getAttribute('data-matrix');
if (matrixData && matrixData.includes('"is_composite"')) {
window.isCompositeEdit = true;
}
btn.click();
}
}