Appearance
XRectCenterWidget
简介
通过一个矩形来定义中心,其四个角点都可以交互,同时通过移动中心可以平移整个 Widget 的位置。

主要方法
- 获取矩形左上角世界坐标(未放置时返回 null)。
- 获取矩形右上角世界坐标(未放置时返回 null)。
- 获取矩形右下角世界坐标(未放置时返回 null)。
- 获取矩形左下角世界坐标(未放置时返回 null)。
- 获取矩形中心点世界坐标(未放置时返回 null)。
- 编程方式设置中心点(保持矩形大小整体平移)。
- 一次性通过左上角和右下角设置矩形。
- 获取当前激活 handle 的世界坐标(未放置时返回 null)。
- 编程方式设置 handle 位置(跳过交互流程)。
- 重置 widget。
- 设置所有 handle 的基础颜色 [R, G, B](0-255)。
- 设置角点 handle 颜色 [R, G, B](0-255)。
- 设置中心 handle 颜色 [R, G, B](0-255)。
- 设置 handle hover 高亮颜色 [R, G, B](0-255)。
- 设置矩形边框颜色 [R, G, B](0-255)。
- 设置矩形边框 hover 高亮颜色 [R, G, B](0-255)。
- 获取当前 handle hover 高亮颜色。
- 设置 handle 缩放因子(世界坐标单位)。
setScale1的别名,与其完全等价。- 放置/移动开始时触发。
- 拖拽过程中持续触发。
- 放置/移动结束时触发。
使用演示
vue
<template>
<div
style="
display: flex;
flex-direction: column;
gap: 10px;
padding: 10px;
height: calc(100vh - 120px);
"
>
<!-- 工具栏 -->
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center">
<button @click="startPlace">开始框选矩形(默认)</button>
<button @click="resetWidget">重置</button>
<span
v-if="position"
style="
padding: 4px 8px;
background: #1a1a2e;
color: #42b883;
border-radius: 4px;
font-size: 12px;
"
>
中心位置: {{ position }}
</span>
</div>
<!-- 提示文字 -->
<div style="font-size: 12px; color: #888; line-height: 1.6">
左键按下定义左上角,拖动并释放定义右下角 · 四角点可调形状 ·
中心点可整体平移
</div>
<!-- 3D 视图 -->
<XView3D
ref="view3D"
viewID="xrectcenter-widget-demo"
:background="[0.08, 0.08, 0.12, 1]"
style="flex: 1; border: 2px solid #42b883; border-radius: 4px"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import XView3D from "@/components/XView3D.vue";
import vtkWidgetManager from "@kitware/vtk.js/Widgets/Core/WidgetManager";
import vtkSphereSource from "@kitware/vtk.js/Filters/Sources/SphereSource";
import XRectCenterWidget from "@/widgets/XRectCenterWidget/index.js";
// ── 组件状态 ──────────────────────────────────────────────────────────────────
const view3D = ref<InstanceType<typeof XView3D> | null>(null);
const position = ref<string | null>(null);
let widgetManager: ReturnType<typeof vtkWidgetManager.newInstance> | null =
null;
let widget: ReturnType<typeof XRectCenterWidget.newInstance> | null = null;
let widgetView: any = null;
// ── 初始化 ────────────────────────────────────────────────────────────────────
onMounted(() => {
if (!view3D.value) return;
const renderer = view3D.value.getRenderer();
const renderWindow = view3D.value.getRenderWindow();
// ① 创建 WidgetManager
widgetManager = vtkWidgetManager.newInstance();
widgetManager!.setRenderer(renderer);
// ② 创建 XRectCenterWidget
widget = XRectCenterWidget.newInstance({
activeColor: [255, 120, 0], // hover 时:橙色
activeScaleFactor: 1.4, // hover 时放大 40%
handleScale: 0.1,
lineThickness: 3,
});
// ③ 注册到场景
widgetView = widgetManager!.addWidget(widget as any);
// ④ 监听交互事件
widgetView?.onEndInteractionEvent(() => {
const origin = widget!.getCenterOrigin();
if (origin) {
position.value = origin.map((v) => v.toFixed(2)).join(", ");
}
renderer.getActiveCamera().setClippingRange(1, 10000);
renderWindow.render();
});
// ⑤ 进入放置模式
widgetManager!.grabFocus(widget as any);
renderer.resetCamera();
renderWindow.render();
});
onBeforeUnmount(() => {
widgetManager?.delete();
});
// ── 按钮操作 ──────────────────────────────────────────────────────────────────
/** 默认球体标记 */
function startPlace() {
resetWidget();
widget!.setGlyphSource(
vtkSphereSource.newInstance({
phiResolution: 16,
thetaResolution: 16,
radius: 0.5,
}),
);
// 角点青色,中心点黄色
widget!.setCornerColor([0, 230, 255]);
widget!.setCenterColor([255, 230, 0]);
widgetManager!.grabFocus(widget as any);
}
/** 重置 widget */
function resetWidget() {
widget!.reset();
position.value = null;
widgetManager!.grabFocus(widget as any);
}
</script>