672 lines
20 KiB
HTML
672 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<style>
|
|
html, body {
|
|
margin: 0px;
|
|
padding: 0px;
|
|
background: #f5f5f5;
|
|
font-family: sans-serif;
|
|
}
|
|
|
|
.container {
|
|
background: #fff;
|
|
padding: 20px;
|
|
padding-bottom: 10px;
|
|
border: 1px solid #ccc;
|
|
max-width:fit-content(1000px);
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.tip-panel {
|
|
background: #eeeeee;
|
|
padding: 10px;
|
|
margin-bottom: 10px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.scroll-container {
|
|
max-width: 100%;
|
|
max-height: 500px;
|
|
overflow: auto;
|
|
border: 1px solid #ccc;
|
|
position: relative;
|
|
margin: 0px;
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
}
|
|
|
|
th, td {
|
|
border: 1px solid #ccc;
|
|
text-align: center;
|
|
padding: 2px;
|
|
position: relative;
|
|
width: 40px;
|
|
height: 25px;
|
|
}
|
|
|
|
thead th {
|
|
position: sticky;
|
|
top: 0;
|
|
background: #eee;
|
|
z-index: 10;
|
|
}
|
|
|
|
tbody th {
|
|
position: sticky;
|
|
left: 0;
|
|
background: #eee;
|
|
z-index: 9;
|
|
}
|
|
|
|
tbody tr:nth-child(even) {
|
|
background: #ffffff;
|
|
}
|
|
|
|
tbody tr:nth-child(odd) {
|
|
background: #eeeeee;
|
|
}
|
|
|
|
tbody td:first-child,
|
|
thead th:first-child {
|
|
position: sticky;
|
|
top: 0;
|
|
left: 0;
|
|
background: #eee;
|
|
z-index: 11;
|
|
}
|
|
|
|
.icon-button {
|
|
display: inline-block;
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 1px solid #999;
|
|
text-align: center;
|
|
line-height: 15px;
|
|
color: #fff;
|
|
font-weight: bold;
|
|
font-size: 12px;
|
|
border-radius: 4px;
|
|
cursor: default;
|
|
user-select: none;
|
|
padding: 1px;
|
|
}
|
|
|
|
input[type="number"].table-input {
|
|
width: 30px;
|
|
height: 20px;
|
|
text-align: center;
|
|
-moz-appearance: textfield;
|
|
appearance: textfield;
|
|
border: none;
|
|
background-color: inherit;
|
|
}
|
|
|
|
input[type="number"].table-input::-webkit-inner-spin-button,
|
|
input[type="number"].table-input::-webkit-outer-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
|
|
input[type="number"].multiplier-input {
|
|
width: 60px;
|
|
}
|
|
body.dark-mode input[type="number"].multiplier-input {
|
|
width: 60px;
|
|
background-color: #4c4c55;
|
|
color: white;
|
|
}
|
|
|
|
.warning-text {
|
|
color: red;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.normal-text {
|
|
color: black;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.icon-gap {
|
|
width: 10px;
|
|
display: inline-block;
|
|
}
|
|
|
|
.btn {
|
|
display: inline-block;
|
|
padding: 6px 9px;
|
|
border: 2px solid transparent;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.btn-ok {
|
|
color: #fff;
|
|
background-color: #21a452;
|
|
}
|
|
.btn-ok:hover {
|
|
background-color: #3dcb73;
|
|
}
|
|
|
|
.btn-cancel {
|
|
color: black;
|
|
background-color: white;
|
|
border: 1px solid black;
|
|
}
|
|
.btn-cancel:hover {
|
|
background-color: #eeeeee;
|
|
}
|
|
|
|
body.dark-mode button.btn-cancel {
|
|
background-color: #2d2d31;
|
|
color: #e0e0e0;
|
|
border: 1px solid #e0e0e0;
|
|
}
|
|
|
|
select {
|
|
padding: 6px 9px;
|
|
border: 1px solid #dbdbdb;
|
|
border-radius: 3px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
appearance: auto;
|
|
background-color: white;
|
|
color: black;
|
|
}
|
|
|
|
select option {
|
|
background-color: white;
|
|
color: black;
|
|
}
|
|
|
|
select:hover,
|
|
select:focus {
|
|
outline: none;
|
|
border-color: #00ae42;
|
|
background-color: #edfaf2;
|
|
}
|
|
|
|
.button-container {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
margin: 10px;
|
|
}
|
|
|
|
/* 暗色模式样式 */
|
|
body.dark-mode {
|
|
background-color: #2d2d31;
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
body.dark-mode .container {
|
|
background: inherit;
|
|
border-color: #2d2d31;
|
|
}
|
|
|
|
body.dark-mode .tip-panel {
|
|
background: #4c4c55;
|
|
color: #ccc;
|
|
}
|
|
|
|
body.dark-mode .scroll-container {
|
|
border-color: #4c4c55;
|
|
}
|
|
|
|
body.dark-mode table {
|
|
background: inherit;
|
|
border-color: #4c4c55;
|
|
}
|
|
|
|
body.dark-mode input[type="number"].table-input {
|
|
width: 30px;
|
|
height: 20px;
|
|
text-align: center;
|
|
-moz-appearance: textfield;
|
|
appearance: textfield;
|
|
border: none;
|
|
color: #e0e0e0;
|
|
background-color: inherit;
|
|
}
|
|
|
|
body.dark-mode th, body.dark-mode td {
|
|
background: inherit;
|
|
border-color: #555;
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
body.dark-mode thead th{
|
|
position: sticky;
|
|
z-index: 10
|
|
}
|
|
|
|
body.dark-mode tbody th{
|
|
position: sticky;
|
|
z-index: 9
|
|
}
|
|
|
|
body.dark-mode tbody tr:nth-child(even) {
|
|
background: #2d2d31;
|
|
}
|
|
|
|
body.dark-mode tbody tr:nth-child(odd) {
|
|
background: #4c4c55;
|
|
}
|
|
|
|
body.dark-mode tbody td:first-child,
|
|
body.dark-mode thead th:first-child {
|
|
position: sticky;
|
|
z-index: 11;
|
|
}
|
|
|
|
body.dark-mode .btn-ok {
|
|
background-color: #3a8f44;
|
|
}
|
|
|
|
body.dark-mode .btn-dark {
|
|
background-color: #34495e;
|
|
}
|
|
|
|
body.dark-mode select {
|
|
background-color: #2d2d31;
|
|
color: white;
|
|
border-color: #4c4c55;
|
|
}
|
|
|
|
body.dark-mode .warning-text {
|
|
color: #f44336;
|
|
}
|
|
|
|
body.dark-mode .normal-text {
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
body.dark-mode .icon-button {
|
|
background-color: #4c4c55;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container">
|
|
<div class="tip-panel" id="auto_flush_tip">
|
|
Studio would re-calculate your flushing volumes every time the filament
|
|
color changed or filaments changed. You could disable the auto-calculate
|
|
in Bambu Studio > Preferences.
|
|
</div>
|
|
|
|
<div style="margin-bottom: 10px; ">
|
|
<button
|
|
class="btn btn-ok"
|
|
onclick="calcFlushingVolumes()"
|
|
id="calc_btn"
|
|
>
|
|
Re-Calculate
|
|
</button>
|
|
<select
|
|
id="extruders"
|
|
onchange="handleExtruderSelect(document.getElementById('extruders').value)"
|
|
>
|
|
<option value="left" id="extruder_label_0">Left Nozzle</option>
|
|
<option value="right" id="extruder_label_1">Right Nozzle</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="scroll-container">
|
|
<table id="flushTable">
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div style="margin-top: 10px;">
|
|
<div id="volume_desp_panel" class="normal-text">
|
|
Flushing volume (mm³) for each filament pair.
|
|
</div>
|
|
<div
|
|
id="volume_range_panel"
|
|
class="normal-text"
|
|
style="margin-top: 5px;"
|
|
>
|
|
Suggestion: Flushing Volume in range [50, 999]
|
|
</div>
|
|
<div style="margin-top: 8px;">
|
|
<label
|
|
for="multiplierInput"
|
|
id="multiplier_label"
|
|
style="font-size: 12px;"
|
|
>Multiplier</label
|
|
>
|
|
<input
|
|
type="number"
|
|
step="0.1"
|
|
id="multiplierInput"
|
|
class="multiplier-input"
|
|
value="1.00"
|
|
oninput="onMultiplierChange()"
|
|
/>
|
|
</div>
|
|
<div
|
|
style="margin-top: 5px; margin-bottom: 5px; color: #666; font-size: 12px;"
|
|
id="multiplier_range_panel"
|
|
>
|
|
The multiplier should be in range [0.50, 3.00].
|
|
</div>
|
|
</div>
|
|
<div class="button-container" style="padding: 0px; margin: 0px;">
|
|
<button class="btn btn-ok" id="ok_btn" style="width: 60px; height: 30px; font-size: 12px; text-align: center;" onclick="storeData()">
|
|
Save
|
|
</button>
|
|
<button class="btn btn-cancel" id="cancel_btn" style="width: 60px; height: 30px; font-size: 12px; text-align: center;" onclick="quit()">
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let m_number_of_filaments // 材料数量
|
|
let m_number_of_extruders // 喷嘴数量
|
|
let m_colours // 颜色
|
|
let m_display_matrix // 显示的矩阵
|
|
let m_raw_matrix // 原始数据
|
|
let m_flush_multipiers // 冲刷系数
|
|
let m_cell_inputs = []; //显示的内容
|
|
let m_curr_extruder_id = 0
|
|
let m_min_flush_volumes = []
|
|
let m_max_flush_volumes = []
|
|
let m_min_flush_multiplier = 0.50
|
|
let m_max_flush_multiplier = 3
|
|
|
|
function storeData() {
|
|
var data = JSON.stringify({
|
|
msg: 'storeData',
|
|
number_of_extruders: m_number_of_extruders,
|
|
raw_matrix: m_raw_matrix,
|
|
flush_multiplier: m_flush_multipiers
|
|
})
|
|
window.wipingDialog.postMessage(data);
|
|
}
|
|
|
|
function quit() {
|
|
var data = JSON.stringify({
|
|
msg: 'quit'
|
|
})
|
|
window.wipingDialog.postMessage(data);
|
|
}
|
|
|
|
function calcFlushingVolumes() {
|
|
var data = JSON.stringify({
|
|
msg: 'updateMatrix',
|
|
extruder_id: m_curr_extruder_id
|
|
})
|
|
window.wipingDialog.postMessage(data);
|
|
}
|
|
|
|
function updateTable(dataMatrix, extruder_id) {
|
|
for (let i = 0; i < m_number_of_filaments; i++) {
|
|
for (let j = 0; j < m_number_of_filaments; j++) {
|
|
var newValue
|
|
var index = m_number_of_filaments * i + j
|
|
newValue = (i == j ? 0 : rawToDislay(dataMatrix[index] ,m_flush_multipiers[extruder_id]))
|
|
newValue = limitDisplayVal(newValue,extruder_id)
|
|
m_cell_inputs[i][j].value = newValue;
|
|
m_display_matrix[index] = newValue;
|
|
m_raw_matrix[extruder_id][index] = dataMatrix[index]
|
|
}
|
|
}
|
|
updateWarningTexts();
|
|
}
|
|
|
|
// const virtual_data = {
|
|
// filament_colors: [
|
|
// '#123456', '#7890AB', '#CDEF01', '#F1A2B3', '#4D8B72', '#D9E1F2', '#B76C8C', '#A9C3D3',
|
|
// '#FF5733', '#33FF57', '#5733FF', '#FF33FF', '#FFFF33', '#33FFFF', '#FF5733', '#3333FF'
|
|
// ],
|
|
// extruder_num: 2,
|
|
// flush_volume_matrixs: [
|
|
// Array.from({ length: 256 }, () => Math.floor(Math.random() * 1000)),
|
|
// Array.from({ length: 256 }, () => Math.floor(Math.random() * 1000))
|
|
// ],
|
|
// flush_multiplier:[1,1]
|
|
// }
|
|
window.addEventListener("DOMContentLoaded",function(){
|
|
var data = JSON.stringify({
|
|
msg: 'init',
|
|
})
|
|
window.wipingDialog.postMessage(data);
|
|
});
|
|
|
|
function buildText(data) {
|
|
document.getElementById('volume_desp_panel').innerText = data.volume_desp_panel
|
|
document.getElementById('volume_range_panel').innerText = data.volume_range_panel
|
|
document.getElementById('multiplier_range_panel').innerText = data.multiplier_range_panel
|
|
document.getElementById('auto_flush_tip').innerText = data.auto_flush_tip
|
|
document.getElementById('calc_btn').innerText = data.calc_btn_panel
|
|
document.getElementById('extruder_label_0').innerText = data.extruder_label_0
|
|
document.getElementById('extruder_label_1').innerText = data.extruder_label_1
|
|
document.getElementById('multiplier_label').innerText = data.multiplier_label
|
|
document.getElementById('ok_btn').innerText = data.ok_btn_label
|
|
document.getElementById('cancel_btn').innerText = data.cancel_btn_label
|
|
updateVolumeRange(m_min_flush_volumes[m_curr_extruder_id],m_max_flush_volumes[m_curr_extruder_id])
|
|
updateMultiplierRange(m_min_flush_multiplier,m_max_flush_multiplier)
|
|
}
|
|
|
|
// 计算亮度
|
|
function getLuminance(color) {
|
|
const hex = color.replace(/^#/, '');
|
|
const r = parseInt(hex.slice(0, 2), 16);
|
|
const g = parseInt(hex.slice(2, 4), 16);
|
|
const b = parseInt(hex.slice(4, 6), 16);
|
|
return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
}
|
|
|
|
|
|
function updateVolumeRange(min_volume,max_volume){
|
|
const panel = document.getElementById('volume_range_panel');
|
|
panel.innerText = panel.innerText.replace(/\[.*\]/, `[${min_volume}, ${max_volume}]`);
|
|
}
|
|
|
|
function updateMultiplierRange(min_multiplier,max_multiplier){
|
|
const panel = document.getElementById('multiplier_range_panel');
|
|
panel.innerText = panel.innerText.replace(/\[.*\]/, `[${min_multiplier}, ${max_multiplier}]`);
|
|
}
|
|
|
|
function updateFlushMultiplier(extruder_id){
|
|
document.getElementById("multiplierInput").value = m_flush_multipiers[extruder_id]
|
|
}
|
|
|
|
function rawToDislay(val, ratio){
|
|
return Math.round(val * ratio)
|
|
}
|
|
|
|
function displayToRaw(val, ratio){
|
|
if(ratio==0)
|
|
return 0
|
|
else
|
|
return Math.round(val / ratio)
|
|
}
|
|
|
|
function limitDisplayVal(val,extruder_id){
|
|
if (isNaN(val))
|
|
return 0
|
|
if (val < 0)
|
|
return 0
|
|
if (val > m_max_flush_volumes[extruder_id])
|
|
return m_max_flush_volumes[extruder_id];
|
|
return val
|
|
}
|
|
|
|
function buildTable(data) {
|
|
m_colours = data.filament_colors
|
|
m_number_of_extruders = data.extruder_num
|
|
m_number_of_filaments = data.filament_colors.length
|
|
m_display_matrix = data.flush_volume_matrixs[0].slice();
|
|
m_raw_matrix = data.flush_volume_matrixs.map(function(arr) {
|
|
return arr.slice();
|
|
});
|
|
m_flush_multipiers = data.flush_multiplier.slice()
|
|
m_max_flush_volumes = data.max_flush_volumes
|
|
m_min_flush_volumes = data.min_flush_volumes
|
|
m_min_flush_multiplier = data.min_flush_multiplier
|
|
m_max_flush_multiplier = data.max_flush_multiplier
|
|
|
|
if(data.is_dark_mode == true)
|
|
document.body.classList.add('dark-mode');
|
|
|
|
updateFlushMultiplier(m_curr_extruder_id)
|
|
|
|
const selectElement = document.getElementById('extruders');
|
|
if (m_number_of_extruders > 1) {
|
|
selectElement.style.display = 'inline-block';
|
|
} else {
|
|
selectElement.style.display = 'none';
|
|
}
|
|
|
|
var thead = document.querySelector("#flushTable thead tr");
|
|
var tbody = document.querySelector("#flushTable tbody");
|
|
thead.innerHTML = "";
|
|
tbody.innerHTML = "";
|
|
|
|
for (let j = 0; j < m_number_of_filaments; j++) {
|
|
if (j == 0) {
|
|
var tag = document.createElement("th")
|
|
tag.innerHTML = "<div style='font-size:12px'>from/to</div>"
|
|
thead.appendChild(tag)
|
|
}
|
|
let th = document.createElement("th");
|
|
const luminance = getLuminance(m_colours[j]);
|
|
const textColor = luminance > 0.5 ? "black" : "white";
|
|
th.innerHTML = `<div class="icon-button" style="font-size:10px;background:${m_colours[j]}; color:${textColor};">${j + 1}</div>`;
|
|
thead.appendChild(th);
|
|
}
|
|
|
|
for (let i = 0; i < m_number_of_filaments; i++) {
|
|
let tr = document.createElement("tr");
|
|
|
|
let rowHeader = document.createElement("th");
|
|
const luminance = getLuminance(m_colours[i]);
|
|
const textColor = luminance > 0.5 ? "black" : "white";
|
|
rowHeader.innerHTML = `<div class="icon-button" style="font-size:10px; background:${m_colours[i]};color:${textColor};">${i + 1}</div>`;
|
|
tr.appendChild(rowHeader);
|
|
|
|
m_cell_inputs[i] = [];
|
|
|
|
for (let j = 0; j < m_number_of_filaments; j++) {
|
|
let td = document.createElement("td");
|
|
td.style.overflow = "hidden";
|
|
td.style.textOverflow = "ellipsis";
|
|
let displayVal = rawToDislay(m_raw_matrix[m_curr_extruder_id][m_number_of_filaments * i + j], m_flush_multipiers[m_curr_extruder_id])
|
|
displayVal = limitDisplayVal(displayVal, m_curr_extruder_id)
|
|
m_display_matrix[m_number_of_filaments*i + j] = displayVal
|
|
let readonly = (i === j);
|
|
let input = document.createElement("input");
|
|
input.className = "table-input"
|
|
input.type = "number";
|
|
input.value = readonly ? 0 : displayVal;
|
|
if (readonly) {
|
|
input.readOnly = true;
|
|
}
|
|
input.addEventListener("input", (e) => onCellInput(i, j, e));
|
|
|
|
m_cell_inputs[i][j] = input;
|
|
|
|
td.appendChild(input);
|
|
tr.appendChild(td);
|
|
}
|
|
|
|
tbody.appendChild(tr);
|
|
}
|
|
updateWarningTexts();
|
|
}
|
|
|
|
function onCellInput(i, j, event) {
|
|
const input = event.target;
|
|
let val = parseInt(input.value, 10);
|
|
val = limitDisplayVal(val, m_curr_extruder_id);
|
|
input.value = val;
|
|
|
|
if (i !== j) {
|
|
var index = m_number_of_filaments*i+j
|
|
m_raw_matrix[m_curr_extruder_id][index] = displayToRaw(val, m_flush_multipiers[m_curr_extruder_id])
|
|
m_display_matrix[index] = val;
|
|
}
|
|
updateWarningTexts();
|
|
}
|
|
|
|
function handleExtruderSelect(extruder) {
|
|
m_curr_extruder_id = extruder == 'left' ? 0 : 1
|
|
updateTable(m_raw_matrix[m_curr_extruder_id], m_curr_extruder_id)
|
|
updateVolumeRange(m_min_flush_volumes[m_curr_extruder_id],m_max_flush_volumes[m_curr_extruder_id])
|
|
updateFlushMultiplier(m_min_flush_multiplier[m_curr_extruder_id],m_max_flush_multiplier[m_curr_extruder_id])
|
|
updateFlushMultiplier(m_curr_extruder_id)
|
|
}
|
|
|
|
function onMultiplierChange() {
|
|
let val = parseFloat(document.getElementById("multiplierInput").value);
|
|
if (isNaN(val)) val = 1.0;
|
|
if(val<m_min_flush_multiplier){
|
|
val = m_min_flush_multiplier
|
|
document.getElementById("multiplierInput").value = m_min_flush_multiplier;
|
|
}
|
|
else if(val>m_max_flush_multiplier){
|
|
val = m_max_flush_multiplier
|
|
document.getElementById("multiplierInput").value = m_max_flush_multiplier;
|
|
}
|
|
m_flush_multipiers[m_curr_extruder_id] = val;
|
|
for (let i = 0; i < m_number_of_filaments; i++) {
|
|
for (let j = 0; j < m_number_of_filaments; j++) {
|
|
if (i === j) continue;
|
|
var index = i * m_number_of_filaments + j;
|
|
let displayVal = rawToDislay(m_raw_matrix[m_curr_extruder_id][index] ,m_flush_multipiers[m_curr_extruder_id])
|
|
displayVal = limitDisplayVal(displayVal, m_curr_extruder_id)
|
|
m_cell_inputs[i][j].value = displayVal;
|
|
m_display_matrix[index] = displayVal;
|
|
}
|
|
}
|
|
updateWarningTexts();
|
|
}
|
|
|
|
|
|
|
|
function updateWarningTexts() {
|
|
let hasException = false;
|
|
for (let i = 0; i < m_number_of_filaments; i++) {
|
|
for (let j = 0; j < m_number_of_filaments; j++) {
|
|
if (i === j) continue;
|
|
const input = m_cell_inputs[i][j];
|
|
let val = parseInt(input.value, 10);
|
|
if (isNaN(val)) val = 0;
|
|
|
|
if (val < m_min_flush_volumes[m_curr_extruder_id] || val > m_max_flush_volumes[m_curr_extruder_id]) {
|
|
input.style.color = "red";
|
|
hasException = true;
|
|
} else {
|
|
input.style.removeProperty("color");
|
|
}
|
|
}
|
|
}
|
|
|
|
const rangeLabel = document.getElementById("volume_range_panel");
|
|
if (hasException) {
|
|
rangeLabel.classList.remove("normal-text");
|
|
rangeLabel.classList.add("warning-text");
|
|
} else {
|
|
rangeLabel.classList.remove("warning-text");
|
|
rangeLabel.classList.add("normal-text");
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|