feat/vtk-3d-view #7
|
|
@ -0,0 +1,495 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>三栏结构重构 · 高保真原型对比</title>
|
||||||
|
<style>
|
||||||
|
/* ===== 真实 app 深色令牌(src/app/Theme.cpp dark 列) ===== */
|
||||||
|
:root{
|
||||||
|
--bg-app:#0E1116; --bg-panel:#161A20; --bg-panel-subtle:#161B22; --bg-header:#12161C;
|
||||||
|
--bg-hover:#1B2129; --bg-selected:#16243F;
|
||||||
|
--border:#262C35; --border-strong:#333B45;
|
||||||
|
--text:#E6E9EF; --text-2:#A4ADBB; --text-3:#7A8494; --text-dis:#5A626F;
|
||||||
|
--accent:#5E8DF5; --accent-h:#93B4FA;
|
||||||
|
--canvas-bg:#0B1320; --canvas-soft:#111B2D; --canvas-grid:#1E2A3D;
|
||||||
|
--canvas-text:#E6ECF5; --canvas-dim:#8A97AC;
|
||||||
|
--danger:#FF6166; --warn:#F5A623; --ok:#46C07A;
|
||||||
|
--r:6px;
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0;}
|
||||||
|
html,body{height:100%;}
|
||||||
|
body{
|
||||||
|
font-family:"Microsoft YaHei UI","Segoe UI",system-ui,sans-serif;
|
||||||
|
background:#05080d;color:var(--text);font-size:13px;
|
||||||
|
display:flex;flex-direction:column;height:100vh;overflow:hidden;
|
||||||
|
}
|
||||||
|
/* ===== 顶部方案切换条(原型控制,非 app 一部分) ===== */
|
||||||
|
.meta{
|
||||||
|
display:flex;align-items:center;gap:14px;padding:10px 16px;
|
||||||
|
background:#11151c;border-bottom:1px solid var(--border);flex:0 0 auto;
|
||||||
|
}
|
||||||
|
.meta .lbl{color:var(--text-3);font-size:12px;}
|
||||||
|
.meta .opts{display:flex;gap:8px;}
|
||||||
|
.meta button{
|
||||||
|
font:inherit;font-size:12.5px;color:var(--text-2);background:var(--bg-panel);
|
||||||
|
border:1px solid var(--border);border-radius:20px;padding:6px 16px;cursor:pointer;
|
||||||
|
transition:.12s;
|
||||||
|
}
|
||||||
|
.meta button:hover{border-color:var(--accent);color:var(--text);}
|
||||||
|
.meta button.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600;}
|
||||||
|
.meta .tag{margin-left:auto;font-size:11.5px;color:var(--text-3);}
|
||||||
|
.meta .tag b{color:var(--ok);}
|
||||||
|
|
||||||
|
/* ===== app 外壳 ===== */
|
||||||
|
.app{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;background:var(--bg-app);}
|
||||||
|
/* TopBar */
|
||||||
|
.topbar{
|
||||||
|
height:46px;flex:0 0 auto;display:flex;align-items:center;gap:14px;padding:0 14px;
|
||||||
|
background:var(--bg-header);border-bottom:1px solid var(--border);
|
||||||
|
}
|
||||||
|
.topbar .logo{width:24px;height:24px;border-radius:6px;background:linear-gradient(135deg,var(--accent),#3B73EC);}
|
||||||
|
.topbar .ws{font-weight:600;color:var(--text);}
|
||||||
|
.topbar .ws small{color:var(--text-3);font-weight:400;margin-left:6px;}
|
||||||
|
.topbar .spacer{flex:1;}
|
||||||
|
.topbar .ico{width:30px;height:30px;border-radius:6px;display:grid;place-items:center;color:var(--text-2);}
|
||||||
|
.topbar .ico:hover{background:var(--bg-hover);}
|
||||||
|
.topbar .avatar{width:30px;height:30px;border-radius:50%;background:var(--accent);color:#fff;display:grid;place-items:center;font-size:12px;font-weight:700;}
|
||||||
|
|
||||||
|
/* dock 网格 */
|
||||||
|
.dockgrid{flex:1 1 auto;display:grid;gap:6px;padding:6px;min-height:0;}
|
||||||
|
/* 默认(A/B):左 / 中 / 右 三列 */
|
||||||
|
.dockgrid.cols{grid-template-columns:288px 1fr 248px;grid-template-rows:1fr;}
|
||||||
|
.col{display:flex;flex-direction:column;gap:6px;min-height:0;min-width:0;}
|
||||||
|
|
||||||
|
/* dock 面板 */
|
||||||
|
.dock{
|
||||||
|
background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--r);
|
||||||
|
display:flex;flex-direction:column;min-height:0;overflow:hidden;
|
||||||
|
}
|
||||||
|
.dock-hd{
|
||||||
|
height:30px;flex:0 0 auto;display:flex;align-items:center;gap:6px;padding:0 10px;
|
||||||
|
background:var(--bg-header);border-bottom:1px solid var(--border);
|
||||||
|
font-size:12.5px;font-weight:600;color:var(--text-2);
|
||||||
|
}
|
||||||
|
.dock-hd .badge{margin-left:auto;font-size:10.5px;font-weight:600;color:var(--text-3);
|
||||||
|
background:var(--bg-hover);border-radius:9px;padding:1px 7px;}
|
||||||
|
.dock-bd{flex:1 1 auto;overflow:auto;padding:8px 10px;min-height:0;}
|
||||||
|
.dock.flex1{flex:1 1 auto;}
|
||||||
|
.dock.flexN{flex:0 0 auto;}
|
||||||
|
|
||||||
|
/* 对象树 / 列表项 */
|
||||||
|
.tree{list-style:none;}
|
||||||
|
.tree li{padding:4px 2px;color:var(--text);white-space:nowrap;border-radius:4px;}
|
||||||
|
.tree li:hover{background:var(--bg-hover);}
|
||||||
|
.tree li.sel{background:var(--bg-selected);}
|
||||||
|
.tree .ind1{padding-left:18px;}
|
||||||
|
.tree .ind2{padding-left:34px;}
|
||||||
|
.tree .ind3{padding-left:50px;}
|
||||||
|
.ck{display:inline-block;width:13px;height:13px;border:1px solid var(--border-strong);border-radius:3px;
|
||||||
|
vertical-align:-2px;margin-right:7px;position:relative;background:var(--canvas-bg);}
|
||||||
|
.ck.on{background:var(--accent);border-color:var(--accent);}
|
||||||
|
.ck.on::after{content:"";position:absolute;left:4px;top:1px;width:3px;height:7px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg);}
|
||||||
|
.tw{color:var(--text-3);margin-right:4px;font-size:10px;}
|
||||||
|
.muted{color:var(--text-3);}
|
||||||
|
.pill{font-size:10px;color:var(--accent-h);border:1px solid var(--canvas-grid);border-radius:8px;padding:0 6px;margin-left:6px;}
|
||||||
|
|
||||||
|
/* 工具条 */
|
||||||
|
.toolbar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px 10px;
|
||||||
|
border-bottom:1px solid var(--border);background:var(--bg-panel-subtle);}
|
||||||
|
.toolbar.canvas{background:var(--canvas-soft);border-color:var(--canvas-grid);}
|
||||||
|
select.mini,.btnm{
|
||||||
|
font:inherit;font-size:11.5px;color:var(--text);background:var(--canvas-bg);
|
||||||
|
border:1px solid var(--canvas-grid);border-radius:4px;padding:3px 7px;cursor:pointer;
|
||||||
|
}
|
||||||
|
.btnm:hover{background:var(--bg-hover);border-color:var(--accent);}
|
||||||
|
.grp{display:flex;gap:3px;flex-wrap:wrap;}
|
||||||
|
.grp .btnm{padding:3px 8px;}
|
||||||
|
/* 工具条分组栏位 */
|
||||||
|
.toolbar.col{flex-direction:column;align-items:stretch;gap:0;}
|
||||||
|
.tgrp{display:flex;flex-wrap:wrap;align-items:center;gap:5px;padding:6px 0;border-bottom:1px dashed var(--canvas-grid);}
|
||||||
|
.tgrp:last-child{border-bottom:none;}
|
||||||
|
.tgrp .glbl{width:100%;font-size:11px;font-weight:600;color:var(--canvas-dim);margin-bottom:2px;}
|
||||||
|
.flbl{font-size:11px;color:var(--canvas-dim);display:inline-flex;align-items:center;gap:2px;}
|
||||||
|
.zin{font:inherit;font-size:11px;width:52px;color:var(--text);background:var(--canvas-bg);
|
||||||
|
border:1px solid var(--canvas-grid);border-radius:4px;padding:2px 5px;}
|
||||||
|
/* 表单行:标签固定宽 + 控件填满,不再流式折行 */
|
||||||
|
.frow{display:flex;align-items:center;gap:8px;width:100%;}
|
||||||
|
.frow .flbl{width:58px;flex:0 0 auto;}
|
||||||
|
.frow select.mini{width:132px;flex:0 0 auto;}
|
||||||
|
.frow .track{flex:1;}
|
||||||
|
.sval{font-size:11px;color:var(--canvas-text);width:36px;text-align:right;flex:0 0 auto;}
|
||||||
|
/* 滑块:独立类,不再依赖 .slider 父级(之前那样导致 track 0 高度看不见) */
|
||||||
|
.track{height:5px;background:var(--canvas-grid);border-radius:3px;position:relative;min-width:80px;}
|
||||||
|
.track .knob{position:absolute;top:-4px;width:13px;height:13px;border-radius:50%;
|
||||||
|
background:var(--accent);box-shadow:0 0 0 3px rgba(94,141,245,.22);cursor:pointer;}
|
||||||
|
.track .fill{position:absolute;left:0;top:0;bottom:0;background:var(--accent);border-radius:3px;opacity:.55;}
|
||||||
|
|
||||||
|
/* VTK 视图区 */
|
||||||
|
.vtk{
|
||||||
|
flex:1 1 auto;position:relative;min-height:0;border-radius:var(--r);overflow:hidden;
|
||||||
|
background:radial-gradient(120% 120% at 50% 18%,#16243f 0%,var(--canvas-bg) 60%);
|
||||||
|
border:1px solid var(--border);
|
||||||
|
}
|
||||||
|
.vtk .hd{position:absolute;top:0;left:0;right:0;height:30px;display:flex;align-items:center;padding:0 10px;
|
||||||
|
background:rgba(18,22,28,.72);border-bottom:1px solid var(--canvas-grid);font-size:12px;color:var(--text-2);z-index:4;backdrop-filter:blur(2px);}
|
||||||
|
/* 全屏按钮(VTK视图 + 数据详情 标题栏右侧) */
|
||||||
|
.fsbtn{margin-left:auto;width:22px;height:20px;display:grid;place-items:center;cursor:pointer;
|
||||||
|
border-radius:4px;color:var(--text-3);font-size:13px;}
|
||||||
|
.fsbtn:hover{background:var(--bg-hover);color:var(--accent);}
|
||||||
|
.fs-on{position:fixed !important;inset:0;z-index:90;border-radius:0;width:auto !important;height:auto !important;}
|
||||||
|
/* 假三维:两片帘面 + 体素盒 + 切片 */
|
||||||
|
.scene{position:absolute;inset:30px 0 0 0;perspective:900px;display:grid;place-items:center;}
|
||||||
|
.stage{transform-style:preserve-3d;transform:rotateX(60deg) rotateZ(-28deg);}
|
||||||
|
.curtain{position:absolute;width:230px;height:120px;left:-115px;top:-60px;
|
||||||
|
background:linear-gradient(90deg,#2a4a8f,#3aa0c0 35%,#7ec96f 60%,#e8c84a 78%,#d9603a);
|
||||||
|
opacity:.92;border:1px solid rgba(255,255,255,.18);box-shadow:0 0 24px rgba(94,141,245,.25);}
|
||||||
|
.curtain.b{transform:rotateZ(90deg) translateZ(0);opacity:.78;}
|
||||||
|
.axes{position:absolute;left:-130px;top:70px;width:0;height:0;}
|
||||||
|
.axes i{position:absolute;height:2px;transform-origin:left center;}
|
||||||
|
.ax-x{width:150px;background:#e5605f;}
|
||||||
|
.ax-y{width:120px;background:#46c07a;transform:rotate(-90deg);}
|
||||||
|
.grid-floor{position:absolute;width:300px;height:300px;left:-150px;top:-150px;
|
||||||
|
background-image:linear-gradient(var(--canvas-grid) 1px,transparent 1px),linear-gradient(90deg,var(--canvas-grid) 1px,transparent 1px);
|
||||||
|
background-size:30px 30px;opacity:.35;transform:translateZ(-2px);}
|
||||||
|
.slice3d{position:absolute;width:160px;height:90px;left:-80px;top:-45px;
|
||||||
|
background:repeating-linear-gradient(45deg,rgba(94,141,245,.35) 0 8px,rgba(94,141,245,.12) 8px 16px);
|
||||||
|
border:2px solid var(--accent);transform:rotateY(0deg) rotateZ(28deg) translateZ(40px);box-shadow:0 0 18px rgba(94,141,245,.4);}
|
||||||
|
.legend{position:absolute;right:12px;bottom:12px;width:14px;height:96px;border-radius:3px;
|
||||||
|
background:linear-gradient(#d9603a,#e8c84a,#7ec96f,#3aa0c0,#2a4a8f);border:1px solid var(--canvas-grid);z-index:3;}
|
||||||
|
.legend::after{content:"Ω·m";position:absolute;left:-24px;top:40px;font-size:10px;color:var(--canvas-dim);}
|
||||||
|
|
||||||
|
/* tabs(方案 A) */
|
||||||
|
.tabbar{display:flex;gap:2px;border-bottom:1px solid var(--border);background:var(--bg-header);flex:0 0 auto;}
|
||||||
|
.tabbar .tab{padding:7px 14px;font-size:12.5px;color:var(--text-3);cursor:pointer;border-bottom:2px solid transparent;}
|
||||||
|
.tabbar .tab:hover{color:var(--text);}
|
||||||
|
.tabbar .tab.on{color:var(--accent);border-bottom-color:var(--accent);font-weight:600;}
|
||||||
|
.tabpane{display:none;flex-direction:column;min-height:0;flex:1 1 auto;}
|
||||||
|
.tabpane.on{display:flex;}
|
||||||
|
|
||||||
|
/* 折叠分段(方案 B) */
|
||||||
|
.section .sec-hd{display:flex;align-items:center;gap:6px;padding:7px 10px;cursor:pointer;
|
||||||
|
background:var(--bg-header);border-top:1px solid var(--border);font-weight:600;color:var(--text-2);font-size:12.5px;}
|
||||||
|
.section:first-child .sec-hd{border-top:none;}
|
||||||
|
.section .sec-hd .tw{font-size:11px;}
|
||||||
|
.section .sec-bd{padding:6px 10px 10px;}
|
||||||
|
|
||||||
|
/* 视图内嵌侧栏(方案 C 修正版):抽屉式,画布在右、不遮挡 */
|
||||||
|
.vtk.with-drawer .scene{left:var(--drawer-w,300px);transition:left .18s;}
|
||||||
|
.view-drawer{position:absolute;top:30px;left:0;bottom:0;width:300px;z-index:6;
|
||||||
|
background:rgba(17,27,45,.94);border-right:1px solid var(--canvas-grid);
|
||||||
|
display:flex;flex-direction:column;backdrop-filter:blur(3px);transition:width .18s;overflow:hidden;}
|
||||||
|
.view-drawer .tabbar{background:rgba(18,22,28,.55);}
|
||||||
|
.drawer-toggle{position:absolute;top:38px;z-index:7;left:300px;width:18px;height:46px;
|
||||||
|
background:rgba(17,27,45,.94);border:1px solid var(--canvas-grid);border-left:none;
|
||||||
|
border-radius:0 6px 6px 0;display:grid;place-items:center;color:var(--canvas-dim);cursor:pointer;
|
||||||
|
font-size:11px;transition:left .18s;}
|
||||||
|
.drawer-toggle:hover{color:var(--accent);}
|
||||||
|
.vtk.drawer-collapsed .view-drawer{width:0;border-right:none;}
|
||||||
|
.vtk.drawer-collapsed .scene{left:0;}
|
||||||
|
.vtk.drawer-collapsed .drawer-toggle{left:0;}
|
||||||
|
|
||||||
|
/* 右键菜单 */
|
||||||
|
.ctx{position:absolute;z-index:30;min-width:150px;background:var(--bg-panel);border:1px solid var(--border-strong);
|
||||||
|
border-radius:6px;box-shadow:0 10px 30px rgba(0,0,0,.6);padding:4px;display:none;}
|
||||||
|
.ctx.show{display:block;}
|
||||||
|
.ctx .it{padding:6px 12px;border-radius:4px;font-size:12.5px;color:var(--text);cursor:pointer;white-space:nowrap;}
|
||||||
|
.ctx .it:hover{background:var(--accent);color:#fff;}
|
||||||
|
.ctx .sep{height:1px;background:var(--border);margin:4px 6px;}
|
||||||
|
.ctx .it.sub::after{content:"▸";float:right;color:var(--text-3);margin-left:18px;}
|
||||||
|
|
||||||
|
.hint{font-size:11px;color:var(--text-3);padding:6px 10px;border-top:1px dashed var(--border);}
|
||||||
|
.note{font-size:11.5px;color:var(--warn);background:rgba(245,166,35,.08);border:1px solid rgba(245,166,35,.25);
|
||||||
|
border-radius:5px;padding:6px 9px;margin:8px 10px;}
|
||||||
|
kbd{background:var(--bg-hover);border:1px solid var(--border-strong);border-radius:3px;padding:0 5px;font-size:11px;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="meta">
|
||||||
|
<span class="lbl">三栏结构重构 · 布局方案</span>
|
||||||
|
<div class="opts">
|
||||||
|
<button data-opt="C" class="on">方案 C · 视图内嵌侧栏(修正·推荐)</button>
|
||||||
|
<button data-opt="A">方案 A · 左侧独立 dock</button>
|
||||||
|
<button data-opt="B">方案 B · 竖向分段 dock</button>
|
||||||
|
</div>
|
||||||
|
<span class="tag">配色取自 <b>Theme.cpp</b> 深色令牌 · 右键「三维分析」树里的三维体试试创建切片</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ============ APP 外壳 ============ -->
|
||||||
|
<div class="app">
|
||||||
|
<div class="topbar">
|
||||||
|
<div class="logo"></div>
|
||||||
|
<div class="ws">地大演示项目 <small>· 工作区</small></div>
|
||||||
|
<div class="spacer" style="flex:1"></div>
|
||||||
|
<div class="ico">⌗</div><div class="ico">⚙</div>
|
||||||
|
<div class="avatar">GZ</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dockgrid cols" id="grid">
|
||||||
|
<!-- 左列:内容随方案变 -->
|
||||||
|
<div class="col" id="leftcol"></div>
|
||||||
|
|
||||||
|
<!-- 中列:VTK + 详情 -->
|
||||||
|
<div class="col" style="min-width:0">
|
||||||
|
<div class="dock flex1" style="padding:0;border:none;background:transparent">
|
||||||
|
<div class="vtk" id="vtk">
|
||||||
|
<div class="hd">VTK视图 · 地大演示项目<span class="fsbtn" data-fs title="全屏 / 还原">⛶</span></div>
|
||||||
|
<div class="scene"><div class="stage">
|
||||||
|
<div class="grid-floor"></div>
|
||||||
|
<div class="curtain"></div>
|
||||||
|
<div class="curtain b"></div>
|
||||||
|
<div class="slice3d"></div>
|
||||||
|
<div class="axes"><i class="ax-x"></i><i class="ax-y"></i></div>
|
||||||
|
</div></div>
|
||||||
|
<div class="legend"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dock flexN" style="height:120px">
|
||||||
|
<div class="dock-hd">数据详情<span class="fsbtn" data-fs title="全屏 / 还原">⛶</span></div>
|
||||||
|
<div class="dock-bd muted" style="font-size:12px">选中数据集查看详情(源数据 / 切片 / 异常 / 插值模型 / 色阶 / 测量)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右列:异常/属性 -->
|
||||||
|
<div class="col">
|
||||||
|
<div class="dock flex1">
|
||||||
|
<div class="dock-hd">异常 / 对象属性</div>
|
||||||
|
<div class="dock-bd">
|
||||||
|
<ul class="tree">
|
||||||
|
<li><span class="tw">▾</span>异常体 A <span class="pill">随GS</span></li>
|
||||||
|
<li class="ind1"><span class="tw">▾</span>分组-1</li>
|
||||||
|
<li class="ind2 muted">异常 #1</li>
|
||||||
|
<li class="ind2 muted">异常 #2</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dock flexN" style="height:150px">
|
||||||
|
<div class="dock-hd">数据集属性</div>
|
||||||
|
<div class="dock-bd muted" style="font-size:12px">名称 / 类型 / 维度 / 创建时间…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 三维体数据集 右键菜单(原文行21-27:切片/色阶/显隐/详情,无删除) -->
|
||||||
|
<div class="ctx" id="ctx">
|
||||||
|
<div class="it sub" data-act="slice">切片</div>
|
||||||
|
<div class="it">色阶</div>
|
||||||
|
<div class="it">显示 / 隐藏</div>
|
||||||
|
<div class="it">数据详情</div>
|
||||||
|
</div>
|
||||||
|
<div class="ctx" id="ctxSub">
|
||||||
|
<div class="it">上下</div>
|
||||||
|
<div class="it">前后</div>
|
||||||
|
<div class="it">左右</div>
|
||||||
|
<div class="it">任意</div>
|
||||||
|
</div>
|
||||||
|
<!-- 切片数据集 右键菜单(原文行29-35:保存/保存为/导出/删除/色阶/显隐/详情) -->
|
||||||
|
<div class="ctx" id="ctxSlice">
|
||||||
|
<div class="it">保存</div>
|
||||||
|
<div class="it">保存为</div>
|
||||||
|
<div class="it">导出</div>
|
||||||
|
<div class="it" style="color:var(--danger)">删除</div>
|
||||||
|
<div class="sep"></div>
|
||||||
|
<div class="it">色阶</div>
|
||||||
|
<div class="it">显示 / 隐藏</div>
|
||||||
|
<div class="it">数据详情</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// ===== 三栏内容片段 =====
|
||||||
|
function dataset3DList(){return `
|
||||||
|
<ul class="tree">
|
||||||
|
<li><span class="ck on"></span>反演剖面-L1 <span class="pill">帘面</span></li>
|
||||||
|
<li><span class="ck on"></span>反演剖面-L2 <span class="pill">帘面</span></li>
|
||||||
|
<li><span class="ck"></span>体素模型-V1 <span class="pill">dd_voxel</span></li>
|
||||||
|
<li><span class="ck"></span>地形 DEM+影像</li>
|
||||||
|
</ul>`;}
|
||||||
|
function dataset2DList(){return `
|
||||||
|
<ul class="tree">
|
||||||
|
<li><span class="ck on"></span>测线-T1 <span class="pill">俯视</span></li>
|
||||||
|
<li><span class="ck"></span>轨迹-Tr1 <span class="pill">trajectory</span></li>
|
||||||
|
</ul>`;}
|
||||||
|
function analysisTree(){return `
|
||||||
|
<ul class="tree" id="anaTree">
|
||||||
|
<li><span class="ck on"></span><span class="tw">▾</span>GS-地大演示</li>
|
||||||
|
<li class="ind1" data-vol="1"><span class="ck on"></span><span class="tw">▾</span>三维体模型-V1 <span class="pill">右键</span></li>
|
||||||
|
<li class="ind2" data-slice="1"><span class="ck on"></span><span class="tw">▸</span>切片·上下-01 <span class="pill">右键</span></li>
|
||||||
|
<li class="ind2" data-slice="1"><span class="ck"></span><span class="tw">▸</span>切片·任意-02</li>
|
||||||
|
<li class="ind1" data-vol="1"><span class="ck"></span><span class="tw">▸</span>三维体模型-V2</li>
|
||||||
|
</ul>
|
||||||
|
<div class="hint">右键<b>三维体</b>→切片▸(上下/前后/左右/任意)·色阶·显隐·详情;右键<b>切片</b>→保存/保存为/导出/删除·色阶·显隐·详情。</div>`;}
|
||||||
|
|
||||||
|
function toolbar3D(canvas){return `
|
||||||
|
<div class="toolbar col ${canvas?'canvas':''}">
|
||||||
|
<div class="tgrp"><span class="glbl">坐标轴设置</span>
|
||||||
|
<div class="frow"><span class="flbl">显示方式</span><select class="mini"><option>标准</option><option>三维立体</option><option>不显示</option></select></div>
|
||||||
|
<div class="frow"><span class="flbl">O点位置</span><span class="btnm">设置…</span></div>
|
||||||
|
<div class="frow"><span class="flbl">刻度</span><select class="mini"><option>无刻度</option><option selected>米</option><option>英尺</option><option>经纬度</option></select></div>
|
||||||
|
<div class="frow"><span class="flbl">字体</span><span class="btnm">设置…</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="tgrp"><span class="glbl">水平/垂直比例</span>
|
||||||
|
<div class="frow"><span class="track"><span class="fill" style="width:24%"></span><span class="knob" style="left:24%"></span></span><span class="sval">2.0×</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="tgrp"><span class="glbl">快捷视图</span>
|
||||||
|
<span class="grp"><span class="btnm">前</span><span class="btnm">后</span><span class="btnm">左</span><span class="btnm">右</span><span class="btnm">上</span><span class="btnm">下</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="tgrp"><span class="glbl">缩放 (Zoom)</span>
|
||||||
|
<span class="grp"><span class="btnm">放大</span><span class="btnm">缩小</span><span class="btnm">适配</span></span>
|
||||||
|
</div>
|
||||||
|
</div>`;}
|
||||||
|
function toolbar2D(canvas){return `
|
||||||
|
<div class="toolbar col ${canvas?'canvas':''}">
|
||||||
|
<div class="tgrp"><span class="glbl">地图</span>
|
||||||
|
<div class="frow"><span class="flbl">底图源</span><select class="mini"><option>天地图</option><option>Google Map</option><option>隐藏</option></select></div>
|
||||||
|
</div>
|
||||||
|
<div class="tgrp"><span class="glbl">2D视图</span>
|
||||||
|
<div class="frow"><span class="flbl">位置</span><select class="mini" onchange="document.getElementById('zwrap').style.display=this.value==='自定义'?'inline-flex':'none'">
|
||||||
|
<option>关闭</option><option selected>Z=0</option><option>顶部</option><option>底部</option><option>自定义</option></select></div>
|
||||||
|
<div class="frow" id="zwrap" style="display:none"><span class="flbl">Z 值</span><input class="zin" type="number" value="0"><span class="flbl">m</span></div>
|
||||||
|
</div>
|
||||||
|
</div>`;}
|
||||||
|
|
||||||
|
// 对象树 dock(A/B/C 都有,作为勾选源)
|
||||||
|
function objectDock(){return `
|
||||||
|
<div class="dock flexN" style="height:170px">
|
||||||
|
<div class="dock-hd">对象 <span class="badge">勾选源</span></div>
|
||||||
|
<div class="dock-bd">
|
||||||
|
<ul class="tree">
|
||||||
|
<li class="sel"><span class="ck on"></span><span class="tw">▾</span>GS-地大演示</li>
|
||||||
|
<li class="ind1"><span class="ck on"></span>TM-反演成果</li>
|
||||||
|
<li class="ind1"><span class="ck"></span>TM-轨迹</li>
|
||||||
|
<li><span class="ck"></span><span class="tw">▸</span>GS-威立雅</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>`;}
|
||||||
|
|
||||||
|
// ===== 三种方案的左列 =====
|
||||||
|
function buildA(){ // Tab 切换
|
||||||
|
return objectDock() + `
|
||||||
|
<div class="dock flex1">
|
||||||
|
<div class="tabbar" id="tabbarA">
|
||||||
|
<div class="tab on" data-t="0">三维数据集</div>
|
||||||
|
<div class="tab" data-t="1">二维数据集</div>
|
||||||
|
<div class="tab" data-t="2">三维分析</div>
|
||||||
|
</div>
|
||||||
|
<div class="tabpane on">${toolbar3D(false)}<div class="dock-bd">${dataset3DList()}</div></div>
|
||||||
|
<div class="tabpane">${toolbar2D()}<div class="dock-bd">${dataset2DList()}</div></div>
|
||||||
|
<div class="tabpane"><div class="dock-bd">${analysisTree()}</div></div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
function buildB(){ // 竖向分段
|
||||||
|
return objectDock() + `
|
||||||
|
<div class="dock flex1">
|
||||||
|
<div class="dock-hd">数据集 / 分析 <span class="badge">全可见</span></div>
|
||||||
|
<div class="dock-bd" style="padding:0">
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-hd"><span class="tw">▾</span>三维数据集</div>
|
||||||
|
<div class="sec-bd">${toolbar3D(false)}${dataset3DList()}</div>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-hd"><span class="tw">▾</span>二维数据集</div>
|
||||||
|
<div class="sec-bd">${toolbar2D()}${dataset2DList()}</div>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-hd"><span class="tw">▾</span>三维分析</div>
|
||||||
|
<div class="sec-bd">${analysisTree()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
function buildC(){ // 视图内嵌侧栏(修正):左列保留对象列表(三栏的筛选来源)+ 详情
|
||||||
|
return objectDock() + `
|
||||||
|
<div class="dock flex1">
|
||||||
|
<div class="dock-hd">数据集(详情查看)</div>
|
||||||
|
<div class="dock-bd">
|
||||||
|
<ul class="tree">
|
||||||
|
<li class="muted">反演剖面-L1</li><li class="muted">体素模型-V1</li><li class="muted">测线-T1</li>
|
||||||
|
</ul>
|
||||||
|
<div class="note" style="color:var(--accent-h);background:rgba(94,141,245,.08);border-color:rgba(94,141,245,.3)">方案 C(修正):三个「子列表栏」内嵌在 VTK 视图左侧(抽屉式侧栏),三 tab 切换。画布在其右侧、不被遮挡;点侧栏右缘 <b>◀</b> 可折叠让画布全宽。这正是需求「VTK视图上提供三个子列表栏」的形态。左侧「对象」列表是三栏的筛选来源(需求:筛勾选对象中的 ds)。</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
function viewDrawerHTML(){return `
|
||||||
|
<div class="view-drawer">
|
||||||
|
<div class="tabbar">
|
||||||
|
<div class="tab on" data-t="0">三维数据集</div>
|
||||||
|
<div class="tab" data-t="1">二维数据集</div>
|
||||||
|
<div class="tab" data-t="2">三维分析</div>
|
||||||
|
</div>
|
||||||
|
<div class="tabpane on">${toolbar3D(true)}<div class="dock-bd">${dataset3DList()}</div></div>
|
||||||
|
<div class="tabpane">${toolbar2D(true)}<div class="dock-bd">${dataset2DList()}</div></div>
|
||||||
|
<div class="tabpane"><div class="dock-bd">${analysisTree()}</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="drawer-toggle" id="drawerToggle">◀</div>`;}
|
||||||
|
|
||||||
|
// ===== 渲染 =====
|
||||||
|
const grid=document.getElementById('grid');
|
||||||
|
const vtk=document.getElementById('vtk');
|
||||||
|
function render(opt){
|
||||||
|
document.querySelectorAll('.meta button').forEach(b=>b.classList.toggle('on',b.dataset.opt===opt));
|
||||||
|
const left=document.getElementById('leftcol');
|
||||||
|
// 清掉方案 C 的内嵌侧栏
|
||||||
|
vtk.classList.remove('with-drawer','drawer-collapsed');
|
||||||
|
vtk.querySelectorAll('.view-drawer,.drawer-toggle').forEach(e=>e.remove());
|
||||||
|
|
||||||
|
if(opt==='A'){ left.innerHTML=buildA(); wireTabs(); }
|
||||||
|
if(opt==='B'){ left.innerHTML=buildB(); wireSections(); }
|
||||||
|
if(opt==='C'){
|
||||||
|
left.innerHTML=buildC();
|
||||||
|
vtk.classList.add('with-drawer');
|
||||||
|
vtk.insertAdjacentHTML('beforeend', viewDrawerHTML());
|
||||||
|
wireTabs(vtk.querySelector('.view-drawer'));
|
||||||
|
const tg=document.getElementById('drawerToggle');
|
||||||
|
tg.onclick=()=>{const c=vtk.classList.toggle('drawer-collapsed');tg.textContent=c?'▶':'◀';};
|
||||||
|
}
|
||||||
|
wireCtx();
|
||||||
|
}
|
||||||
|
function wireTabs(scope){
|
||||||
|
(scope||document).querySelectorAll('.tabbar .tab').forEach(t=>{
|
||||||
|
t.onclick=()=>{
|
||||||
|
const bar=t.parentElement, panes=bar.parentElement.querySelectorAll(':scope > .tabpane');
|
||||||
|
bar.querySelectorAll('.tab').forEach(x=>x.classList.remove('on'));
|
||||||
|
t.classList.add('on');
|
||||||
|
panes.forEach((p,i)=>p.classList.toggle('on',i===+t.dataset.t));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function wireSections(){
|
||||||
|
document.querySelectorAll('.section .sec-hd').forEach(h=>{
|
||||||
|
h.onclick=()=>{const bd=h.nextElementSibling, tw=h.querySelector('.tw');
|
||||||
|
const open=bd.style.display!=='none'; bd.style.display=open?'none':''; tw.textContent=open?'▸':'▾';};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右键菜单:三维体(ctx+ctxSub子菜单) / 切片(ctxSlice)
|
||||||
|
const ctx=document.getElementById('ctx'), ctxSub=document.getElementById('ctxSub'),
|
||||||
|
ctxSlice=document.getElementById('ctxSlice');
|
||||||
|
function popAt(menu,e){e.preventDefault();hideCtx();
|
||||||
|
menu.style.left=e.pageX+'px';menu.style.top=e.pageY+'px';menu.classList.add('show');}
|
||||||
|
function wireCtx(){
|
||||||
|
document.querySelectorAll('[data-vol]').forEach(li=>li.oncontextmenu=(e)=>popAt(ctx,e));
|
||||||
|
document.querySelectorAll('[data-slice]').forEach(li=>li.oncontextmenu=(e)=>popAt(ctxSlice,e));
|
||||||
|
ctx.querySelector('[data-act=slice]').onmouseenter=()=>{
|
||||||
|
const r=ctx.getBoundingClientRect();
|
||||||
|
ctxSub.style.left=r.right+'px';ctxSub.style.top=r.top+'px';ctxSub.classList.add('show');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function hideCtx(){[ctx,ctxSub,ctxSlice].forEach(m=>m.classList.remove('show'));}
|
||||||
|
document.addEventListener('click',hideCtx);
|
||||||
|
document.addEventListener('scroll',hideCtx,true);
|
||||||
|
|
||||||
|
// 全屏切换:VTK视图 / 数据详情
|
||||||
|
document.querySelectorAll('[data-fs]').forEach(b=>b.onclick=(e)=>{
|
||||||
|
e.stopPropagation();
|
||||||
|
const panel=b.closest('.vtk')||b.closest('.dock');
|
||||||
|
const on=panel.classList.toggle('fs-on');
|
||||||
|
b.textContent=on?'🗗':'⛶';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.meta button').forEach(b=>b.onclick=()=>render(b.dataset.opt));
|
||||||
|
render('C');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
# VTK 三维视图「三栏结构重构」设计
|
||||||
|
|
||||||
|
- 日期:2026-06-16
|
||||||
|
- 分支:`feat/vtk-3d-view`
|
||||||
|
- 状态:**设计稿(经高保真原型逐项对齐用户反馈后定稿)**
|
||||||
|
- 上位文档:`2026-06-15-vtk-3d-supplementary-design.md`(总设计 v2)。本文是其 **A1 三栏结构** 的实现增量设计,**就 UI 形态与若干控件细节,取代总 spec §7.1 中"左侧新 dock / tab 二选一"的开放表述**。
|
||||||
|
- 高保真原型:`docs/superpowers/mockups/2026-06-16-three-column-layout.html`(深色令牌取自 `src/app/Theme.cpp`;默认即定稿方案)。
|
||||||
|
- 需求来源:`Geopro3.0 需求表.xlsx`「补充需求」页签(已逐行精读,行号见下文)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 目标与范围(用户已确认)
|
||||||
|
|
||||||
|
把当前"旧二维/三维切换 + 三个浮层"的过渡态 UI,重组为需求 A1 的**三个子列表栏**,并接通到已有的渲染/交互能力。
|
||||||
|
|
||||||
|
### 本轮 IN
|
||||||
|
1. **三栏内嵌侧栏**(VTK 视图内左侧抽屉 + 三 tab + 可折叠);删除旧「二维地图/三维视图」互斥切换;中央 dock 改名 **「VTK视图」**。
|
||||||
|
2. **收编三浮层**:左上 `layerPanel`(图层勾选)、右上 `axisBar`(P2 工具条)→ 并入「三维数据集」栏;左下 `sliceBar` → **删除**(切片改走三维分析右键菜单)。
|
||||||
|
3. **三维数据集栏**:工具条 4 栏位接 P2 已实现控制器;数据集列表按 `dimensionOf` 过滤 3D ds + 勾选 → 接现有 `VtkSceneController` 渲染。
|
||||||
|
4. **二维数据集栏**:列表按 2D 过滤;「地图 / 2D视图」控件做出来(底图瓦片渲染留 P5,本轮控件 UI + 2D 视图 Z 平面接通)。
|
||||||
|
5. **三维分析栏**:对象→三维体模型→切片 树;两个右键菜单 UI 完整;右键「切片」(上下/前后/左右/任意) **接已有 `SliceTool`**(替代 sliceBar)。
|
||||||
|
6. **VTK视图 + 数据详情** 标题栏右侧加**全屏按钮**。
|
||||||
|
|
||||||
|
### 本轮 OUT(菜单项可见但暂 stub/禁用,留 P4/P5)
|
||||||
|
- 切片 CRUD:保存 / 保存为 / 导出 / 删除(P4,`I3dSceneRepository` 接口已留位)。
|
||||||
|
- 色阶编辑(F26 参考 Geopro 1.0,无参考,留 P4)。
|
||||||
|
- 底图瓦片渲染(P5)、异常体管理 / 三维体详情 / 任务管理(P4)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 核心架构决策(关键澄清)
|
||||||
|
|
||||||
|
**全工作台只有一个共享的中央 VTK 视图**,三栏是叠在其上的「子列表栏」,各自把数据喂进同一视图。证据(需求交叉验证):
|
||||||
|
- 行 2「**VTK视图上**提供三个子列表栏」;行 11「显示在 VTK 中」;行 16「显示在 VTK 的 2D 视图」;行 19「显示在 vtk 的三维视图」——三处同指一个 VTK。
|
||||||
|
- 需求行 36「VTK视图」挂在「三维分析」下,**只是把视图交互(选中拖动旋转 / 双击正视)归类描述**,不代表视图归三维分析独有。
|
||||||
|
|
||||||
|
**推论(本轮落地):**
|
||||||
|
- 旧「二维地图/三维视图」互斥分段按钮(`main.cpp:308-316` 一带)**删除**。
|
||||||
|
- 2D 不再是独立视图模式,而是 VTK 视图里的一个 **2D 图层/平面**(底图 + 2D 数据,摆在某 Z 平面),由「二维数据集」栏控制。
|
||||||
|
- 中央 dock 名 `二维地图/三维视图` → **`VTK视图`**(同时承载 2D/3D,且为需求原文叫法)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 三栏物理形态(方案 C·视图内嵌侧栏)
|
||||||
|
|
||||||
|
- 三栏 = **VTK 视图内左侧的抽屉式侧栏**,三个 tab:`三维数据集 / 二维数据集 / 三维分析`,一次显一栏。
|
||||||
|
- 画布在侧栏右侧、**不被遮挡**;侧栏右缘有折叠开关(◀/▶),折叠后画布全宽。
|
||||||
|
- 侧栏与画布同属 VTK 视图容器(侧栏是视图子控件,符合"VTK视图上提供")。
|
||||||
|
- 左侧保留现有 `ObjectTreePanel`(「对象」dock)作为**筛选来源**:三栏列表只在"被勾选对象"范围内、按维度过滤显示 ds(需求行 10/15)。两级关系:勾对象 → 三栏按维度显示其 ds。
|
||||||
|
- 现有左下「数据集」dock(详情查看)保留,与三栏列表并存(总 spec §7.1 已定的"两条线")。
|
||||||
|
|
||||||
|
> 取舍记录:曾考虑"左侧独立 dock + tab"(方案 A)与"竖向分段"(方案 B),均被否——需求「VTK视图**上**」明确栏在视图内;浮窗式(最初的 C)遮挡画面亦否,改为抽屉式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 各栏内容(逐行对齐需求,含控件形态)
|
||||||
|
|
||||||
|
### 3.1 三维数据集栏(行 3–11)
|
||||||
|
工具条 = **4 个分组栏位** + 数据集列表:
|
||||||
|
|
||||||
|
1. **坐标轴设置**(行 4–6)——表单式,每项一行、左对齐:
|
||||||
|
- 显示方式:下拉(标准 / 三维立体 / 不显示)
|
||||||
|
- O点位置:按钮(弹框设原点)
|
||||||
|
- 刻度:下拉(无刻度 / 米 / 英尺 / 经纬度)
|
||||||
|
- 字体:按钮(设刻度文字字体)
|
||||||
|
2. **水平/垂直比例**(行 7)——**单个拖动滑块 + 数值**(如 2.0×,纵向放大系数;现有 `kVerticalExaggeration`)。**非两个独立控件**。
|
||||||
|
3. **快捷视图**(行 8)——6 钮:前 / 后 / 左 / 右 / 上 / 下。
|
||||||
|
4. **缩放 Zoom**(行 9)——3 钮:放大(In) / 缩小(Out) / 适配(Fit)。
|
||||||
|
5. **数据集列表**(行 10–11)——`dimensionOf==Dim3D` 过滤勾选对象的 ds,勾选→渲染。
|
||||||
|
|
||||||
|
### 3.2 二维数据集栏(行 12–16)= 3 栏位
|
||||||
|
1. **地图**(行 13)——下拉(天地图 / Google Map / 隐藏)。**底图瓦片渲染留 P5**。
|
||||||
|
2. **2D视图**(行 14)——下拉(关闭 / Z=0 / 顶部 / 底部 / **自定义**);选「自定义」显数值输入框。
|
||||||
|
- **「自定义 Z」= 世界绝对高程(米),向上为正**,与「Z=0/顶部/底部」同坐标系(`GeoLocalFrame` 世界 Z)。决策依据:同一下拉里「Z=0」即绝对值,「自定义」只是输入任意绝对 Z;与业界(Petrel/Leapfrog/ParaView)水平面高度用项目 CRS 绝对高程一致。
|
||||||
|
3. **数据集列表**(行 15–16)——`dimensionOf==Dim2D` 过滤,勾选→渲染到 VTK 的 2D 视图。
|
||||||
|
|
||||||
|
### 3.3 三维分析栏(行 17–35)
|
||||||
|
- **数据集列表 = 树**(行 18):对象 → 三维体模型数据集 → 切片。可勾选三维体/切片 → 渲染(行 19)。
|
||||||
|
- **右键菜单(按节点类型分派):**
|
||||||
|
|
||||||
|
**三维体数据集**(行 20–27,**直接项,无"创建切片"父级,无删除**):
|
||||||
|
| 菜单项 | 说明 | 本轮 |
|
||||||
|
|---|---|---|
|
||||||
|
| 切片 ▸ 上下 / 前后 / 左右 / 任意 | 一级「切片」父菜单 + 二级方向(上下/前后/左右=固定角度;任意=初始 45°可调)| **接已有 SliceTool** |
|
||||||
|
| 色阶 | 参考 Geopro 1.0 | OUT(stub/禁用)|
|
||||||
|
| 显示 / 隐藏 | actor 可见性 | IN |
|
||||||
|
| 数据详情 | 详情栏显示 | IN(接现有详情)|
|
||||||
|
|
||||||
|
> 注:需求字面是"上下切片/前后切片…"直接项;本轮按用户决策归入「切片」一级父菜单、二级去「切片」二字显「上下/前后/左右/任意」。
|
||||||
|
|
||||||
|
**切片数据集**(行 28–35,**有删除**):
|
||||||
|
| 菜单项 | 本轮 |
|
||||||
|
|---|---|
|
||||||
|
| 保存 / 保存为 / 导出 / 删除 | OUT(P4 CRUD,stub/禁用)|
|
||||||
|
| 色阶 | OUT |
|
||||||
|
| 显示 / 隐藏 | IN |
|
||||||
|
| 数据详情 | IN |
|
||||||
|
|
||||||
|
- **VTK视图交互**(行 36–38)、**切片分析**(行 39–51,含视图内切片右键:创建异常/保存/导出图片/导出dat/正视图/视图翻转/关闭)——属交互层,多已在 P3 实现或留 P4,本轮不在树结构范围内。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 全屏功能(新需求)
|
||||||
|
|
||||||
|
- 在 **「VTK视图」** 与 **「数据详情」** 两个 dock 标题栏右侧各加一个**全屏切换按钮**:点击→该视图充满工作区;再点→还原。
|
||||||
|
- 理由:两视图含图形、内容多,常需全屏操作。
|
||||||
|
- 实现方向(ADS):给 `CDockWidget` 标题栏插入自定义 `QToolButton`,切换时把该 dock 最大化覆盖 dock 管理区(隐藏同级 / 浮动后最大化,择一,落地时定)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 代码触点(落地指引,细节进 plan)
|
||||||
|
|
||||||
|
- `src/app/main.cpp::buildWorkbench`:
|
||||||
|
- 删 `layerPanel` / `axisBar` / `sliceBar` 三浮层及其锚定器(`RightTopAnchor`/`BottomLeftAnchor`)与 `showLayerPanel` 显隐逻辑。
|
||||||
|
- 删旧「二维地图/三维视图」分段切换;`vtkDock` 改名「VTK视图」。
|
||||||
|
- VTK 视图容器内新建**抽屉侧栏**(QTabWidget 或自绘,三 tab)+ 折叠开关;三栏工具条/列表迁入。
|
||||||
|
- **dock 布局持久化版本号须 bump**(见 `main.cpp:1429` 附近注释:改 dock 名/结构要升版本,否则旧布局反序列化错位)。
|
||||||
|
- 三维数据集工具条接 P2 已实现的 `VtkSceneController` 坐标轴/比例/快捷视图/zoom 槽(原 axisBar 的接线迁移,不重写控制器)。
|
||||||
|
- 数据集列表:用 `I3dSceneRepository::dimensionOf` 过滤;勾选信号接 `VtkSceneController`(复用现 `checkedTmsChanged` 一路的编排)。
|
||||||
|
- 三维分析树右键「切片」→ 调已有 `InteractionManager`/`SliceTool` 建切片(替代 sliceBar 原按钮路径)。
|
||||||
|
- 全屏按钮:ADS 标题栏自定义按钮 + 最大化/还原。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 待定 / 风险
|
||||||
|
|
||||||
|
- 全屏在 ADS 的最大化实现方式(隐藏同级 vs 浮动最大化)落地时定。
|
||||||
|
- 二维「地图」底图本轮仅控件,渲染 P5;需确保控件状态能持久化到 P5 不返工。
|
||||||
|
- 三栏侧栏与 ADS dock 的交互(侧栏是 VTK 容器子控件,不是 dock)——确保折叠/全屏时布局正确。
|
||||||
|
- dock 名变更后旧用户布局失效(bump 版本号后回落默认排布,可接受)。
|
||||||
Loading…
Reference in New Issue