Skip to content

XPointWidget

简介

用于在视图中定义标注点坐标。既可以通过点击左键来初始化,也可以通过设定坐标来初始化。通过交互结束事件拿到对应的点坐标。

XPointWidget

主要方法

  • 获取当前 handle 的世界坐标(未放置时返回 null)。
  • 编程方式直接设置点位置(跳过交互放置流程)。
  • 重置 widget 到初始 IDLE 状态(可重新放置)。
  • 设置 handle 的基础颜色(非高亮状态)。
  • 设置悬停高亮颜色。
  • 获取当前高亮颜色。
  • 设置缩放因子。
  • 放置/移动开始时触发。
  • 拖拽过程中持续触发。
  • 放置/移动结束时触发。

使用演示

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">
      <button @click="placeSphere">放置球体点(默认)</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>

    <!-- 3D 视图 -->
    <XView3D
      ref="view3D"
      viewID="xpoint-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 vtkCubeSource from "@kitware/vtk.js/Filters/Sources/CubeSource";
import vtkSphereSource from "@kitware/vtk.js/Filters/Sources/SphereSource";
import vtkPolyData from "@kitware/vtk.js/Common/DataModel/PolyData";
import vtkPoints from "@kitware/vtk.js/Common/Core/Points";
import vtkCellArray from "@kitware/vtk.js/Common/Core/CellArray";
import XPointWidget from "@/widgets/XPointWidget/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 XPointWidget.newInstance> | null = null;
let widgetView: any = null; // view widget(addWidget 返回值),拥有 onXxxInteractionEvent

// ── 初始化 ────────────────────────────────────────────────────────────────────
onMounted(() => {
  if (!view3D.value) return;

  const renderer = view3D.value.getRenderer();
  const renderWindow = view3D.value.getRenderWindow();

  // ① 创建 WidgetManager
  widgetManager = vtkWidgetManager.newInstance();
  widgetManager!.setRenderer(renderer);

  // ② 创建 XPointWidget(默认球体 glyph,黄色,scale1=20)
  widget = XPointWidget.newInstance({
    activeColor: [255, 120, 0], // hover 时:橙色
    activeScaleFactor: 1.4, // hover 时放大 40%
  });

  // ③ 注册到场景;addWidget 返回 view widget(vtkAbstractWidget 实例)
  // onStartInteractionEvent / onInteractionEvent / onEndInteractionEvent
  // 由 vtkInteractorObserver.extend 注册,只存在于 view widget 上,
  // 而非 factory widget(vtkAbstractWidgetFactory)上。
  widgetView = widgetManager!.addWidget(widget as any);
  widget.setHandleOrigin([10, 0, 0]); // 初始位置

  // ④ 监听交互事件(从 widgetView 上监听)
  widgetView?.onEndInteractionEvent(() => {
    const origin = widget!.getHandleOrigin();
    if (origin) {
      position.value = origin.map((v) => v.toFixed(2)).join(", ");
    }
    // 放置/移动完成后重置裁切平面,避免几何体被 near/far 裁切
    renderer.resetCameraClippingRange();
    renderWindow.render();
  });

  // ⑤ 进入放置模式
  // widgetManager!.grabFocus(widget as any);

  renderer.resetCamera();
  renderWindow.render();
});

onBeforeUnmount(() => {
  widgetManager?.delete();
});

// ── 按钮操作 ─────────────────────────────────────────────────────────────────

/** 默认球体形态 */
function placeSphere() {
  resetWidget();
  widget!.setGlyphSource(
    vtkSphereSource.newInstance({
      phiResolution: 16,
      thetaResolution: 16,
      radius: 0.5,
    }),
  );
  // 恢复黄色
  widget!.setColor3([255, 230, 0]);
  //   widget!.setScale1(20);
  widgetManager!.grabFocus(widget as any);
}

/** 重置 widget */
function resetWidget() {
  widget!.reset();
  position.value = null;
  widgetManager!.grabFocus(widget as any);
}
</script>