2D
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
config.json
|
config.json
|
||||||
refer/*
|
# refer/*
|
||||||
# __pycache__/*
|
*/__pycache__/*
|
||||||
|
.venv/*
|
||||||
45
refer/GcodeParser.py
Normal file
45
refer/GcodeParser.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def load_gcode_vertices(path):
|
||||||
|
vertices = []
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
z = 0
|
||||||
|
|
||||||
|
with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith("G0") or line.startswith("G1"):
|
||||||
|
old_x = x
|
||||||
|
old_y = y
|
||||||
|
old_z = z
|
||||||
|
|
||||||
|
mx = re.search(r"X([-0-9.]+)", line)
|
||||||
|
my = re.search(r"Y([-0-9.]+)", line)
|
||||||
|
mz = re.search(r"Z([-0-9.]+)", line)
|
||||||
|
|
||||||
|
if mx:
|
||||||
|
x = float(mx.group(1))
|
||||||
|
|
||||||
|
if my:
|
||||||
|
y = float(my.group(1))
|
||||||
|
|
||||||
|
if mz:
|
||||||
|
z = float(mz.group(1))
|
||||||
|
|
||||||
|
vertices.append({
|
||||||
|
"x1": old_x,
|
||||||
|
"y1": old_y,
|
||||||
|
"z1": old_z,
|
||||||
|
"x2": x,
|
||||||
|
"y2": y,
|
||||||
|
"z2": z,
|
||||||
|
})
|
||||||
|
|
||||||
|
return vertices
|
||||||
77
refer/api_handle.py
Normal file
77
refer/api_handle.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import functools
|
||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from app.models import ApiKey
|
||||||
|
from app.utils.octoprint_client import OctoPrintClient
|
||||||
|
from app.models import SystemConfig
|
||||||
|
|
||||||
|
api_bp = Blueprint('api_handle', __name__, url_prefix='/api/v1')
|
||||||
|
|
||||||
|
def get_octo_client():
|
||||||
|
url = SystemConfig.query.filter_by(key='octoprint_url').first()
|
||||||
|
apikey = SystemConfig.query.filter_by(key='octoprint_apikey').first()
|
||||||
|
if url and url.value and apikey and apikey.value:
|
||||||
|
return OctoPrintClient(url.value, apikey.value)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def require_api_key(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def decorated(*args, **kwargs):
|
||||||
|
api_key_header = request.headers.get('X-Api-Key')
|
||||||
|
if not api_key_header:
|
||||||
|
return jsonify({'error': 'Missing API Key in headers (X-Api-Key)'}), 401
|
||||||
|
|
||||||
|
key_record = ApiKey.query.filter_by(key=api_key_header).first()
|
||||||
|
if not key_record:
|
||||||
|
return jsonify({'error': 'Invalid API Key'}), 401
|
||||||
|
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated
|
||||||
|
|
||||||
|
@api_bp.route('/status', methods=['GET'])
|
||||||
|
@require_api_key
|
||||||
|
def get_status():
|
||||||
|
client = get_octo_client()
|
||||||
|
if not client:
|
||||||
|
return jsonify({'error': 'Printer not configured'}), 503
|
||||||
|
try:
|
||||||
|
status_data = client.get_printer_status()
|
||||||
|
job_data = client.get_job_info()
|
||||||
|
return jsonify({'status': status_data, 'job': job_data})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@api_bp.route('/octoprint_client', methods=['POST'])
|
||||||
|
@require_api_key
|
||||||
|
def invoke_octoprint_client():
|
||||||
|
"""
|
||||||
|
Expects JSON payload like:
|
||||||
|
{
|
||||||
|
"method": "pause_print",
|
||||||
|
"kwargs": {"action": "pause"}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
client = get_octo_client()
|
||||||
|
if not client:
|
||||||
|
return jsonify({'error': 'Printer not configured'}), 503
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
if not data or 'method' not in data:
|
||||||
|
return jsonify({'error': 'Missing method in JSON payload'}), 400
|
||||||
|
|
||||||
|
method_name = data['method']
|
||||||
|
kwargs = data.get('kwargs', {})
|
||||||
|
args = data.get('args', [])
|
||||||
|
|
||||||
|
if not hasattr(client, method_name):
|
||||||
|
return jsonify({'error': f'Method {method_name} not found on OctoPrintClient'}), 400
|
||||||
|
|
||||||
|
func = getattr(client, method_name)
|
||||||
|
if not callable(func) or method_name.startswith('_'):
|
||||||
|
return jsonify({'error': f'Method {method_name} is not allowed'}), 403
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
return jsonify({'success': True, 'result': result})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
549
refer/gcode_preview.html
Normal file
549
refer/gcode_preview.html
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-2 border-bottom">
|
||||||
|
<div>
|
||||||
|
<h1 class="h2 mb-1"><i class="bi bi-eye text-primary me-2"></i>{{ _('GCode Preview') }}: {{ file.original_filename }}</h1>
|
||||||
|
<div class="text-muted small">
|
||||||
|
<span class="me-3"><i class="bi bi-clock-history me-1"></i>{{ _('Estimated Time:') }} <span class="fw-bold">{{ time_info }}</span></span>
|
||||||
|
<span class="me-3"><i class="bi bi-layers me-1"></i>{{ _('First Layer Time:') }} <span class="fw-bold">{{ layer1_time }}</span></span>
|
||||||
|
<span><i class="bi bi-rulers me-1"></i>{{ _('Filament Used [mm]:') }} <span class="fw-bold">{{ filament_used }}</span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 mt-md-0">
|
||||||
|
<a href="{{ url_for('printer.prepare') }}#file-{{ file.id }}" class="btn btn-warning btn-sm rounded shadow-sm fw-bold"><i class="bi bi-printer"></i> {{ _('Go to Print') }}</a>
|
||||||
|
<a href="{{ url_for('main.download_gcode', file_id=file.id) }}" class="btn btn-primary btn-sm rounded shadow-sm ms-2"><i class="bi bi-download"></i> {{ _('Download GCode') }}</a>
|
||||||
|
<a href="{{ url_for('main.files') }}" class="btn btn-outline-secondary btn-sm rounded ms-2 shadow-sm"><i class="bi bi-arrow-left"></i> {{ _('Back') }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loading-overlay" class="text-center py-5 my-5">
|
||||||
|
<div class="spinner-border text-primary shadow-sm" role="status" style="width: 3rem; height: 3rem;"></div>
|
||||||
|
<h4 class="mt-4 text-secondary">{{ _('Loading and Parsing GCode Data...') }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row d-none" id="preview-container" style="height: 75vh;">
|
||||||
|
<!-- 3D Canvas Area -->
|
||||||
|
<div class="col-md-11 position-relative h-100 p-0 border rounded border-secondary shadow-sm" style="background: #111;">
|
||||||
|
<div id="canvas-container" class="w-100 h-100 d-block overflow-hidden"></div>
|
||||||
|
|
||||||
|
<!-- Legend Overlay -->
|
||||||
|
<div id="legend-overlay" class="position-absolute top-0 start-0 m-3 p-2 rounded shadow bg-dark bg-opacity-75 border border-secondary" style="color: #eee; font-size: 0.85rem; pointer-events: auto; z-index: 10;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Slider (Intra-Layer Progress) -->
|
||||||
|
<div class="position-absolute bottom-0 start-0 w-100 p-3" style="background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.8) 100%); z-index: 10;">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<span class="text-white fw-medium text-nowrap user-select-none"><i class="bi bi-play-circle me-1"></i>{{ _('Layer Progress:') }}</span>
|
||||||
|
<input type="range" class="form-range flex-grow-1" id="progress-slider" min="0" max="100" value="100" step="0.1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right Sidebar (Layer Slider) -->
|
||||||
|
<div class="col-md-1 h-100 d-flex flex-column align-items-center justify-content-center bg-white border rounded shadow-sm position-relative">
|
||||||
|
<label class="form-label mb-3 fw-bold text-center text-primary mt-3">{{ _('Layer') }}<br>
|
||||||
|
<span id="layer-display" class="badge bg-primary fs-6 mt-1 shadow-sm px-3 rounded-pill">0</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex-grow-1 w-100 d-flex justify-content-center pb-4 py-2">
|
||||||
|
<input type="range" class="form-range h-100" id="layer-slider" min="0" max="0" value="0" style="writing-mode: bt-lr; -webkit-appearance: slider-vertical; cursor: ns-resize;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='js/three.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/OrbitControls.js') }}"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
|
||||||
|
// Inject printer machine dimensions via Jinja
|
||||||
|
const bedWidth = {{ machine_width | default(220) }};
|
||||||
|
const bedDepth = {{ machine_depth | default(220) }};
|
||||||
|
const bedHeight = {{ machine_height | default(250) }};
|
||||||
|
const offsetX = {{ offset_x | default(0.0) }};
|
||||||
|
const offsetY = {{ offset_y | default(0.0) }};
|
||||||
|
|
||||||
|
// Type indices for shader visibility filtering
|
||||||
|
let COLORS = {};
|
||||||
|
let TYPE_INDEX = {};
|
||||||
|
let gcodeMat = null;
|
||||||
|
|
||||||
|
const SLICER_CONFIGS = {
|
||||||
|
'Cura': [
|
||||||
|
{ id: 'TRAVEL', label: '{{ _("Travel (Move)") }}', color: 0x405060, defaultShow: false },
|
||||||
|
{ id: 'WALL-OUTER', label: '{{ _("Outer Wall") }}', color: 0xeb8b38, defaultShow: true },
|
||||||
|
{ id: 'WALL-INNER', label: '{{ _("Inner Wall") }}', color: 0x4080cf, defaultShow: true },
|
||||||
|
{ id: 'FILL', label: '{{ _("Infill") }}', color: 0xccc04b, defaultShow: true },
|
||||||
|
{ id: 'SKIN', label: '{{ _("Skin/TopBottom") }}', color: 0x9e60b3, defaultShow: true },
|
||||||
|
{ id: 'SUPPORT', label: '{{ _("Support") }}', color: 0x57b357, defaultShow: true },
|
||||||
|
{ id: 'SKIRT', label: '{{ _("Skirt") }}', color: 0x00ffff, defaultShow: true },
|
||||||
|
{ id: 'SUPPORT-INTERFACE', label: '{{ _("Support Interface") }}', color: 0x2b6b2b, defaultShow: true },
|
||||||
|
{ id: 'DEFAULT', label: '{{ _("Others") }}', color: 0xaaaaaa, defaultShow: true }
|
||||||
|
],
|
||||||
|
'Prusa': [
|
||||||
|
{ id: 'TRAVEL', label: '{{ _("Travel (Move)") }}', color: 0x405060, defaultShow: false },
|
||||||
|
{ id: 'Custom', label: '{{ _("Custom") }}', color: 0xd0e0ff, defaultShow: true },
|
||||||
|
{ id: 'Skirt/Brim', label: '{{ _("Skirt/Brim") }}', color: 0x00FFFF, defaultShow: true },
|
||||||
|
{ id: 'Support material', label: '{{ _("Support material") }}', color: 0x90EE90, defaultShow: true },
|
||||||
|
{ id: 'Perimeter', label: '{{ _("Perimeter") }}', color: 0xFFFFE0, defaultShow: true },
|
||||||
|
{ id: 'External perimeter', label: '{{ _("External perimeter") }}', color: 0xFFA500, defaultShow: true },
|
||||||
|
{ id: 'Solid infill', label: '{{ _("Solid infill") }}', color: 0x800080, defaultShow: true },
|
||||||
|
{ id: 'Overhang perimeter', label: '{{ _("Overhang perimeter") }}', color: 0x00008B, defaultShow: true },
|
||||||
|
{ id: 'Internal infill', label: '{{ _("Internal infill") }}', color: 0x8B0000, defaultShow: true },
|
||||||
|
{ id: 'Bridge infill', label: '{{ _("Bridge infill") }}', color: 0x0000FF, defaultShow: true },
|
||||||
|
{ id: 'Top solid infill', label: '{{ _("Top solid infill") }}', color: 0xFF0000, defaultShow: true },
|
||||||
|
{ id: 'Support material interface', label: '{{ _("Support Interface") }}', color: 0x2b6b2b, defaultShow: true },
|
||||||
|
{ id: 'DEFAULT', label: '{{ _("Others") }}', color: 0xaaaaaa, defaultShow: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let layers = [];
|
||||||
|
let scene, camera, renderer, controls;
|
||||||
|
let group = new THREE.Group();
|
||||||
|
|
||||||
|
const layerSlider = document.getElementById('layer-slider');
|
||||||
|
const layerDisplay = document.getElementById('layer-display');
|
||||||
|
const progressSlider = document.getElementById('progress-slider');
|
||||||
|
|
||||||
|
function setupSlicerConfig(text) {
|
||||||
|
let slicerType = 'Cura'; // default
|
||||||
|
if (text.substring(0, 500).includes('generated by PrusaSlicer')) {
|
||||||
|
slicerType = 'Prusa';
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = SLICER_CONFIGS[slicerType];
|
||||||
|
|
||||||
|
// 1. Build uniforms & shader strings dynamically
|
||||||
|
let uniformsObj = {};
|
||||||
|
let fragmentUniformsDecl = '';
|
||||||
|
let fragmentUniformsLogic = '';
|
||||||
|
|
||||||
|
let overlayHTML = '';
|
||||||
|
|
||||||
|
config.forEach((c, idx) => {
|
||||||
|
COLORS[c.id] = new THREE.Color(c.color);
|
||||||
|
TYPE_INDEX[c.id] = idx;
|
||||||
|
|
||||||
|
const uniformName = 'uShow' + idx;
|
||||||
|
uniformsObj[uniformName] = { value: c.defaultShow ? 1.0 : 0.0 };
|
||||||
|
|
||||||
|
fragmentUniformsDecl += `uniform float ${uniformName};\n`;
|
||||||
|
|
||||||
|
if (idx === 0) {
|
||||||
|
fragmentUniformsLogic += `if (t == 0) show = ${uniformName};\n`;
|
||||||
|
} else {
|
||||||
|
fragmentUniformsLogic += ` else if (t == ${idx}) show = ${uniformName};\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Legend UI
|
||||||
|
const hexColor = '#' + c.color.toString(16).padStart(6, '0');
|
||||||
|
const opacityStyle = c.defaultShow ? '1.0' : '0.4';
|
||||||
|
overlayHTML += `
|
||||||
|
<div class="mb-1 legend-item user-select-none" data-id="${c.id}" data-uniform="${uniformName}" style="cursor: pointer; transition: opacity 0.2s; opacity: ${opacityStyle};"><span class="d-inline-block rounded-circle me-2 border border-dark" style="width: 12px; height: 12px; background: ${hexColor};"></span>${c.label}</div>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add fallback condition
|
||||||
|
fragmentUniformsLogic += ` else show = 1.0;\n`;
|
||||||
|
|
||||||
|
document.getElementById('legend-overlay').innerHTML = overlayHTML;
|
||||||
|
|
||||||
|
gcodeMat = new THREE.ShaderMaterial({
|
||||||
|
uniforms: uniformsObj,
|
||||||
|
vertexShader: `
|
||||||
|
attribute float pType;
|
||||||
|
varying vec3 vColor;
|
||||||
|
varying float vType;
|
||||||
|
void main() {
|
||||||
|
vColor = color;
|
||||||
|
vType = pType;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
varying vec3 vColor;
|
||||||
|
varying float vType;
|
||||||
|
${fragmentUniformsDecl}
|
||||||
|
void main() {
|
||||||
|
float show = 1.0;
|
||||||
|
int t = int(vType + 0.5);
|
||||||
|
${fragmentUniformsLogic}
|
||||||
|
if (show < 0.5) discard;
|
||||||
|
gl_FragColor = vec4(vColor, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
vertexColors: true,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
linewidth: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
// Legend binding
|
||||||
|
document.querySelectorAll('.legend-item').forEach(el => {
|
||||||
|
el.addEventListener('click', function() {
|
||||||
|
const uniformName = this.dataset.uniform;
|
||||||
|
if (uniformName && gcodeMat.uniforms[uniformName]) {
|
||||||
|
const currentVal = gcodeMat.uniforms[uniformName].value;
|
||||||
|
const newVal = currentVal > 0.5 ? 0.0 : 1.0;
|
||||||
|
gcodeMat.uniforms[uniformName].value = newVal;
|
||||||
|
this.style.opacity = newVal > 0.5 ? "1.0" : "0.4";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init3D() {
|
||||||
|
const container = document.getElementById('canvas-container');
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
scene.background = new THREE.Color(0x1a1a1a);
|
||||||
|
|
||||||
|
scene.add(group);
|
||||||
|
|
||||||
|
camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 1, 5000);
|
||||||
|
camera.up.set(0, 0, 1);
|
||||||
|
|
||||||
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
|
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
container.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||||||
|
controls.enableDamping = false;
|
||||||
|
controls.mouseButtons.MIDDLE = THREE.MOUSE.PAN;
|
||||||
|
|
||||||
|
window.addEventListener('resize', onWindowResize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWindowResize() {
|
||||||
|
const container = document.getElementById('canvas-container');
|
||||||
|
camera.aspect = container.clientWidth / container.clientHeight;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
controls.update();
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadGCode() {
|
||||||
|
try {
|
||||||
|
const url = '{{ url_for("main.download_gcode", file_id=file.id) }}';
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) throw new Error("GCode Request Failed");
|
||||||
|
|
||||||
|
const gcodeText = await response.text();
|
||||||
|
|
||||||
|
document.getElementById('loading-overlay').classList.add('d-none');
|
||||||
|
document.getElementById('preview-container').classList.remove('d-none');
|
||||||
|
|
||||||
|
setupSlicerConfig(gcodeText);
|
||||||
|
init3D();
|
||||||
|
parseGCode(gcodeText);
|
||||||
|
|
||||||
|
// Add grid matching printer size
|
||||||
|
setupMachineEnvironment();
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
|
// Init controls
|
||||||
|
layerSlider.max = Math.max(0, layers.length - 1);
|
||||||
|
layerSlider.value = Math.max(0, layers.length - 1);
|
||||||
|
updateUI();
|
||||||
|
|
||||||
|
layerSlider.addEventListener('input', updateUI);
|
||||||
|
progressSlider.addEventListener('input', updateUI);
|
||||||
|
|
||||||
|
} catch(e) {
|
||||||
|
console.error("Error Loading GCode", e);
|
||||||
|
document.getElementById('loading-overlay').innerHTML = `
|
||||||
|
<div class="text-danger my-5 py-5">
|
||||||
|
<i class="bi bi-exclamation-triangle display-1"></i>
|
||||||
|
<h3 class="mt-3">{{ _('Failed to load GCode preview.') }}</h3>
|
||||||
|
<p class="text-muted">${e.toString()}</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseGCode(text) {
|
||||||
|
const lines = text.split('\n');
|
||||||
|
|
||||||
|
let current = { x: 0, y: 0, z: 0, e: 0 };
|
||||||
|
let relativeE = false; // Track M83 (relative) vs M82 (absolute)
|
||||||
|
|
||||||
|
// Dynamically compute width and layer height based on gcode info if possible
|
||||||
|
let extWidth = 0.4;
|
||||||
|
let layerHeight = 0.2;
|
||||||
|
let pWidth = extWidth;
|
||||||
|
|
||||||
|
let currentTypeStr = 'DEFAULT';
|
||||||
|
|
||||||
|
let currentExtrudePoints = [];
|
||||||
|
let currentExtrudeColors = [];
|
||||||
|
let currentExtrudeTypes = [];
|
||||||
|
|
||||||
|
let currentTravelPoints = [];
|
||||||
|
let currentTravelColors = [];
|
||||||
|
let currentTravelTypes = [];
|
||||||
|
|
||||||
|
function flushLayer() {
|
||||||
|
if (currentExtrudePoints.length === 0 && currentTravelPoints.length === 0) return;
|
||||||
|
|
||||||
|
let layerGroup = new THREE.Group();
|
||||||
|
|
||||||
|
if (currentExtrudePoints.length > 0) {
|
||||||
|
const geo = new THREE.BufferGeometry();
|
||||||
|
geo.setAttribute('position', new THREE.Float32BufferAttribute(currentExtrudePoints, 3));
|
||||||
|
geo.setAttribute('color', new THREE.Float32BufferAttribute(currentExtrudeColors, 3));
|
||||||
|
geo.setAttribute('pType', new THREE.Float32BufferAttribute(currentExtrudeTypes, 1));
|
||||||
|
const mesh = new THREE.Mesh(geo, gcodeMat);
|
||||||
|
mesh.userData.isExtrude = true;
|
||||||
|
layerGroup.add(mesh);
|
||||||
|
currentExtrudePoints = []; currentExtrudeColors = []; currentExtrudeTypes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTravelPoints.length > 0) {
|
||||||
|
const geo = new THREE.BufferGeometry();
|
||||||
|
geo.setAttribute('position', new THREE.Float32BufferAttribute(currentTravelPoints, 3));
|
||||||
|
geo.setAttribute('color', new THREE.Float32BufferAttribute(currentTravelColors, 3));
|
||||||
|
geo.setAttribute('pType', new THREE.Float32BufferAttribute(currentTravelTypes, 1));
|
||||||
|
const lineSeg = new THREE.LineSegments(geo, gcodeMat);
|
||||||
|
lineSeg.userData.isTravel = true;
|
||||||
|
layerGroup.add(lineSeg);
|
||||||
|
currentTravelPoints = []; currentTravelColors = []; currentTravelTypes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
layers.push(layerGroup);
|
||||||
|
group.add(layerGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
let chunk = lines[i].trim();
|
||||||
|
if (!chunk) continue;
|
||||||
|
let upperChunk = chunk.toUpperCase();
|
||||||
|
|
||||||
|
if (upperChunk.startsWith('M82')) relativeE = false;
|
||||||
|
else if (upperChunk.startsWith('M83')) relativeE = true;
|
||||||
|
|
||||||
|
if (upperChunk.startsWith(';LAYER:') || upperChunk.startsWith(';LAYER_CHANGE')) {
|
||||||
|
flushLayer();
|
||||||
|
} else if (upperChunk.startsWith(';LAYER_HEIGHT:')) {
|
||||||
|
let lh = parseFloat(chunk.substring(14));
|
||||||
|
if (!isNaN(lh) && lh > 0) layerHeight = lh;
|
||||||
|
} else if (upperChunk.startsWith(';HEIGHT:')) {
|
||||||
|
let lh = parseFloat(chunk.substring(8));
|
||||||
|
if (!isNaN(lh) && lh > 0) layerHeight = lh;
|
||||||
|
} else if (upperChunk.startsWith(';WIDTH:')) {
|
||||||
|
let w = parseFloat(chunk.substring(7));
|
||||||
|
if (!isNaN(w) && w > 0) pWidth = w;
|
||||||
|
} else if (upperChunk.startsWith(';TYPE:')) {
|
||||||
|
currentTypeStr = chunk.substring(6).trim();
|
||||||
|
} else if (chunk.startsWith(';') && COLORS[chunk.substring(1).trim()] !== undefined) {
|
||||||
|
currentTypeStr = chunk.substring(1).trim();
|
||||||
|
} else if (upperChunk.startsWith(';') && chunk.includes(' perimeter')) {
|
||||||
|
currentTypeStr = chunk.substring(1).trim();
|
||||||
|
} else if (upperChunk.startsWith(';') && chunk.includes(' infill')) {
|
||||||
|
// Heuristics for Prusa/Slic3r specific comments like `; Internal infill`
|
||||||
|
currentTypeStr = chunk.substring(1).trim();
|
||||||
|
} else if (upperChunk.startsWith(';') && chunk.includes(' material')) {
|
||||||
|
// Support material
|
||||||
|
currentTypeStr = chunk.substring(1).trim();
|
||||||
|
} else if (upperChunk.startsWith(';') && chunk.includes('Skirt/Brim')) {
|
||||||
|
// Skirt/Brim
|
||||||
|
currentTypeStr = 'Skirt/Brim';
|
||||||
|
} else if (upperChunk.startsWith('G0') || upperChunk.startsWith('G1')) {
|
||||||
|
let next = { x: current.x, y: current.y, z: current.z, e: current.e };
|
||||||
|
let parts = upperChunk.split(/\s+/);
|
||||||
|
let hasMove = false;
|
||||||
|
let hasE = false;
|
||||||
|
let eVal = 0;
|
||||||
|
|
||||||
|
for (let p of parts) {
|
||||||
|
if (p.startsWith('X')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) { next.x = v; hasMove = true; } }
|
||||||
|
if (p.startsWith('Y')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) { next.y = v; hasMove = true; } }
|
||||||
|
if (p.startsWith('Z')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) { next.z = v; hasMove = true; } }
|
||||||
|
if (p.startsWith('E')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) { eVal = v; hasE = true; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasMove && !isNaN(next.x) && !isNaN(next.y) && !isNaN(next.z)) {
|
||||||
|
let isExtrude = false;
|
||||||
|
if (hasE) {
|
||||||
|
if (relativeE) {
|
||||||
|
next.e = current.e + eVal;
|
||||||
|
isExtrude = eVal > 0;
|
||||||
|
} else {
|
||||||
|
next.e = eVal;
|
||||||
|
isExtrude = next.e > current.e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Cura uses G0 for travel generally
|
||||||
|
if (upperChunk.startsWith('G0') && !upperChunk.includes('E')) isExtrude = false;
|
||||||
|
|
||||||
|
let activeType = isExtrude ? currentTypeStr : 'TRAVEL';
|
||||||
|
let resolvedType = activeType;
|
||||||
|
|
||||||
|
if (isExtrude && COLORS[activeType] === undefined) {
|
||||||
|
resolvedType = 'DEFAULT';
|
||||||
|
}
|
||||||
|
|
||||||
|
let col = COLORS[resolvedType] || COLORS['DEFAULT'];
|
||||||
|
let tIdx = TYPE_INDEX[resolvedType] !== undefined ? TYPE_INDEX[resolvedType] : TYPE_INDEX['DEFAULT'];
|
||||||
|
|
||||||
|
if (isExtrude) {
|
||||||
|
let dx = next.x - current.x;
|
||||||
|
let dy = next.y - current.y;
|
||||||
|
let dist = Math.sqrt(dx*dx + dy*dy);
|
||||||
|
if (dist > 0.0001) {
|
||||||
|
let hw = pWidth / 2.0;
|
||||||
|
let hh = layerHeight / 2.0;
|
||||||
|
|
||||||
|
let nx = -(dy / dist) * hw;
|
||||||
|
let ny = (dx / dist) * hw;
|
||||||
|
|
||||||
|
let p1x = current.x + nx, p1y = current.y + ny; // current-left
|
||||||
|
let p2x = current.x - nx, p2y = current.y - ny; // current-right
|
||||||
|
let p3x = next.x + nx, p3y = next.y + ny; // next-left
|
||||||
|
let p4x = next.x - nx, p4y = next.y - ny; // next-right
|
||||||
|
|
||||||
|
// Top face
|
||||||
|
currentExtrudePoints.push(
|
||||||
|
p1x, p1y, current.z + hh,
|
||||||
|
p3x, p3y, next.z + hh,
|
||||||
|
p2x, p2y, current.z + hh,
|
||||||
|
p3x, p3y, next.z + hh,
|
||||||
|
p4x, p4y, next.z + hh,
|
||||||
|
p2x, p2y, current.z + hh
|
||||||
|
);
|
||||||
|
for(let k=0; k<6; k++) { currentExtrudeColors.push(col.r, col.g, col.b); currentExtrudeTypes.push(tIdx); }
|
||||||
|
|
||||||
|
// Bottom face
|
||||||
|
currentExtrudePoints.push(
|
||||||
|
p1x, p1y, current.z - hh,
|
||||||
|
p2x, p2y, current.z - hh,
|
||||||
|
p3x, p3y, next.z - hh,
|
||||||
|
p2x, p2y, current.z - hh,
|
||||||
|
p4x, p4y, next.z - hh,
|
||||||
|
p3x, p3y, next.z - hh
|
||||||
|
);
|
||||||
|
for(let k=0; k<6; k++) { currentExtrudeColors.push(col.r*0.4, col.g*0.4, col.b*0.4); currentExtrudeTypes.push(tIdx); }
|
||||||
|
|
||||||
|
// Left face
|
||||||
|
currentExtrudePoints.push(
|
||||||
|
p1x, p1y, current.z - hh,
|
||||||
|
p3x, p3y, next.z - hh,
|
||||||
|
p1x, p1y, current.z + hh,
|
||||||
|
p3x, p3y, next.z - hh,
|
||||||
|
p3x, p3y, next.z + hh,
|
||||||
|
p1x, p1y, current.z + hh
|
||||||
|
);
|
||||||
|
// Fake lighting based on normal side
|
||||||
|
for(let k=0; k<6; k++) { currentExtrudeColors.push(col.r*0.6, col.g*0.6, col.b*0.6); currentExtrudeTypes.push(tIdx); }
|
||||||
|
|
||||||
|
// Right face
|
||||||
|
currentExtrudePoints.push(
|
||||||
|
p2x, p2y, current.z - hh,
|
||||||
|
p2x, p2y, current.z + hh,
|
||||||
|
p4x, p4y, next.z - hh,
|
||||||
|
p2x, p2y, current.z + hh,
|
||||||
|
p4x, p4y, next.z + hh,
|
||||||
|
p4x, p4y, next.z - hh
|
||||||
|
);
|
||||||
|
for(let k=0; k<6; k++) { currentExtrudeColors.push(col.r*0.8, col.g*0.8, col.b*0.8); currentExtrudeTypes.push(tIdx); }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Travel lines get slight vertical offset for visibility
|
||||||
|
let zOff = 0.05;
|
||||||
|
currentTravelPoints.push(current.x, current.y, current.z + zOff);
|
||||||
|
currentTravelPoints.push(next.x, next.y, next.z + zOff);
|
||||||
|
currentTravelColors.push(col.r, col.g, col.b, col.r, col.g, col.b);
|
||||||
|
currentTravelTypes.push(tIdx, tIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update E based on parsed G-code execution type
|
||||||
|
if (hasE) {
|
||||||
|
if (relativeE) current.e += eVal;
|
||||||
|
else current.e = eVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.x = next.x; current.y = next.y; current.z = next.z;
|
||||||
|
}
|
||||||
|
} else if (upperChunk.startsWith('G92')) {
|
||||||
|
let parts = chunk.split(/\s+/);
|
||||||
|
for (let p of parts) {
|
||||||
|
if (p.startsWith('E')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) current.e = v; }
|
||||||
|
if (p.startsWith('X')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) current.x = v; }
|
||||||
|
if (p.startsWith('Y')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) current.y = v; }
|
||||||
|
if (p.startsWith('Z')) { let v = parseFloat(p.substring(1)); if(!isNaN(v)) current.z = v; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flushLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMachineEnvironment() {
|
||||||
|
if (layers.length === 0) return;
|
||||||
|
|
||||||
|
let bbox = new THREE.Box3();
|
||||||
|
for (let layerGrp of layers) {
|
||||||
|
layerGrp.children.forEach(child => {
|
||||||
|
child.geometry.computeBoundingBox();
|
||||||
|
bbox.union(child.geometry.boundingBox);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The GCode coordinates for the actual print bed are from (0,0) to (W,H).
|
||||||
|
// The GCode trajectory is ALREADY offset by plater.html during slicing.
|
||||||
|
// We just need to place the grid exactly in the center of the bed: (W/2, H/2).
|
||||||
|
let gridOffsetX = (bedWidth / 2);
|
||||||
|
let gridOffsetY = (bedDepth / 2);
|
||||||
|
|
||||||
|
// Add Grid
|
||||||
|
const gridDivisions = Math.ceil(Math.max(bedWidth, bedDepth) / 10);
|
||||||
|
const gridHelper = new THREE.GridHelper(Math.max(bedWidth, bedDepth), gridDivisions, 0x444444, 0x242424);
|
||||||
|
gridHelper.rotation.x = Math.PI / 2;
|
||||||
|
gridHelper.position.set(gridOffsetX, gridOffsetY, 0);
|
||||||
|
scene.add(gridHelper);
|
||||||
|
|
||||||
|
// Add Printer Volume Outline
|
||||||
|
const boxGeo = new THREE.BoxGeometry(bedWidth, bedDepth, bedHeight);
|
||||||
|
const edges = new THREE.EdgesGeometry(boxGeo);
|
||||||
|
const boxOutline = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x444444 }));
|
||||||
|
boxOutline.position.set(gridOffsetX, gridOffsetY, bedHeight/2);
|
||||||
|
scene.add(boxOutline);
|
||||||
|
|
||||||
|
// Align Camera to target the center of the bed grid
|
||||||
|
controls.target.set(gridOffsetX, gridOffsetY, 0);
|
||||||
|
camera.position.set(gridOffsetX, gridOffsetY - (bedDepth * 1.5), bedHeight * 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUI() {
|
||||||
|
if (layers.length === 0) return;
|
||||||
|
let activeIdx = parseInt(layerSlider.value);
|
||||||
|
let intraProg = parseFloat(progressSlider.value);
|
||||||
|
|
||||||
|
layerDisplay.innerText = activeIdx + " / " + (layers.length - 1);
|
||||||
|
|
||||||
|
for (let i = 0; i < layers.length; i++) {
|
||||||
|
let layerGrp = layers[i];
|
||||||
|
if (i < activeIdx) {
|
||||||
|
layerGrp.visible = true;
|
||||||
|
layerGrp.children.forEach(child => child.geometry.setDrawRange(0, Infinity));
|
||||||
|
} else if (i === activeIdx) {
|
||||||
|
layerGrp.visible = true;
|
||||||
|
layerGrp.children.forEach(child => {
|
||||||
|
let totalVertices = child.geometry.attributes.position.count;
|
||||||
|
let elementsPerUnit = child.userData.isTravel ? 2 : 24;
|
||||||
|
let totalUnits = totalVertices / elementsPerUnit;
|
||||||
|
let drawCount = Math.floor(totalUnits * (intraProg / 100)) * elementsPerUnit;
|
||||||
|
child.geometry.setDrawRange(0, drawCount);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layerGrp.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadGCode();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
34
refer/gcode_view.qml
Normal file
34
refer/gcode_view.qml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick3D
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property real progress: 100
|
||||||
|
property real maxLayer: 10
|
||||||
|
|
||||||
|
View3D {
|
||||||
|
anchors.fill: parent
|
||||||
|
environment: SceneEnvironment {
|
||||||
|
clearColor: "#1a1a1a"
|
||||||
|
backgroundMode: SceneEnvironment.Color
|
||||||
|
}
|
||||||
|
PerspectiveCamera {
|
||||||
|
id: camera
|
||||||
|
z: 300
|
||||||
|
y: -200
|
||||||
|
eulerRotation.x: 45
|
||||||
|
}
|
||||||
|
DirectionalLight { eulerRotation.x: -45 }
|
||||||
|
|
||||||
|
// Render layers using repeated line models or a custom geometry
|
||||||
|
// Since QtQuick3D dynamic geometry from Python is complex, we render primitives for demo
|
||||||
|
Node { id: gcodeNode }
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "QtQuick3D GCode Preview\nProgress: " + progress.toFixed(1) + "%"
|
||||||
|
color: "white"
|
||||||
|
font.pixelSize: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
91
refer/gcode_view2.qml
Normal file
91
refer/gcode_view2.qml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Window
|
||||||
|
import QtQuick3D
|
||||||
|
|
||||||
|
Window {
|
||||||
|
visible: true
|
||||||
|
visibility: Window.FullScreen
|
||||||
|
width: 1280
|
||||||
|
height: 720
|
||||||
|
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
View3D {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
environment: SceneEnvironment {
|
||||||
|
clearColor: "#202020"
|
||||||
|
backgroundMode: SceneEnvironment.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
PerspectiveCamera {
|
||||||
|
id: camera
|
||||||
|
position: Qt.vector3d(0, 100, 300)
|
||||||
|
eulerRotation.x: -20
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectionalLight {
|
||||||
|
eulerRotation.x: -45
|
||||||
|
brightness: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Node {
|
||||||
|
id: rootNode
|
||||||
|
|
||||||
|
Repeater3D {
|
||||||
|
model: gcodeData
|
||||||
|
|
||||||
|
delegate: Model {
|
||||||
|
property real dx: modelData.x2 - modelData.x1
|
||||||
|
property real dy: modelData.y2 - modelData.y1
|
||||||
|
property real dz: modelData.z2 - modelData.z1
|
||||||
|
|
||||||
|
property real length: Math.sqrt(dx*dx + dy*dy + dz*dz)
|
||||||
|
|
||||||
|
source: "#Cylinder"
|
||||||
|
|
||||||
|
position: Qt.vector3d(
|
||||||
|
(modelData.x1 + modelData.x2)/2,
|
||||||
|
(modelData.z1 + modelData.z2)/2,
|
||||||
|
(modelData.y1 + modelData.y2)/2
|
||||||
|
)
|
||||||
|
|
||||||
|
scale: Qt.vector3d(0.2, length / 100.0, 0.2)
|
||||||
|
|
||||||
|
materials: [
|
||||||
|
DefaultMaterial {
|
||||||
|
diffuseColor: "#00ff88"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property real lastX
|
||||||
|
property real lastY
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
lastX = mouse.x
|
||||||
|
lastY = mouse.y
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionChanged: {
|
||||||
|
let dx = mouse.x - lastX
|
||||||
|
let dy = mouse.y - lastY
|
||||||
|
|
||||||
|
rootNode.eulerRotation.y += dx * 0.3
|
||||||
|
rootNode.eulerRotation.x += dy * 0.3
|
||||||
|
|
||||||
|
lastX = mouse.x
|
||||||
|
lastY = mouse.y
|
||||||
|
}
|
||||||
|
|
||||||
|
onWheel: {
|
||||||
|
camera.position.z += wheel.angleDelta.y * -0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
refer/qml.py
Normal file
37
refer/qml.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import sys
|
||||||
|
from PyQt6.QtGui import QGuiApplication
|
||||||
|
from PyQt6.QtQml import QQmlApplicationEngine
|
||||||
|
from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||||
|
|
||||||
|
from GcodeParser import load_gcode_vertices
|
||||||
|
|
||||||
|
|
||||||
|
class GcodeData(QObject):
|
||||||
|
verticesChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
# self._vertices = load_gcode_vertices("/home/lhye200/.octoprint/uploads/20260414135441_42bff5215c6148b8b5f4d8c4f15d5ddc.gcode")
|
||||||
|
self._vertices = load_gcode_vertices("test.gcode")
|
||||||
|
|
||||||
|
def getVertices(self):
|
||||||
|
return self._vertices
|
||||||
|
|
||||||
|
vertices = pyqtProperty('QVariantList', getVertices, notify=verticesChanged)
|
||||||
|
|
||||||
|
|
||||||
|
app = QGuiApplication(sys.argv)
|
||||||
|
|
||||||
|
engine = QQmlApplicationEngine()
|
||||||
|
|
||||||
|
model = GcodeData()
|
||||||
|
|
||||||
|
engine.rootContext().setContextProperty("gcodeData", model.vertices)
|
||||||
|
|
||||||
|
engine.load("gcode_view2.qml")
|
||||||
|
|
||||||
|
if not engine.rootObjects():
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
sys.exit(app.exec())
|
||||||
242948
refer/smp4bwithfan.gcode
Normal file
242948
refer/smp4bwithfan.gcode
Normal file
File diff suppressed because it is too large
Load Diff
268
refer/test.gcode
Normal file
268
refer/test.gcode
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
G92 E0 ;Reset Extruder
|
||||||
|
G1 Z20.0 F3000 ;Move Z Axis up
|
||||||
|
M104 S235 ;Set final nozzle temp
|
||||||
|
G1 X-13 Y20 Z3 F5000.0 ;Move to out position
|
||||||
|
M190 S70 ;Wait for bed temp to stabilize
|
||||||
|
M109 S235 ;Wait for nozzle temp to stabilize
|
||||||
|
G1 X-2.1 Y20 Z0.28 F5000.0 ;Move to start position
|
||||||
|
G1 X-2.1 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line
|
||||||
|
G1 X-2.4 Y145.0 Z0.28 F5000.0 ;Move to side a little
|
||||||
|
G1 X-2.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line
|
||||||
|
G92 E0 ;Reset Extruder
|
||||||
|
G1 E-1.0000 F1800 ;Retract a bit
|
||||||
|
G1 Z2.0 F3000 ;Move Z Axis up
|
||||||
|
G1 E0.0000 F1800
|
||||||
|
G21 ; set units to millimeters
|
||||||
|
G90 ; use absolute coordinates
|
||||||
|
M83 ; use relative distances for extrusion
|
||||||
|
M107
|
||||||
|
;LAYER_CHANGE
|
||||||
|
;Z:0.2
|
||||||
|
;HEIGHT:0.2
|
||||||
|
G1 E-1.2 F1800
|
||||||
|
G1 Z.6 F9000
|
||||||
|
G1 X52.817 Y67.413
|
||||||
|
G1 Z.2
|
||||||
|
G1 E1.2 F1800
|
||||||
|
M204 P500
|
||||||
|
;TYPE:Skirt/Brim
|
||||||
|
;WIDTH:0.5
|
||||||
|
G1 X53.827 Y66.755 E.04581
|
||||||
|
G1 X54.927 Y66.264 E.04578
|
||||||
|
G1 X56.091 Y65.95 E.04582
|
||||||
|
G1 X57.5 Y65.814 E.0538
|
||||||
|
G1 X162.711 Y65.823 E3.99868
|
||||||
|
G1 X163.87 Y65.945 E.04429
|
||||||
|
G1 X165.073 Y66.264 E.0473
|
||||||
|
G1 X165.798 Y66.564 E.02982
|
||||||
|
G1 X166.842 Y67.165 E.04578
|
||||||
|
G1 X167.78 Y67.922 E.04581
|
||||||
|
G1 X168.587 Y68.817 E.0458
|
||||||
|
G1 X169.436 Y70.202 E.06174
|
||||||
|
G1 X169.867 Y71.328 E.04582
|
||||||
|
G1 X170.05 Y72.091 E.02982
|
||||||
|
G1 X170.177 Y73.289 E.04579
|
||||||
|
G1 X170.186 Y146.5 E2.78248
|
||||||
|
G1 X170.05 Y147.909 E.0538
|
||||||
|
G1 X169.736 Y149.073 E.04582
|
||||||
|
G1 X169.436 Y149.798 E.02982
|
||||||
|
G1 X168.587 Y151.183 E.06174
|
||||||
|
G1 X168.078 Y151.78 E.02982
|
||||||
|
G1 X167.183 Y152.587 E.0458
|
||||||
|
G1 X165.798 Y153.436 E.06174
|
||||||
|
G1 X164.672 Y153.867 E.04582
|
||||||
|
G1 X163.796 Y154.074 E.03421
|
||||||
|
G1 X129.811 Y160.705 E1.316
|
||||||
|
G1 X128.826 Y160.825 E.03771
|
||||||
|
G1 X127.704 Y160.792 E.04266
|
||||||
|
G1 X56.467 Y154.112 E2.71933
|
||||||
|
G1 X54.927 Y153.736 E.06025
|
||||||
|
G1 X53.827 Y153.245 E.04578
|
||||||
|
G1 X53.158 Y152.835 E.02982
|
||||||
|
G1 X52.22 Y152.078 E.04581
|
||||||
|
G1 X51.413 Y151.183 E.0458
|
||||||
|
G1 X50.755 Y150.173 E.04581
|
||||||
|
G1 X50.264 Y149.073 E.04578
|
||||||
|
G1 X49.95 Y147.909 E.04582
|
||||||
|
G1 X49.814 Y146.5 E.0538
|
||||||
|
G1 X49.814 Y73.5 E2.77446
|
||||||
|
G1 X49.95 Y72.091 E.0538
|
||||||
|
G1 X50.133 Y71.328 E.02982
|
||||||
|
G1 X50.564 Y70.202 E.04582
|
||||||
|
G1 X51.413 Y68.817 E.06174
|
||||||
|
G1 X51.922 Y68.22 E.02982
|
||||||
|
G1 X52.772 Y67.453 E.04351
|
||||||
|
M204 P2500
|
||||||
|
M204 T2000
|
||||||
|
G1 X53.077 Y67.793 F9000
|
||||||
|
M204 P500
|
||||||
|
G1 F1800
|
||||||
|
G1 X53.114 Y67.76 E.00188
|
||||||
|
G1 X54.066 Y67.145 E.04308
|
||||||
|
G1 X55.102 Y66.686 E.04307
|
||||||
|
G1 X56.198 Y66.395 E.0431
|
||||||
|
G1 X57.5 Y66.271 E.04971
|
||||||
|
G1 X162.675 Y66.278 E3.99732
|
||||||
|
G1 X163.765 Y66.39 E.04165
|
||||||
|
G1 X164.554 Y66.575 E.0308
|
||||||
|
G1 X165.623 Y66.986 E.04353
|
||||||
|
G1 X166.603 Y67.555 E.04307
|
||||||
|
G1 X167.73 Y68.517 E.05632
|
||||||
|
G1 X168.445 Y69.397 E.04309
|
||||||
|
G1 X169.014 Y70.377 E.04307
|
||||||
|
G1 X169.314 Y71.102 E.02982
|
||||||
|
G1 X169.605 Y72.198 E.0431
|
||||||
|
G1 X169.722 Y73.325 E.04306
|
||||||
|
G1 X169.729 Y146.5 E2.78111
|
||||||
|
G1 X169.605 Y147.802 E.04971
|
||||||
|
G1 X169.422 Y148.565 E.02982
|
||||||
|
G1 X169.014 Y149.623 E.0431
|
||||||
|
G1 X168.24 Y150.886 E.0563
|
||||||
|
G1 X167.73 Y151.483 E.02984
|
||||||
|
G1 X166.886 Y152.24 E.04309
|
||||||
|
G1 X165.623 Y153.014 E.0563
|
||||||
|
G1 X164.565 Y153.422 E.0431
|
||||||
|
G1 X163.709 Y153.626 E.03344
|
||||||
|
G1 X129.723 Y160.256 E1.31603
|
||||||
|
G1 X128.837 Y160.368 E.03394
|
||||||
|
G1 X127.746 Y160.337 E.04148
|
||||||
|
G1 X56.51 Y153.657 E2.7193
|
||||||
|
G1 X55.102 Y153.314 E.05508
|
||||||
|
G1 X54.066 Y152.855 E.04307
|
||||||
|
G1 X53.114 Y152.24 E.04308
|
||||||
|
G1 X52.517 Y151.73 E.02984
|
||||||
|
G1 X51.76 Y150.886 E.04309
|
||||||
|
G1 X51.145 Y149.934 E.04308
|
||||||
|
G1 X50.686 Y148.898 E.04307
|
||||||
|
G1 X50.395 Y147.802 E.0431
|
||||||
|
G1 X50.271 Y146.5 E.04971
|
||||||
|
G1 X50.271 Y73.5 E2.77446
|
||||||
|
G1 X50.395 Y72.198 E.04971
|
||||||
|
G1 X50.578 Y71.435 E.02982
|
||||||
|
G1 X50.986 Y70.377 E.0431
|
||||||
|
G1 X51.76 Y69.114 E.0563
|
||||||
|
G1 X52.27 Y68.517 E.02984
|
||||||
|
G1 X53.033 Y67.833 E.03895
|
||||||
|
M204 P2500
|
||||||
|
G1 E-1.2
|
||||||
|
G1 Z.6 F9000
|
||||||
|
; printing object temp_edit_f69b6cf4417a43fa81e39d69b3952b58.stl id:0 copy 0
|
||||||
|
G1 X131.959 Y152.611
|
||||||
|
G1 Z.2
|
||||||
|
G1 E1.2 F1800
|
||||||
|
M204 P500
|
||||||
|
;TYPE:Support material
|
||||||
|
G1 X132.246 Y153.441 E.03338
|
||||||
|
G1 X132.344 Y154.317 E.0335
|
||||||
|
G1 X132.246 Y155.193 E.0335
|
||||||
|
G1 X131.959 Y156.022 E.03334
|
||||||
|
G1 X131.547 Y156.692 E.02989
|
||||||
|
G1 X131.146 Y157.136 E.02274
|
||||||
|
G1 X130.723 Y157.499 E.02118
|
||||||
|
G1 X129.894 Y157.918 E.0353
|
||||||
|
G1 X128.891 Y158.141 E.03905
|
||||||
|
G1 X127.954 Y158.118 E.03562
|
||||||
|
G1 X127.013 Y157.85 E.03719
|
||||||
|
G1 X126.539 Y157.579 E.02075
|
||||||
|
G1 X126.056 Y157.138 E.02486
|
||||||
|
G1 X125.499 Y156.436 E.03406
|
||||||
|
G1 X125.153 Y155.827 E.02662
|
||||||
|
G1 X124.929 Y155.168 E.02645
|
||||||
|
G1 X124.834 Y154.278 E.03402
|
||||||
|
G1 X124.963 Y153.42 E.03298
|
||||||
|
G1 X125.287 Y152.609 E.03319
|
||||||
|
G1 X125.63 Y152.1 E.02333
|
||||||
|
G1 X131.643 Y152.1 E.22853
|
||||||
|
G1 X131.923 Y152.553 E.02024
|
||||||
|
M204 P2500
|
||||||
|
G1 X131.838 Y155.742 F9000
|
||||||
|
M204 P500
|
||||||
|
G1 F1800
|
||||||
|
G1 X131.838 Y153.52 E.08445
|
||||||
|
G1 X131.585 Y152.789 E.0294
|
||||||
|
G1 X131.414 Y152.511 E.0124
|
||||||
|
G1 X131.33 Y152.511 E.00319
|
||||||
|
G1 X131.33 Y156.259 E.14245
|
||||||
|
G1 X130.822 Y156.872 E.03026
|
||||||
|
G1 X130.822 Y152.511 E.16575
|
||||||
|
G1 X130.315 Y152.511 E.01927
|
||||||
|
G1 X130.315 Y157.244 E.17988
|
||||||
|
G1 X129.807 Y157.501 E.02164
|
||||||
|
G1 X129.807 Y152.511 E.18965
|
||||||
|
G1 X129.299 Y152.511 E.01931
|
||||||
|
G1 X129.299 Y157.644 E.19509
|
||||||
|
G1 X128.791 Y157.727 E.01956
|
||||||
|
G1 X128.791 Y152.511 E.19824
|
||||||
|
G1 X128.283 Y152.511 E.01931
|
||||||
|
G1 X128.283 Y157.714 E.19775
|
||||||
|
G1 X127.775 Y157.639 E.01952
|
||||||
|
G1 X127.775 Y152.511 E.1949
|
||||||
|
G1 X127.267 Y152.511 E.01931
|
||||||
|
G1 X127.267 Y157.494 E.18939
|
||||||
|
G1 X127.173 Y157.468 E.00371
|
||||||
|
G1 X126.76 Y157.224 E.01823
|
||||||
|
G1 X126.76 Y152.511 E.17912
|
||||||
|
G1 X126.252 Y152.511 E.01931
|
||||||
|
G1 X126.252 Y156.695 E.15902
|
||||||
|
G1 X125.744 Y156.034 E.03168
|
||||||
|
G1 X125.744 Y152.306 E.14169
|
||||||
|
M204 P2500
|
||||||
|
G1 E-1.2
|
||||||
|
G1 Z.6 F9000
|
||||||
|
G1 X140.305 Y87.116
|
||||||
|
G1 Z.2
|
||||||
|
G1 E1.2 F1800
|
||||||
|
M204 P500
|
||||||
|
;TYPE:Perimeter
|
||||||
|
;WIDTH:0.499999
|
||||||
|
G1 X140.124 Y87.275 E.00916
|
||||||
|
G1 X139.922 Y87.388 E.0088
|
||||||
|
G1 X139.444 Y87.556 E.01926
|
||||||
|
G1 X138.76 Y87.587 E.02602
|
||||||
|
G1 X138.531 Y87.548 E.00883
|
||||||
|
G1 X138.055 Y87.379 E.0192
|
||||||
|
G1 X137.502 Y86.973 E.02607
|
||||||
|
G1 X137.349 Y86.799 E.00881
|
||||||
|
G1 X137.085 Y86.371 E.01911
|
||||||
|
G1 X137.01 Y86.175 E.00798
|
||||||
|
G1 X136.904 Y85.68 E.01924
|
||||||
|
G1 X136.899 Y85.473 E.00787
|
||||||
|
G1 X136.967 Y84.955 E.01986
|
||||||
|
G1 X137.244 Y84.346 E.02543
|
||||||
|
G1 X137.382 Y84.159 E.00883
|
||||||
|
G1 X137.771 Y83.79 E.02038
|
||||||
|
G1 X138.359 Y83.499 E.02493
|
||||||
|
G1 X138.584 Y83.441 E.00883
|
||||||
|
G1 X139.121 Y83.397 E.02048
|
||||||
|
G1 X139.587 Y83.478 E.01798
|
||||||
|
G1 X140.079 Y83.697 E.02047
|
||||||
|
G1 X140.271 Y83.827 E.00881
|
||||||
|
G1 X140.66 Y84.204 E.02059
|
||||||
|
G1 X140.972 Y84.775 E.02473
|
||||||
|
G1 X141.04 Y84.996 E.00879
|
||||||
|
G1 X141.105 Y85.513 E.0198
|
||||||
|
G1 X140.99 Y86.175 E.02554
|
||||||
|
G1 X140.903 Y86.39 E.00881
|
||||||
|
G1 X140.632 Y86.828 E.01958
|
||||||
|
G1 X140.35 Y87.076 E.01427
|
||||||
|
M204 P2500
|
||||||
|
G1 X140.021 Y86.764 F9000
|
||||||
|
M204 P500
|
||||||
|
;TYPE:External perimeter
|
||||||
|
G1 F1800
|
||||||
|
G1 X139.837 Y86.919 E.00914
|
||||||
|
G1 X139.316 Y87.117 E.02118
|
||||||
|
G1 X138.76 Y87.13 E.02114
|
||||||
|
G1 X138.23 Y86.957 E.02119
|
||||||
|
G1 X137.789 Y86.617 E.02116
|
||||||
|
G1 X137.486 Y86.15 E.02116
|
||||||
|
G1 X137.356 Y85.608 E.02118
|
||||||
|
G1 X137.414 Y85.055 E.02113
|
||||||
|
G1 X137.653 Y84.551 E.0212
|
||||||
|
G1 X138.046 Y84.157 E.02115
|
||||||
|
G1 X138.548 Y83.916 E.02116
|
||||||
|
G1 X139.101 Y83.855 E.02114
|
||||||
|
G1 X139.452 Y83.916 E.01354
|
||||||
|
G1 X139.908 Y84.123 E.01903
|
||||||
|
G1 X140.301 Y84.489 E.02041
|
||||||
|
G1 X140.564 Y84.981 E.0212
|
||||||
|
G1 X140.647 Y85.531 E.02114
|
||||||
|
G1 X140.543 Y86.078 E.02116
|
||||||
|
G1 X140.262 Y86.559 E.02117
|
||||||
|
G1 X140.066 Y86.725 E.00976
|
||||||
|
M204 P2500
|
||||||
|
G1 E-1.2
|
||||||
|
G1 Z.6 F9000
|
||||||
|
G1 X162.311 Y71.773
|
||||||
|
G1 Z.2
|
||||||
|
G1 E1.2 F1800
|
||||||
|
M204 P500
|
||||||
|
G1 X162.659 Y71.282 E.02287
|
||||||
|
G1 X162.97 Y71.031 E.01519
|
||||||
|
G1 X163.488 Y70.797 E.0216
|
||||||
|
G1 X164.024 Y70.726 E.02055
|
||||||
|
G1 X164.494 Y70.79 E.01803
|
||||||
|
G1 X164.817 Y70.912 E.01312
|
||||||
|
G1 X165.285 Y71.228 E.02146
|
||||||
|
G1 X165.677 Y71.736 E.02439
|
||||||
|
G1 X165.877 Y72.346 E.0244
|
||||||
Reference in New Issue
Block a user