From dad17dbaddfaca1415709af3a6448ff4a37cfec7 Mon Sep 17 00:00:00 2001 From: lhye200 Date: Mon, 13 Apr 2026 08:20:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=A9=E6=94=BE=E5=90=8E=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=EF=BC=8C=E5=90=88=E5=B9=B6=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- app/routes.py | 75 ++++++++++++---- app/tasks.py | 50 +++++++++++ app/templates/admin_settings.html | 6 ++ app/templates/files.html | 29 ++++++- app/templates/plater.html | 96 +++++++++++++++++---- assets/i18n/en.json | 4 + assets/i18n/zh-cn.json | 4 + out.stl | Bin 0 -> 184 bytes stl_merger.py | 138 +++++++++++------------------- test_out.STL | Bin 684 -> 0 bytes 11 files changed, 285 insertions(+), 122 deletions(-) create mode 100644 out.stl delete mode 100644 test_out.STL diff --git a/.gitignore b/.gitignore index c495d6a..0bc8dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ __pycache__ uploads/* -tmp/* \ No newline at end of file +tmp/* +venv +instance +huey_queue.* \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 65e9f76..b6c8c38 100644 --- a/app/routes.py +++ b/app/routes.py @@ -9,7 +9,7 @@ from flask_login import login_user, logout_user, login_required, current_user from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.utils import secure_filename from .models import db, User, PrintFile, SystemConfig -from .tasks import merge_and_slice_task, slice_stl_task +from .tasks import merge_and_slice_task, slice_stl_task, simplify_stl_task from app import i18n_dict # import trimesh.repair from stl_simplifier import simplify_stl @@ -118,11 +118,14 @@ def files(): original_filename=original_filename, file_type='stl', user_id=current_user.id, - status='uploaded' # Only display as uploaded, no automatic slicing + status='simplifying' # Set to simplifying while proxy is generated ) db.session.add(print_file) db.session.commit() + # Start background simplification + simplify_stl_task(print_file.id, filepath) + flash('File uploaded successfully!', 'success') return redirect(url_for('main.files')) @@ -241,6 +244,7 @@ def settings(): # concurrent_slices = request.form.get('concurrent_slices') offset_x = request.form.get('offset_x', '0') offset_y = request.form.get('offset_y', '0') + proxy_skip_size_mb = request.form.get('proxy_skip_size_mb', '5.0') default_infill = request.form.get('default_infill', '20') default_support = request.form.get('default_support', 'false') default_support_pattern = request.form.get('default_support_pattern', 'tree') @@ -250,6 +254,7 @@ def settings(): config_items = [ ('offset_x', offset_x), ('offset_y', offset_y), + ('proxy_skip_size_mb', proxy_skip_size_mb), ('default_infill', default_infill), ('default_support', default_support), ('default_support_pattern', default_support_pattern), @@ -368,11 +373,6 @@ def serve_proxy_file(file_id): abort(403) path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename) proxy_path = path + '.proxy.stl' - if not os.path.exists(proxy_path): - try: - simplify_stl(path, proxy_path, keep_ratio=0.05) # compress to 90% - except: - return send_file(path) # fallback to original if error if os.path.exists(proxy_path): return send_file(proxy_path) return send_file(path) @@ -406,24 +406,53 @@ def merge_and_slice(): else: combined_name += " 单独切片" + is_edit = data.get('is_edit', False) + for p in pieces: f = PrintFile.query.get(p['file_id']) if f and (f.user_id == current_user.id or current_user.is_admin): path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename) inputs.append((path, p['matrix'])) - if 'raw_matrix' in p: + # 只有在单一编辑模式才修改原模型的矩阵 (如果多模型/新建模式,我们不修改原模型,而是后续记录到新的包含实体上) + if 'raw_matrix' in p and is_edit and len(pieces) == 1: f.transform_matrix = json.dumps(p['raw_matrix']) db.session.add(f) db.session.commit() - if len(inputs) == 0: - return jsonify({'error': 'Invalid files'}), 400 + target_file_id = data.get('target_file_id') - is_edit = data.get('is_edit', False) - - if len(inputs) == 1 and is_edit: - # User is just generating gcode for a single original model, do NOT pollute list with new STL + if is_edit and target_file_id: + print_file = PrintFile.query.get(target_file_id) + if not print_file: + return jsonify({'error': 'Original file not found'}), 404 + print_file.status = 'merging' + + if print_file.transform_matrix and 'is_composite' in print_file.transform_matrix: + composite_data = { + "is_composite": True, + "parts": [] + } + for p in pieces: + pf = PrintFile.query.get(p['file_id']) + if pf: + composite_data['parts'].append({ + "file_id": pf.id, + "name": pf.original_filename, + "url": url_for('main.serve_proxy_file', file_id=pf.id), + "raw_matrix": p.get('raw_matrix', p['matrix']) + }) + print_file.transform_matrix = json.dumps(composite_data) + elif len(pieces) == 1: + print_file.transform_matrix = json.dumps(pieces[0].get('raw_matrix', pieces[0]['matrix'])) + + db.session.commit() + + temp_filename = f"temp_edit_{uuid.uuid4().hex}.stl" + temp_filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], temp_filename) + + merge_and_slice_task(target_file_id, inputs, temp_filepath, quality, infill_density, support_enable, support_pattern, delete_stl=True) + elif len(inputs) == 1 and is_edit: target_file_id = pieces[0]['file_id'] print_file = PrintFile.query.get(target_file_id) if not print_file: @@ -442,12 +471,28 @@ def merge_and_slice(): unique_filename = f"{timestamp}_{uuid.uuid4().hex}.stl" merged_filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], unique_filename) + # 构建组合文件元数据树 (is_composite: true) + composite_data = { + "is_composite": True, + "parts": [] + } + for p in pieces: + pf = PrintFile.query.get(p['file_id']) + if pf: + composite_data['parts'].append({ + "file_id": pf.id, + "name": pf.original_filename, + "url": url_for('main.serve_proxy_file', file_id=pf.id), + "raw_matrix": p.get('raw_matrix', p['matrix']) + }) + print_file = PrintFile( filename=unique_filename, original_filename=f"{combined_name}.stl", file_type='stl', user_id=current_user.id, - status='merging' + status='merging', + transform_matrix=json.dumps(composite_data) ) db.session.add(print_file) db.session.commit() diff --git a/app/tasks.py b/app/tasks.py index e8a36c5..591a09c 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -238,3 +238,53 @@ def merge_and_slice_task(file_id, inputs, merged_filepath, quality_preset=None, app.logger.error(f"Merge Exception: {e}") finally: db.session.remove() + +@huey.task() +def simplify_stl_task(file_id, filepath): + from app import create_app + app = create_app() + with app.app_context(): + from .models import PrintFile, SystemConfig, db + import os + from stl_simplifier import simplify_stl + + print_file = PrintFile.query.get(file_id) + if not print_file: + return + + try: + file_size_mb = os.path.getsize(filepath) / (1024 * 1024) + + configs = {c.key: c.value for c in SystemConfig.query.all()} + skip_size = float(configs.get('proxy_skip_size_mb', '5.0')) + + proxy_path = filepath + '.proxy.stl' + + if file_size_mb <= skip_size: + # File is small enough, no proxy needed + print_file.status = 'uploaded' + db.session.commit() + return + + # Aim for approx 7.5 MB for the proxy + target_mb = 7.5 + keep_ratio = target_mb / file_size_mb + if keep_ratio > 1.0: + keep_ratio = 1.0 + elif keep_ratio < 0.01: + keep_ratio = 0.01 + + app.logger.info(f"Simplifying {filepath}... Size: {file_size_mb:.2f}MB, Target Ratio: {keep_ratio:.3f}") + + simplify_stl(filepath, proxy_path, keep_ratio=keep_ratio) + + except Exception as e: + app.logger.error(f"Simplify task error: {e}") + + # Update status to uploaded regardless of success or failure of proxy generation + # So the user can still slice or download it + print_file = PrintFile.query.get(file_id) + if print_file: + print_file.status = 'uploaded' + db.session.commit() + db.session.remove() diff --git a/app/templates/admin_settings.html b/app/templates/admin_settings.html index d19d01c..61b7e23 100644 --- a/app/templates/admin_settings.html +++ b/app/templates/admin_settings.html @@ -22,6 +22,12 @@
{{ _('Adjust the Y-axis compilation offset for combined files on the build plate.') }}
+
+ + +
{{ _('Files smaller than this will not generate a simplified proxy.') }}
+
+
{{ _('Default Plater Settings') }}

diff --git a/app/templates/files.html b/app/templates/files.html index 465d5a3..fa19b32 100644 --- a/app/templates/files.html +++ b/app/templates/files.html @@ -40,11 +40,16 @@ {% for file in files %} - {{ file.created_at.strftime('%Y-%m-%d %H:%M:%S') }} + + + {{ file.created_at.strftime('%Y-%m-%d %H:%M:%S') }} + {{ file.original_filename }} {% if file.status == 'waiting' %} {{ _('Waiting') }}... + {% elif file.status == 'simplifying' %} + {{ _('Simplifying') }}... {% elif file.status == 'uploaded' %} {{ _('Uploaded') }} {% elif file.status == 'merging' %} @@ -86,6 +91,25 @@