有构建板,支持多模型构建,但生成支撑的切片还有bug
This commit is contained in:
8
tmp/parse_bed.py
Normal file
8
tmp/parse_bed.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
with open('print_config/printers/creality_ender3v3se.def.json') as f:
|
||||
d = json.load(f)
|
||||
print(d['overrides']['machine_width']['default_value'])
|
||||
print(d['overrides']['machine_depth']['default_value'])
|
||||
print(d['overrides']['machine_height']['default_value'])
|
||||
19
tmp/parse_presets.py
Normal file
19
tmp/parse_presets.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
import configparser
|
||||
|
||||
def get_quality_presets():
|
||||
preset_dir = os.path.join(os.path.dirname(__file__), 'print_config', 'presets', 'creality', 'base')
|
||||
presets = []
|
||||
if os.path.exists(preset_dir):
|
||||
for f in os.listdir(preset_dir):
|
||||
if f.startswith('base_global_') and f.endswith('.inst.cfg'):
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
config.read(os.path.join(preset_dir, f))
|
||||
name = config.get('general', 'name', fallback=f)
|
||||
presets.append((f, name))
|
||||
except Exception as e:
|
||||
pass
|
||||
return sorted(presets, key=lambda x: x[1])
|
||||
|
||||
print(get_quality_presets())
|
||||
53
tmp/patch_proxy.py
Normal file
53
tmp/patch_proxy.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import re
|
||||
|
||||
with open('app/routes.py', 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
|
||||
# Add serve_proxy_file after serve_file
|
||||
serve_file_code = """@main_bp.route('/file/<int:file_id>')
|
||||
@login_required
|
||||
def serve_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
abort(403)
|
||||
path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename)
|
||||
return send_file(path)"""
|
||||
|
||||
proxy_code = """@main_bp.route('/file/<int:file_id>')
|
||||
@login_required
|
||||
def serve_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
abort(403)
|
||||
path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename)
|
||||
return send_file(path)
|
||||
|
||||
@main_bp.route('/proxy/<int:file_id>')
|
||||
@login_required
|
||||
def serve_proxy_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
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):
|
||||
from stl_simplifier import simplify_stl
|
||||
try:
|
||||
simplify_stl(path, proxy_path, keep_ratio=0.1) # compress to 10%
|
||||
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)"""
|
||||
|
||||
text = text.replace(serve_file_code, proxy_code)
|
||||
|
||||
# Change plater models to use serve_proxy_file
|
||||
old_str = "url_for('main.serve_file', file_id=f.id)"
|
||||
new_str = "url_for('main.serve_proxy_file', file_id=f.id)"
|
||||
|
||||
text = text.replace(old_str, new_str)
|
||||
|
||||
with open('app/routes.py', 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
print("Patched successfully")
|
||||
36
tmp/patch_routes.py
Normal file
36
tmp/patch_routes.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import re
|
||||
|
||||
with open('app/routes.py', 'r', encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
|
||||
old_str = """ print_file = PrintFile(
|
||||
filename=unique_filename,
|
||||
original_filename=f"{combined_name}.stl",
|
||||
file_type='stl',
|
||||
user_id=current_user.id,
|
||||
status='waiting'
|
||||
)
|
||||
db.session.add(print_file)
|
||||
db.session.commit()
|
||||
|
||||
slice_stl_task(print_file.id, merged_filepath, quality)"""
|
||||
|
||||
new_str = """ print_file = PrintFile(
|
||||
filename=unique_filename,
|
||||
original_filename=f"{combined_name}.stl",
|
||||
file_type='stl',
|
||||
user_id=current_user.id,
|
||||
status='merging'
|
||||
)
|
||||
db.session.add(print_file)
|
||||
db.session.commit()
|
||||
|
||||
from .tasks import merge_and_slice_task
|
||||
merge_and_slice_task(print_file.id, inputs, merged_filepath, quality)"""
|
||||
|
||||
if old_str in text:
|
||||
with open('app/routes.py', 'w', encoding='utf-8') as f:
|
||||
f.write(text.replace(old_str, new_str))
|
||||
print("Patched successfully")
|
||||
else:
|
||||
print("Old string not found")
|
||||
141
tmp/repatch_routes.py
Normal file
141
tmp/repatch_routes.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import json, os, uuid
|
||||
from datetime import datetime
|
||||
|
||||
with open('app/routes.py', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
prefix = """import json
|
||||
import uuid
|
||||
import os
|
||||
"""
|
||||
|
||||
functions = """
|
||||
def get_bed_dimensions():
|
||||
try:
|
||||
from flask import current_app
|
||||
path = os.path.join(current_app.root_path, '..', 'print_config', 'printers', 'creality_ender3v3se.def.json')
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
w = data['overrides']['machine_width']['default_value']
|
||||
h = data['overrides']['machine_depth']['default_value']
|
||||
hd = data['overrides']['machine_height']['default_value']
|
||||
return w, h, hd
|
||||
except:
|
||||
return 200, 200, 200
|
||||
|
||||
def get_quality_presets():
|
||||
try:
|
||||
from flask import current_app
|
||||
path = os.path.join(current_app.root_path, '..', 'print_config', 'presets', 'creality', 'base')
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
presets = []
|
||||
for f in files:
|
||||
name = f.replace('.inst.cfg', '').replace('base_', '').replace('_', ' ')
|
||||
presets.append((f, name))
|
||||
return presets
|
||||
except:
|
||||
return []
|
||||
"""
|
||||
|
||||
routes = """
|
||||
@main_bp.route('/plater')
|
||||
@login_required
|
||||
def plater():
|
||||
w, h, hd = get_bed_dimensions()
|
||||
presets = get_quality_presets()
|
||||
last_quality = request.cookies.get('last_quality_preset', 'base_global_standard.inst.cfg')
|
||||
user_files = PrintFile.query.filter_by(user_id=current_user.id, file_type='stl').order_by(PrintFile.created_at.desc()).all()
|
||||
models = [{'id': f.id, 'name': f.original_filename, 'url': url_for('main.serve_proxy_file', file_id=f.id)} for f in user_files]
|
||||
return render_template('plater.html', w=w, h=h, hd=hd, presets=presets, last_quality=last_quality, models=models)
|
||||
|
||||
@main_bp.route('/file/<int:file_id>')
|
||||
@login_required
|
||||
def serve_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
abort(403)
|
||||
path = os.path.join(current_app.config['UPLOAD_FOLDER'], f.filename)
|
||||
return send_file(path)
|
||||
|
||||
@main_bp.route('/proxy/<int:file_id>')
|
||||
@login_required
|
||||
def serve_proxy_file(file_id):
|
||||
f = PrintFile.query.get_or_404(file_id)
|
||||
if f.user_id != current_user.id and not current_user.is_admin:
|
||||
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):
|
||||
from stl_simplifier import simplify_stl
|
||||
try:
|
||||
simplify_stl(path, proxy_path, keep_ratio=0.1) # compress to 10%
|
||||
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)
|
||||
|
||||
@main_bp.route('/api/merge_and_slice', methods=['POST'])
|
||||
@login_required
|
||||
def merge_and_slice():
|
||||
data = request.json
|
||||
pieces = data.get('pieces', [])
|
||||
quality = data.get('quality', 'base_global_standard.inst.cfg')
|
||||
|
||||
if not pieces:
|
||||
return jsonify({'error': 'No pieces provided'}), 400
|
||||
|
||||
inputs = []
|
||||
# Build a combined name
|
||||
names = []
|
||||
for p in pieces[:3]: # Cap names at 3 to avoid super long string
|
||||
f = PrintFile.query.get(p['file_id'])
|
||||
if f and (f.user_id == current_user.id or current_user.is_admin):
|
||||
names.append(f.original_filename.replace('.stl', ''))
|
||||
|
||||
combined_name = ", ".join(names)
|
||||
if len(pieces) > 3:
|
||||
combined_name += "等合并切片"
|
||||
else:
|
||||
combined_name += "合并切片"
|
||||
|
||||
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 not inputs:
|
||||
return jsonify({'error': 'Invalid files'}), 400
|
||||
|
||||
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
|
||||
unique_filename = f"{timestamp}_{uuid.uuid4().hex}.stl"
|
||||
merged_filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], unique_filename)
|
||||
|
||||
print_file = PrintFile(
|
||||
filename=unique_filename,
|
||||
original_filename=f"{combined_name}.stl",
|
||||
file_type='stl',
|
||||
user_id=current_user.id,
|
||||
status='merging'
|
||||
)
|
||||
db.session.add(print_file)
|
||||
db.session.commit()
|
||||
|
||||
from .tasks import merge_and_slice_task
|
||||
merge_and_slice_task(print_file.id, inputs, merged_filepath, quality)
|
||||
|
||||
return jsonify({'success': True, 'message': 'Plater slice queued!'})
|
||||
"""
|
||||
|
||||
# Only patch if not already present
|
||||
if 'def plater()' not in content:
|
||||
with open('app/routes.py', 'w', encoding='utf-8') as f:
|
||||
# Prepend imports if needed
|
||||
lines = content.split('\n')
|
||||
# Insert mostly at the end
|
||||
new_content = "\n".join(lines[:-1]) + functions + routes + "\n"
|
||||
f.write(prefix + new_content)
|
||||
print("Repatched successfully.")
|
||||
else:
|
||||
print("Already present.")
|
||||
14
tmp/test_pq.py
Normal file
14
tmp/test_pq.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from app import create_app
|
||||
from app.routes import get_quality_presets
|
||||
|
||||
import logging
|
||||
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
||||
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
presets = get_quality_presets()
|
||||
print("Presets length:", len(presets))
|
||||
if len(presets) > 0:
|
||||
print("First 3 presets:")
|
||||
for p in presets[:3]:
|
||||
print(p)
|
||||
19
tmp/test_pq2.py
Normal file
19
tmp/test_pq2.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import os, configparser
|
||||
|
||||
preset_dir = 'print_config/presets/creality/base'
|
||||
presets = []
|
||||
for f in os.listdir(preset_dir):
|
||||
if f.endswith('.inst.cfg'):
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(preset_dir, f))
|
||||
try:
|
||||
name = config.get('general', 'name', fallback=f)
|
||||
except:
|
||||
name = f.replace('.inst.cfg', '').replace('base_', '').replace('_', ' ')
|
||||
presets.append((f, name))
|
||||
|
||||
presets = sorted(presets, key=lambda x: str(x[1]).lower())
|
||||
print("Presets length:", len(presets))
|
||||
if len(presets) > 0:
|
||||
for p in presets[:5]:
|
||||
print(p)
|
||||
9
tmp/test_pq3.py
Normal file
9
tmp/test_pq3.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from app import create_app
|
||||
from app.routes import get_quality_presets
|
||||
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
presets = get_quality_presets()
|
||||
print("Presets length:", len(presets))
|
||||
for p in presets[:5]:
|
||||
print(p)
|
||||
35
tmp/test_simplifier.py
Normal file
35
tmp/test_simplifier.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import numpy as np
|
||||
from stl import mesh
|
||||
|
||||
def simplify_stl_grid(input_path, output_path, target_ratio=0.1):
|
||||
m = mesh.Mesh.from_file(input_path)
|
||||
vertices = m.vectors.reshape(-1, 3)
|
||||
|
||||
min_v = vertices.min(axis=0)
|
||||
max_v = vertices.max(axis=0)
|
||||
bbox_size = max_v - min_v
|
||||
max_dim = np.max(bbox_size)
|
||||
|
||||
# Adjust resolution to rough target_ratio by guessing.
|
||||
# The number of vertices drops roughly by (resolution_factor)^2.
|
||||
# So if we want 10% faces, resolution_factor can be heuristically set.
|
||||
# Let's try 0.05
|
||||
grid_size = max_dim * 0.05
|
||||
|
||||
v_idx = np.round((vertices - min_v) / grid_size).astype(np.int32)
|
||||
_, unique_idx, inv_idx = np.unique(v_idx, axis=0, return_index=True, return_inverse=True)
|
||||
|
||||
new_vertices = vertices[unique_idx]
|
||||
faces = inv_idx.reshape(-1, 3)
|
||||
|
||||
valid = (faces[:,0] != faces[:,1]) & (faces[:,1] != faces[:,2]) & (faces[:,0] != faces[:,2])
|
||||
valid_faces = faces[valid]
|
||||
|
||||
new_m = mesh.Mesh(np.zeros(valid_faces.shape[0], dtype=mesh.Mesh.dtype))
|
||||
for i, f in enumerate(valid_faces):
|
||||
for j in range(3):
|
||||
new_m.vectors[i][j] = new_vertices[f[j]]
|
||||
|
||||
new_m.update_normals()
|
||||
new_m.save(output_path)
|
||||
|
||||
Reference in New Issue
Block a user