HTML Olarak çalışan Versiyonu
This commit is contained in:
539
index.html
Normal file
539
index.html
Normal file
@@ -0,0 +1,539 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>3D Yazıcı Kalibrasyon</title>
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
||||
|
||||
/* Utility Classes */
|
||||
.min-h-screen { min-height: 100vh; }
|
||||
.bg-gradient { background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%); }
|
||||
.text-white { color: white; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.max-w-6xl { max-width: 72rem; }
|
||||
.mx-auto { margin-left: auto; margin-right: auto; }
|
||||
.text-center { text-align: center; }
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
.mb-8 { margin-bottom: 2rem; }
|
||||
.mt-1 { margin-top: 0.25rem; }
|
||||
.mt-2 { margin-top: 0.5rem; }
|
||||
.mt-4 { margin-top: 1rem; }
|
||||
.mt-6 { margin-top: 1.5rem; }
|
||||
.text-4xl { font-size: 2.25rem; line-height: 2.5rem; }
|
||||
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
|
||||
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
|
||||
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
|
||||
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
|
||||
.text-xs { font-size: 0.75rem; line-height: 1rem; }
|
||||
.font-bold { font-weight: 700; }
|
||||
.font-semibold { font-weight: 600; }
|
||||
.bg-slate-800 { background-color: rgba(30, 41, 59, 0.5); }
|
||||
.bg-slate-700 { background-color: #334155; }
|
||||
.bg-slate-900 { background-color: #0f172a; }
|
||||
.text-slate-400 { color: #94a3b8; }
|
||||
.text-slate-500 { color: #64748b; }
|
||||
.text-slate-300 { color: #cbd5e1; }
|
||||
.text-cyan-400 { color: #22d3ee; }
|
||||
.text-cyan-300 { color: #67e8f9; }
|
||||
.text-blue-400 { color: #60a5fa; }
|
||||
.text-blue-300 { color: #93c5fd; }
|
||||
.text-green-400 { color: #4ade80; }
|
||||
.text-yellow-400 { color: #facc15; }
|
||||
.text-yellow-300 { color: #fde047; }
|
||||
.border { border-width: 1px; }
|
||||
.border-slate-700 { border-color: #334155; }
|
||||
.border-slate-600 { border-color: #475569; }
|
||||
.rounded { border-radius: 0.25rem; }
|
||||
.rounded-lg { border-radius: 0.5rem; }
|
||||
.px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
|
||||
.px-4 { padding-left: 1rem; padding-right: 1rem; }
|
||||
.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
|
||||
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
|
||||
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
.w-full { width: 100%; }
|
||||
.flex { display: flex; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
.grid { display: grid; }
|
||||
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
|
||||
.overflow-x-auto { overflow-x: auto; }
|
||||
.whitespace-nowrap { white-space: nowrap; }
|
||||
.shadow-lg { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); }
|
||||
.cursor-pointer { cursor: pointer; }
|
||||
.transition-all { transition: all 0.3s; }
|
||||
.backdrop-blur { backdrop-filter: blur(8px); }
|
||||
.block { display: block; }
|
||||
.space-y-1 > * + * { margin-top: 0.25rem; }
|
||||
.space-y-3 > * + * { margin-top: 0.75rem; }
|
||||
.list-decimal { list-style-type: decimal; }
|
||||
.list-inside { list-style-position: inside; }
|
||||
.ml-4 { margin-left: 1rem; }
|
||||
.overflow-x-auto::-webkit-scrollbar { height: 8px; }
|
||||
.overflow-x-auto::-webkit-scrollbar-track { background: #1e293b; }
|
||||
.overflow-x-auto::-webkit-scrollbar-thumb { background: #334155; border-radius: 4px; }
|
||||
|
||||
.bg-gradient-cyan { background: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%); }
|
||||
.bg-cyan-10 { background-color: rgba(34, 211, 238, 0.1); }
|
||||
.border-cyan-30 { border-color: rgba(34, 211, 238, 0.3); }
|
||||
.bg-blue-10 { background-color: rgba(96, 165, 250, 0.1); }
|
||||
.border-blue-30 { border-color: rgba(96, 165, 250, 0.3); }
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
color: white;
|
||||
}
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 20px 25px -5px rgba(6, 182, 212, 0.3);
|
||||
}
|
||||
|
||||
.tab-active {
|
||||
background: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tab-inactive {
|
||||
background-color: rgba(30, 41, 59, 0.5);
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.tab-inactive:hover {
|
||||
background-color: #334155;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #0f172a;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.md\\:flex-row { flex-direction: row; }
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.flex-col { flex-direction: column; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script>
|
||||
const { useState, createElement: e } = React;
|
||||
|
||||
// Icon components (simplified versions)
|
||||
const Download = () => e('svg', { width: 20, height: 20, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },
|
||||
e('path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3' }));
|
||||
|
||||
const Settings = () => e('svg', { width: 20, height: 20, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },
|
||||
e('circle', { cx: 12, cy: 12, r: 3 }),
|
||||
e('path', { d: 'M12 1v6m0 6v6m8.66-10a9 9 0 1 1-17.32 0' }));
|
||||
|
||||
const Move = () => e('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },
|
||||
e('polyline', { points: '5 9 2 12 5 15' }),
|
||||
e('polyline', { points: '9 5 12 2 15 5' }),
|
||||
e('polyline', { points: '15 19 12 22 9 19' }),
|
||||
e('polyline', { points: '19 9 22 12 19 15' }),
|
||||
e('line', { x1: 2, y1: 12, x2: 22, y2: 12 }),
|
||||
e('line', { x1: 12, y1: 2, x2: 12, y2: 22 }));
|
||||
|
||||
const Box = () => e('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },
|
||||
e('path', { d: 'M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z' }),
|
||||
e('polyline', { points: '3.27 6.96 12 12.01 20.73 6.96' }),
|
||||
e('line', { x1: 12, y1: 22.08, x2: 12, y2: 12 }));
|
||||
|
||||
const Maximize2 = () => e('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },
|
||||
e('polyline', { points: '15 3 21 3 21 9' }),
|
||||
e('polyline', { points: '9 21 3 21 3 15' }),
|
||||
e('line', { x1: 21, y1: 3, x2: 14, y2: 10 }),
|
||||
e('line', { x1: 3, y1: 21, x2: 10, y2: 14 }));
|
||||
|
||||
const Droplet = () => e('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },
|
||||
e('path', { d: 'M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z' }));
|
||||
|
||||
function PrinterCalibration() {
|
||||
const [activeTab, setActiveTab] = useState('x-axis');
|
||||
const [settings, setSettings] = useState({
|
||||
bedTemp: 60,
|
||||
nozzleTemp: 200,
|
||||
nozzleDiameter: 0.4
|
||||
});
|
||||
|
||||
const [xAxisConfig, setXAxisConfig] = useState({ distance: 100, steps: 80, measured: 0 });
|
||||
const [yAxisConfig, setYAxisConfig] = useState({ distance: 100, steps: 80, measured: 0 });
|
||||
const [zAxisConfig, setZAxisConfig] = useState({ distance: 10, steps: 400, measured: 0 });
|
||||
const [extruderConfig, setExtruderConfig] = useState({ distance: 100, steps: 93, measured: 0 });
|
||||
const [skewConfig, setSkewConfig] = useState({ ac: 141.4, bd: 141.4, ad: 141.4 });
|
||||
const [flowConfig, setFlowConfig] = useState({
|
||||
wallThickness: 0.4,
|
||||
measuredWall: 0,
|
||||
currentFlow: 100,
|
||||
layerHeight: 0.2,
|
||||
lineWidth: 0.4
|
||||
});
|
||||
|
||||
const calculateSteps = (config) => {
|
||||
if (config.measured > 0) {
|
||||
return ((config.distance / config.measured) * config.steps).toFixed(2);
|
||||
}
|
||||
return config.steps;
|
||||
};
|
||||
|
||||
const calculateSkew = () => {
|
||||
const ac = parseFloat(skewConfig.ac);
|
||||
const bd = parseFloat(skewConfig.bd);
|
||||
const ad = parseFloat(skewConfig.ad);
|
||||
|
||||
if (ac > 0 && bd > 0 && ad > 0) {
|
||||
return {
|
||||
xy: (Math.atan2(ac - 141.4, 100) * (180 / Math.PI)).toFixed(4),
|
||||
xz: (Math.atan2(bd - 141.4, 100) * (180 / Math.PI)).toFixed(4),
|
||||
yz: (Math.atan2(ad - 141.4, 100) * (180 / Math.PI)).toFixed(4)
|
||||
};
|
||||
}
|
||||
return { xy: '0.0000', xz: '0.0000', yz: '0.0000' };
|
||||
};
|
||||
|
||||
const calculateFlow = () => {
|
||||
if (flowConfig.measuredWall > 0) {
|
||||
const newFlow = (flowConfig.wallThickness / flowConfig.measuredWall) * flowConfig.currentFlow;
|
||||
return newFlow.toFixed(2);
|
||||
}
|
||||
return flowConfig.currentFlow;
|
||||
};
|
||||
|
||||
const generateGCode = () => {
|
||||
let gcode = '; 3D Yazıcı Kalibrasyon G-Code\n';
|
||||
gcode += '; Oluşturma Tarihi: ' + new Date().toLocaleString('tr-TR') + '\n\n';
|
||||
gcode += `M140 S${settings.bedTemp}\nM104 S${settings.nozzleTemp}\n`;
|
||||
gcode += `M190 S${settings.bedTemp}\nM109 S${settings.nozzleTemp}\n\n`;
|
||||
|
||||
const configs = { 'x-axis': xAxisConfig, 'y-axis': yAxisConfig, 'z-axis': zAxisConfig, 'extruder': extruderConfig };
|
||||
const axes = { 'x-axis': 'X', 'y-axis': 'Y', 'z-axis': 'Z', 'extruder': 'E' };
|
||||
|
||||
if (activeTab === 'skew') {
|
||||
const skew = calculateSkew();
|
||||
gcode += `M852 I${skew.xy} J${skew.xz} K${skew.yz}\nM500\n`;
|
||||
} else if (activeTab === 'flow') {
|
||||
gcode += '; Akış Kalibrasyonu - Tek Duvarlı Küp\n';
|
||||
gcode += 'G28\nG90\nM82\n';
|
||||
gcode += `M221 S${flowConfig.currentFlow} ; Mevcut akış oranı\n`;
|
||||
gcode += 'G92 E0\nG1 Z0.2 F300\n\n';
|
||||
|
||||
// 20x20mm tek duvarlı küp (10 katman)
|
||||
const size = 20;
|
||||
const wallThickness = flowConfig.wallThickness;
|
||||
const layerHeight = flowConfig.layerHeight;
|
||||
const feedrate = 1800;
|
||||
|
||||
for (let layer = 0; layer < 10; layer++) {
|
||||
const z = (layer + 1) * layerHeight;
|
||||
gcode += `; Katman ${layer + 1}\n`;
|
||||
gcode += `G1 Z${z.toFixed(2)} F300\n`;
|
||||
|
||||
// Kare çiz (tek duvar)
|
||||
const startX = 100;
|
||||
const startY = 100;
|
||||
|
||||
gcode += `G1 X${startX} Y${startY} F${feedrate}\n`;
|
||||
gcode += `G1 X${startX + size} Y${startY} E${((layer * 4 + 1) * 0.5).toFixed(4)} F${feedrate}\n`;
|
||||
gcode += `G1 X${startX + size} Y${startY + size} E${((layer * 4 + 2) * 0.5).toFixed(4)} F${feedrate}\n`;
|
||||
gcode += `G1 X${startX} Y${startY + size} E${((layer * 4 + 3) * 0.5).toFixed(4)} F${feedrate}\n`;
|
||||
gcode += `G1 X${startX} Y${startY} E${((layer * 4 + 4) * 0.5).toFixed(4)} F${feedrate}\n`;
|
||||
}
|
||||
|
||||
gcode += `\n; Hedef duvar kalınlığı: ${wallThickness}mm\n`;
|
||||
gcode += `; Ölçülen duvar kalınlığı: ${flowConfig.measuredWall || '?'}mm\n`;
|
||||
gcode += `; Hesaplanan akış: ${calculateFlow()}%\n`;
|
||||
gcode += `; Slicer'da uygulamak için Flow Rate: ${calculateFlow()}%\n`;
|
||||
} else {
|
||||
const config = configs[activeTab];
|
||||
const axis = axes[activeTab];
|
||||
gcode += 'G28\nG90\n';
|
||||
if (activeTab === 'extruder') {
|
||||
gcode += `M82\nG92 E0\nG1 E${config.distance} F100\n`;
|
||||
} else {
|
||||
if (activeTab !== 'z-axis') gcode += 'G1 Z10 F3000\n';
|
||||
gcode += `G1 ${axis}${config.distance} F${activeTab === 'z-axis' ? '300' : '3000'}\n`;
|
||||
}
|
||||
gcode += `M400\n; M92 ${axis}${calculateSteps(config)}\n; M500\n`;
|
||||
}
|
||||
|
||||
gcode += '\nM104 S0\nM140 S0\nG28 X Y\nM84\n';
|
||||
return gcode;
|
||||
};
|
||||
|
||||
const downloadGCode = () => {
|
||||
const blob = new Blob([generateGCode()], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `calibration_${activeTab}_${Date.now()}.gcode`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{ id: 'x-axis', name: 'X Ekseni', icon: Move },
|
||||
{ id: 'y-axis', name: 'Y Ekseni', icon: Move },
|
||||
{ id: 'z-axis', name: 'Z Ekseni', icon: Move },
|
||||
{ id: 'extruder', name: 'Extruder', icon: Box },
|
||||
{ id: 'flow', name: 'Akış', icon: Droplet },
|
||||
{ id: 'skew', name: 'Çarpıklık', icon: Maximize2 }
|
||||
];
|
||||
|
||||
const renderAxisCalibration = (config, setConfig, axis) => {
|
||||
return e('div', null,
|
||||
e('h3', { className: 'text-2xl font-semibold mb-4 text-cyan-400' }, `${axis} Ekseni Kalibrasyonu`),
|
||||
e('div', { className: 'grid grid-cols-1 md:grid-cols-3 gap-4 mb-6' },
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Hedef Mesafe (mm)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
value: config.distance,
|
||||
onChange: (ev) => setConfig({...config, distance: parseInt(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
})
|
||||
),
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Mevcut Steps/mm'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
step: '0.01',
|
||||
value: config.steps,
|
||||
onChange: (ev) => setConfig({...config, steps: parseFloat(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
})
|
||||
),
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Ölçülen Mesafe (mm)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
step: '0.01',
|
||||
value: config.measured,
|
||||
onChange: (ev) => setConfig({...config, measured: parseFloat(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2',
|
||||
placeholder: 'Test sonrası giriniz'
|
||||
})
|
||||
)
|
||||
),
|
||||
config.measured > 0 && e('div', { className: 'bg-cyan-10 border border-cyan-30 rounded-lg p-4' },
|
||||
e('h4', { className: 'font-semibold text-cyan-400 mb-2' }, 'Hesaplanan Değer:'),
|
||||
e('p', { className: 'text-2xl font-bold text-cyan-300' }, `${calculateSteps(config)} steps/mm`)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return e('div', { className: 'min-h-screen bg-gradient text-white p-4' },
|
||||
e('div', { className: 'max-w-6xl mx-auto' },
|
||||
e('div', { className: 'text-center mb-8' },
|
||||
e('h1', { className: 'text-4xl font-bold mb-2', style: { background: 'linear-gradient(135deg, #22d3ee 0%, #3b82f6 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' } },
|
||||
'3D Yazıcı Kalibrasyon'
|
||||
),
|
||||
e('p', { className: 'text-slate-400' }, 'Profesyonel Kalibrasyon ve G-code Üretici')
|
||||
),
|
||||
e('div', { className: 'bg-slate-800 backdrop-blur rounded-lg p-6 mb-6 border border-slate-700' },
|
||||
e('div', { className: 'flex items-center gap-2 mb-4' },
|
||||
e(Settings),
|
||||
e('h2', { className: 'text-xl font-semibold' }, 'Genel Ayarlar')
|
||||
),
|
||||
e('div', { className: 'grid grid-cols-1 md:grid-cols-3 gap-4' },
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Tabla Sıcaklığı (°C)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
value: settings.bedTemp,
|
||||
onChange: (ev) => setSettings({...settings, bedTemp: parseInt(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
})
|
||||
),
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Nozzle Sıcaklığı (°C)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
value: settings.nozzleTemp,
|
||||
onChange: (ev) => setSettings({...settings, nozzleTemp: parseInt(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
})
|
||||
),
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Nozzle Çapı (mm)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
step: '0.1',
|
||||
value: settings.nozzleDiameter,
|
||||
onChange: (ev) => setSettings({...settings, nozzleDiameter: parseFloat(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
e('div', { className: 'flex gap-2 mb-6 overflow-x-auto' },
|
||||
tabs.map(tab => e('button', {
|
||||
key: tab.id,
|
||||
onClick: () => setActiveTab(tab.id),
|
||||
className: `flex items-center gap-2 px-4 py-3 rounded-lg transition-all whitespace-nowrap ${activeTab === tab.id ? 'tab-active shadow-lg' : 'tab-inactive'}`
|
||||
},
|
||||
e(tab.icon),
|
||||
tab.name
|
||||
))
|
||||
),
|
||||
e('div', { className: 'bg-slate-800 backdrop-blur rounded-lg p-6 border border-slate-700 mb-6' },
|
||||
activeTab === 'x-axis' && renderAxisCalibration(xAxisConfig, setXAxisConfig, 'X'),
|
||||
activeTab === 'y-axis' && renderAxisCalibration(yAxisConfig, setYAxisConfig, 'Y'),
|
||||
activeTab === 'z-axis' && renderAxisCalibration(zAxisConfig, setZAxisConfig, 'Z'),
|
||||
activeTab === 'extruder' && renderAxisCalibration(extruderConfig, setExtruderConfig, 'Extruder'),
|
||||
activeTab === 'flow' && e('div', null,
|
||||
e('h3', { className: 'text-2xl font-semibold mb-4 text-cyan-400' }, 'Akış Kalibrasyonu'),
|
||||
e('p', { className: 'text-slate-400 mb-6' },
|
||||
'Tek duvarlı küp bastırın ve duvar kalınlığını ölçerek akış oranını optimize edin.'
|
||||
),
|
||||
e('div', { className: 'grid grid-cols-1 md:grid-cols-3 gap-4 mb-6' },
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Hedef Duvar Kalınlığı (mm)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
step: '0.01',
|
||||
value: flowConfig.wallThickness,
|
||||
onChange: (ev) => setFlowConfig({...flowConfig, wallThickness: parseFloat(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
}),
|
||||
e('p', { className: 'text-xs text-slate-500 mt-1' }, 'Genellikle nozzle çapı ile aynı')
|
||||
),
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Mevcut Akış Oranı (%)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
value: flowConfig.currentFlow,
|
||||
onChange: (ev) => setFlowConfig({...flowConfig, currentFlow: parseInt(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
}),
|
||||
e('p', { className: 'text-xs text-slate-500 mt-1' }, 'Slicer\'daki mevcut değer')
|
||||
),
|
||||
e('div', null,
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, 'Ölçülen Duvar Kalınlığı (mm)'),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
step: '0.01',
|
||||
value: flowConfig.measuredWall,
|
||||
onChange: (ev) => setFlowConfig({...flowConfig, measuredWall: parseFloat(ev.target.value)}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2',
|
||||
placeholder: 'Baskı sonrası ölçün'
|
||||
}),
|
||||
e('p', { className: 'text-xs text-slate-500 mt-1' }, 'Kumpas ile ölçüm yapın')
|
||||
)
|
||||
),
|
||||
e('div', { className: 'bg-blue-10 border border-blue-30 rounded-lg p-4 mb-6' },
|
||||
e('h4', { className: 'font-semibold text-blue-400 mb-2' }, '📐 Ölçüm İpuçları:'),
|
||||
e('ul', { className: 'text-sm text-slate-300 space-y-1 ml-4 list-inside' },
|
||||
e('li', null, '• Küpün ortasından ölçüm yapın (üst ve alt köşelerden kaçının)'),
|
||||
e('li', null, '• Farklı noktalardan 3-5 ölçüm alın ve ortalamasını kullanın'),
|
||||
e('li', null, '• Baskı tamamen soğuduktan sonra ölçün'),
|
||||
e('li', null, '• Dijital kumpas kullanarak 0.01mm hassasiyetle ölçün')
|
||||
)
|
||||
),
|
||||
flowConfig.measuredWall > 0 && e('div', { className: 'bg-cyan-10 border border-cyan-30 rounded-lg p-4' },
|
||||
e('h4', { className: 'font-semibold text-cyan-400 mb-2' }, 'Hesaplanan Değerler:'),
|
||||
e('div', { className: 'grid grid-cols-1 md:grid-cols-2 gap-4' },
|
||||
e('div', null,
|
||||
e('p', { className: 'text-sm text-slate-400' }, 'Yeni Akış Oranı'),
|
||||
e('p', { className: 'text-2xl font-bold text-cyan-300' }, `${calculateFlow()}%`)
|
||||
),
|
||||
e('div', null,
|
||||
e('p', { className: 'text-sm text-slate-400' }, 'Fark'),
|
||||
e('p', { className: 'text-xl font-bold text-cyan-300' },
|
||||
`${(calculateFlow() - flowConfig.currentFlow).toFixed(2)}%`
|
||||
)
|
||||
)
|
||||
),
|
||||
e('p', { className: 'text-sm text-slate-400 mt-4' },
|
||||
'Slicer\'ınızda Flow Rate ayarını ',
|
||||
e('code', { className: 'text-cyan-400' }, `${calculateFlow()}%`),
|
||||
' olarak değiştirin.'
|
||||
),
|
||||
Math.abs(calculateFlow() - flowConfig.currentFlow) > 10 &&
|
||||
e('p', { className: 'text-sm text-yellow-400 mt-2' },
|
||||
'⚠️ Uyarı: %10\'dan fazla fark var. Extruder steps/mm kalibrasyonunu kontrol edin.'
|
||||
)
|
||||
)
|
||||
),
|
||||
activeTab === 'skew' && e('div', null,
|
||||
e('h3', { className: 'text-2xl font-semibold mb-4 text-cyan-400' }, 'Çarpıklık Kalibrasyonu'),
|
||||
e('div', { className: 'grid grid-cols-1 md:grid-cols-3 gap-4 mb-6' },
|
||||
['ac', 'bd', 'ad'].map((key, idx) => e('div', { key },
|
||||
e('label', { className: 'block text-sm text-slate-400 mb-1' }, ['AC Köşegeni (mm)', 'BD Köşegeni (mm)', 'AD Köşegeni (mm)'][idx]),
|
||||
e('input', {
|
||||
type: 'number',
|
||||
step: '0.01',
|
||||
value: skewConfig[key],
|
||||
onChange: (ev) => setSkewConfig({...skewConfig, [key]: ev.target.value}),
|
||||
className: 'w-full bg-slate-700 border border-slate-600 rounded px-3 py-2'
|
||||
})
|
||||
))
|
||||
),
|
||||
e('div', { className: 'bg-cyan-10 border border-cyan-30 rounded-lg p-4' },
|
||||
e('h4', { className: 'font-semibold text-cyan-400 mb-3' }, 'Hesaplanan Çarpıklık Değerleri:'),
|
||||
e('div', { className: 'grid grid-cols-1 md:grid-cols-3 gap-4' },
|
||||
['xy', 'xz', 'yz'].map(k => e('div', { key: k },
|
||||
e('p', { className: 'text-sm text-slate-400' }, `${k.toUpperCase()} Çarpıklığı`),
|
||||
e('p', { className: 'text-xl font-bold text-cyan-300' }, `${calculateSkew()[k]}°`)
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
e('div', { className: 'bg-slate-800 backdrop-blur rounded-lg p-6 border border-slate-700' },
|
||||
e('div', { className: 'flex flex-col md:flex-row items-center justify-between gap-4' },
|
||||
e('div', null,
|
||||
e('h3', { className: 'text-lg font-semibold mb-1' }, 'G-code Oluştur'),
|
||||
e('p', { className: 'text-sm text-slate-400' }, 'Seçili kalibrasyon için G-code dosyası indir')
|
||||
),
|
||||
e('button', {
|
||||
onClick: downloadGCode,
|
||||
className: 'flex items-center gap-2 bg-gradient-cyan px-6 py-3 rounded-lg font-semibold transition-all shadow-lg cursor-pointer'
|
||||
},
|
||||
e(Download),
|
||||
'G-code İndir'
|
||||
)
|
||||
),
|
||||
e('div', { className: 'mt-6' },
|
||||
e('h4', { className: 'text-sm font-semibold text-slate-400 mb-2' }, 'G-code Önizleme:'),
|
||||
e('div', { className: 'bg-slate-900 rounded-lg p-4 overflow-x-auto' },
|
||||
e('pre', { className: 'text-xs text-green-400' }, generateGCode())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(e(PrinterCalibration));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user