Align registration status and crisp slice masks
This commit is contained in:
@@ -1813,7 +1813,7 @@ function paintMaskPixel(maskData, width, height, x, y, rgb, alpha) {
|
||||
}
|
||||
|
||||
function solidStrokeRadius(width, height) {
|
||||
return Math.max(2.2, Math.min(5.5, Math.max(width, height) * 0.006));
|
||||
return Math.max(1, Math.min(2.4, Math.max(width, height) * 0.002));
|
||||
}
|
||||
|
||||
function fillSegmentCapsulesIntoMask(maskData, width, height, segments, rgb, alpha, radius) {
|
||||
@@ -1972,12 +1972,13 @@ function drawFallbackClosedRegion(context, width, height, segments, color, opaci
|
||||
function fillSegmentsAsSolidMask(context, width, height, segments, color, opacity = 0.72) {
|
||||
if (!segments.length) return 0;
|
||||
const rgb = parseCssColor(color);
|
||||
const alpha = Math.round(clamp(opacity, 0.1, 1) * 190);
|
||||
const alpha = Math.round(clamp(opacity, 0.1, 1) * 220);
|
||||
const maskCanvas = document.createElement("canvas");
|
||||
maskCanvas.width = width;
|
||||
maskCanvas.height = height;
|
||||
const maskContext = maskCanvas.getContext("2d");
|
||||
if (!maskContext) return 0;
|
||||
maskContext.imageSmoothingEnabled = false;
|
||||
const maskData = maskContext.createImageData(width, height);
|
||||
const radius = solidStrokeRadius(width, height);
|
||||
const groups = groupPlaneSegmentsByConnectivity(segments, radius * 1.15);
|
||||
@@ -2018,40 +2019,12 @@ function fillSegmentsAsSolidMask(context, width, height, segments, color, opacit
|
||||
filledPixels += groupPixels;
|
||||
});
|
||||
|
||||
filledPixels += closeSmallMaskGaps(maskData, width, height, rgb, alpha, 3);
|
||||
filledPixels += closeSmallMaskGaps(maskData, width, height, rgb, alpha, 1);
|
||||
filledPixels += fillInternalMaskHoles(maskData, width, height, rgb, alpha);
|
||||
maskContext.putImageData(maskData, 0, 0);
|
||||
context.imageSmoothingEnabled = false;
|
||||
context.drawImage(maskCanvas, 0, 0);
|
||||
|
||||
if (filledPixels === 0) {
|
||||
context.save();
|
||||
context.globalAlpha = clamp(opacity, 0.1, 1) * 0.42;
|
||||
context.strokeStyle = color;
|
||||
context.lineWidth = Math.max(0.8, Math.max(width, height) * 0.0012);
|
||||
context.lineCap = "round";
|
||||
context.lineJoin = "round";
|
||||
context.beginPath();
|
||||
segments.forEach((segment) => {
|
||||
context.moveTo(segment.a.x, segment.a.y);
|
||||
context.lineTo(segment.b.x, segment.b.y);
|
||||
});
|
||||
context.stroke();
|
||||
context.restore();
|
||||
}
|
||||
context.save();
|
||||
context.globalAlpha = clamp(opacity, 0.1, 1) * 0.65;
|
||||
context.strokeStyle = color;
|
||||
context.lineWidth = Math.max(0.8, Math.max(width, height) * 0.0012);
|
||||
context.lineCap = "round";
|
||||
context.lineJoin = "round";
|
||||
context.beginPath();
|
||||
segments.forEach((segment) => {
|
||||
context.moveTo(segment.a.x, segment.a.y);
|
||||
context.lineTo(segment.b.x, segment.b.y);
|
||||
});
|
||||
context.stroke();
|
||||
context.restore();
|
||||
|
||||
return filledPixels;
|
||||
}
|
||||
|
||||
@@ -2079,6 +2052,7 @@ async function drawMappingView(version = state.mappingVersion) {
|
||||
if (canvas.width !== width) canvas.width = width;
|
||||
if (canvas.height !== height) canvas.height = height;
|
||||
const context = canvas.getContext("2d");
|
||||
context.imageSmoothingEnabled = false;
|
||||
context.clearRect(0, 0, width, height);
|
||||
|
||||
const targetZ = sliceToSceneZ(state.sliceIndex, volumeScene);
|
||||
@@ -2101,7 +2075,7 @@ async function drawMappingView(version = state.mappingVersion) {
|
||||
const position = record.mesh.geometry.attributes.position;
|
||||
if (!position) continue;
|
||||
const triangleCount = Math.floor(position.count / 3);
|
||||
const step = Math.max(1, Math.ceil(triangleCount / 260000));
|
||||
const step = 1;
|
||||
const segments = [];
|
||||
for (let tri = 0; tri < triangleCount; tri += step) {
|
||||
tempA.fromBufferAttribute(position, tri * 3).applyMatrix4(matrix);
|
||||
@@ -3166,8 +3140,8 @@ function wireEvents() {
|
||||
$("autoSettingsPanel").scrollIntoView({ block: "nearest", behavior: "smooth" });
|
||||
});
|
||||
$("suggestBoneBtn").addEventListener("click", suggestBoneSelection);
|
||||
$("autoCoarseBtn").addEventListener("click", runAutoCoarse);
|
||||
$("autoFineBtn").addEventListener("click", runAutoFine);
|
||||
$("autoCoarseBtn")?.addEventListener("click", runAutoCoarse);
|
||||
$("autoFineBtn")?.addEventListener("click", runAutoFine);
|
||||
$("savePoseBtn").addEventListener("click", () => saveRegistration());
|
||||
$("saveIterateBtn").addEventListener("click", saveAndIteratePose);
|
||||
$("restorePoseBtn").addEventListener("click", restoreAutoPose);
|
||||
|
||||
@@ -236,9 +236,8 @@
|
||||
<label>迭代轮次<input id="autoIterations" type="number" min="1" max="100" value="30" /></label>
|
||||
<label>每轮候选<input id="autoCandidates" type="number" min="6" max="96" value="36" /></label>
|
||||
</div>
|
||||
<div class="pose-actions modal-actions auto-run-actions">
|
||||
<button id="autoCoarseBtn" type="button">自动粗配准</button>
|
||||
<button id="autoFineBtn" type="button">自动微调</button>
|
||||
<div class="registration-operation-hint">
|
||||
下方为配准操作区:保存当前位姿、保存并迭代自动微调,或退回上次自动调整前位姿。
|
||||
</div>
|
||||
<div class="pose-actions modal-actions save-pose-actions">
|
||||
<button id="savePoseBtn" type="button">保存位姿</button>
|
||||
|
||||
@@ -2039,6 +2039,18 @@ input[type="range"] {
|
||||
margin: 12px 0 0;
|
||||
}
|
||||
|
||||
.registration-operation-hint {
|
||||
border: 1px solid rgba(25, 214, 195, 0.26);
|
||||
border-radius: 8px;
|
||||
background: rgba(20, 184, 166, 0.08);
|
||||
color: #a7f3d0;
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
line-height: 1.45;
|
||||
margin-top: 12px;
|
||||
padding: 9px 10px;
|
||||
}
|
||||
|
||||
.auto-config-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(240px, 0.85fr) minmax(300px, 1.15fr);
|
||||
|
||||
Reference in New Issue
Block a user