npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tz-stl-design

v6.10.2609

Published

beta: x.x.0-beta.x, x.x.x

Readme

TuZhi

TuZhi 是一个基于 TypeScript、Vite 和 Babylon.js 的 3D 刀具预览工具。它提供场景初始化、STL 模型导入、刀具参数 JSON 解析、尺寸标注和点击事件,也可以通过 Tool3d 单独使用 Babylon.js 基础工具。

本文档以“如何接入和调用”为主,适用于直接运行本项目或在 Vue 页面中集成构建后的库。

运行项目

环境要求:Node.js 18 或更高版本。

页面容器与初始化

创建 App 之前,页面必须已经有一个 ID 为 web3d 的 DOM 元素。

<div id="web3d"></div>

容器必须有明确的宽高,否则 canvas 没有可用的显示区域。项目内页面使用全屏布局:

html,
body,
#web3d {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  position: relative;
}

初始化会创建 Babylon.js Engine、Scene、相机、默认灯光、方向立方体以及持续渲染循环:

import { App } from 'tz-stl-design';

const app = new App();

在非 Vue 页面中,应在 DOM 创建完成后初始化,例如在入口模块执行 new App()。如果容器不存在,初始化会抛出 Div with id web3d not found。

方向立方体根据主输入设备自动适配样式:PC 保持原有的 84px 立方体;无悬停能力、使用粗指针的触屏设备(手机、平板)采用 64px 紧凑立方体,调整右上角间距并避让屏幕安全区域。移动端保留方向切换、斜视图和锁定视角功能,正面点击区域及锁定按钮高度为 44px。仅缩小 PC 浏览器窗口不会触发移动端样式。

方向切换动画结束后会恢复相机控制,触屏仍使用单指旋转、双指平移或缩放。恢复控制时不会重复绑定指针输入;视角已锁定时,继续保持指针控制禁用。

App 是应用主实例,常用管理器可通过以下属性访问:

| 属性 | 用途 | | --- | --- | | app.apiMgr | 高层刀具导入 API。 | | app.eventMgr | 应用事件中心,使用 Node.js EventEmitter 的 on、off 等方法。 |

Vue 中使用

必须等 Vue 将容器挂载到 DOM 后再创建 App。保留固定的 id="web3d";库内部通过这个 ID 查找容器,ref 仅用于 Vue 中检查挂载状态。请为容器设置明确高度。

<template>
  <div id="web3d" ref="viewer"></div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { App } from 'tz-stl-design';

const viewer = ref<HTMLDivElement | null>(null);
let app: App | undefined;

const onDimensionLabelClick = (payload: DimensionLabelClickPayload) => {
  console.log('点击尺寸标注:', payload.label, payload.category, payload.dimensionValue);
};

onMounted(async () => {
  if (!viewer.value) {
    throw new Error('3D 容器尚未挂载');
  }

  app = new App();
  app.eventMgr.center.on('dimension-label-click', onDimensionLabelClick);

  try {
    await app.apiMgr.import('xxx/models/tool.stl', 'xxx/models/tool.json');
  } catch (error) {
    console.error('刀具模型加载失败:', error);
  }
});

onBeforeUnmount(() => {
  if (!app) return;

  app.eventMgr.center.off('dimension-label-click', onDimensionLabelClick);
  app.clean();
  app.sceneMgr.engine.stopRenderLoop();
  app.sceneMgr.scene.dispose();
  app.sceneMgr.engine.dispose();
  app = undefined;
});
</script>

<style scoped>
#web3d {
  width: 100%;
  height: 600px;
  overflow: hidden;
  position: relative;
}
</style>

将示例路径替换为项目可访问的模型地址。跨域托管 STL/JSON 时,服务器需要允许页面来源访问。组件销毁时示例会移除业务事件监听并释放场景资源;目前场景初始化注册的 window.resize 监听器没有对应的移除 API。如果 Vue 路由会反复创建/销毁此组件,建议将 3D 视图放在生命周期稳定的父级中,避免反复创建 App。

ApiManager 接口说明

app.apiMgr 是面向前端的高层接口。它将 STL 导入、模型变换、相机适配和刀具尺寸标注组合为完整流程;业务页面通常应优先调用这里的方法,而不是自行拼接底层 Tool3d 工具。

| 方法 | 签名 | 返回值 | 用途 | | --- | --- | --- | --- | | import | import(stlUrl?: string, jsonUrl?: string) | Promise<void> | 导入匹配的 STL 和刀具 JSON,并自动生成尺寸标注。 | | clear | clear() | void | 清除所有由 STL 工具导入的网格和所有尺寸标注。 |

调试模型面板

在应用中按 Ctrl+D 可打开或关闭调试模型面板,也可以使用面板中的“退出”按钮关闭。选择钻头、铣刀、铰刀或丝锥及其具体型号后,点击“加载模型”会先清空当前模型和尺寸标注,再从测试资源目录导入对应的 STL 与 JSON 文件。面板显示时,可按 ↑ 或 ↓ 在当前产品目录的上一个或下一个型号间循环切换,并立即加载所选模型;加载期间不会响应方向键。

import(stlUrl?, jsonUrl?)

导入 STL、应用默认变换、适配相机,再读取 JSON 并根据刀具参数生成尺寸标注。

await app.apiMgr.import('xxx/models/tool.stl', 'xxx/models/tool.json');

| 参数 | 类型 | 说明 | | --- | --- | --- | | stlUrl | string,可选 | STL 文件 URL。 | | jsonUrl | string,可选 | 与 STL 对应的刀具参数 JSON URL。 |

两个参数省略时会使用代码内置的示例 STL/JSON 地址。实际项目建议明确传入一对匹配的 URL。import() 返回 Promise<void>,不返回网格对象;STL 或 JSON 加载失败时 Promise 会 reject,应使用 try/catch 处理。JSON 需符合 ToolData 数据结构。

该高层导入流程假设 STL 模型的尺寸单位为毫米,会将网格缩放 0.001 后按米制坐标显示;JSON 刀具尺寸按毫米读取。不要将已按米建模的 STL 不经换算传给此流程。

调用时不会自动移除之前导入的模型或标注。加载另一把刀具前应先调用 app.apiMgr.clear(),再等待新的 import() 完成:

async function loadTool(stlUrl: string, jsonUrl: string) {
  app.apiMgr.clear();

  try {
    await app.apiMgr.import(stlUrl, jsonUrl);
  } catch (error) {
    console.error('刀具模型加载失败:', error);
  }
}

clear()

释放 Tool3d.stl 已登记的 STL 网格,并释放 Tool3d.dimension 已创建的全部尺寸标注:

app.apiMgr.clear();

该方法不会销毁场景、相机、默认灯光或渲染引擎;清空刀具后仍可继续调用 import()。它也不会释放通过 mesh、material、texture 等其他工具单独创建的资源。

事件:dimension-label-click

当用户在 Babylon.js 场景中按下尺寸标注的文字标签时,应用会通过 app.eventMgr.center 同步触发 dimension-label-click。只有由 app.sceneMgr.tool3d.dimension.create() 创建的标注文字会触发该事件;尺寸线、箭头、STL 模型及其他网格不会触发它。

事件名常量为 DIMENSION_LABEL_CLICK_EVENT,其值为 'dimension-label-click'。未引入常量时可直接使用该字符串:

import { DIMENSION_LABEL_CLICK_EVENT } from './src/core/managers/eventManager';

const onDimensionLabelClick = (payload: DimensionLabelClickPayload) => {
  console.log(payload.label, payload.category);
};

app.eventMgr.center.on(DIMENSION_LABEL_CLICK_EVENT, onDimensionLabelClick);

事件负载为单个对象:

interface DimensionLabelClickPayload {
  /** 尺寸标签当前显示的文本,例如 "10"、"∅6" 或 "118°"。 */
  label: string;
  /** 创建标注时传入的 category;未传入时为 "default"。 */
  category: string;
  /**
   * 与标注关联的刀具尺寸参数。由 ApiManager 根据 ToolData 创建的参数标注会提供该值;
   * 静态自定义标注则可能为 undefined。
   */
  dimensionValue?: DimensionValue;
  /** 根据当前尺寸参数仅刷新标签文字和公差,不重建尺寸线、箭头或标注位置。 */
  updateDisplayContent(): void;
}

category 用于让前端区分业务尺寸。由 ApiManager.import() 自动生成的刀具参数标注会传出对应的 dimensionValue。监听器可以直接修改该对象,再调用 updateDisplayContent() 立即刷新标签:

const onDimensionLabelClick = ({ category, dimensionValue, updateDisplayContent }: DimensionLabelClickPayload) => {
  if (category !== 'L' || !dimensionValue) return;

  dimensionValue.value = 100;
  dimensionValue.tol.top = 0.02;
  dimensionValue.tol.low = -0.02;
  updateDisplayContent();
};

updateDisplayContent() 只会重绘标签文字与公差;它不会根据新的尺寸值调整标注几何、引出线或箭头位置。如需重新计算标注位置,应清除并重新导入模型。

创建自定义标注时应为需要交互的尺寸传入稳定的业务标识。若需要支持参数编辑,则同时传入 dimensionValue 和 getDisplayContent:

import { Vector3 } from 'babylonjs';

const dimensionValue: DimensionValue = {
  value: 50,
  tol: { type: 0, top: 0, low: 0 },
};

app.sceneMgr.tool3d.dimension.create({
  kind: 'linear',
  start: new Vector3(0, 0, 0),
  end: new Vector3(0.05, 0, 0),
  info: String(dimensionValue.value),
  category: 'overall-length',
  dimensionValue,
  getDisplayContent: () => ({
    label: String(dimensionValue.value),
  }),
  direction: '-z',
});
const onDimensionLabelClick = ({ category, dimensionValue, updateDisplayContent }: DimensionLabelClickPayload) => {
  if (category !== 'overall-length' || !dimensionValue) return;
  
  dimensionValue.value = 60;
  updateDisplayContent();
};

app.eventMgr.center.on('dimension-label-click', onDimensionLabelClick);

// 解绑时必须传入与 on() 相同的函数引用。
app.eventMgr.center.off('dimension-label-click', onDimensionLabelClick);

事件由 Node.js EventEmitter 分发,同一个标签按下时会依次执行所有已注册监听器;监听器中的异常会影响当前事件调用链。因此监听器应只处理 UI 状态或业务逻辑,异步操作应自行处理错误。Vue 组件应在 onMounted 后注册、在 onBeforeUnmount 中使用相同的函数引用调用 off(),避免组件销毁后仍响应事件。

刀具资源约定

刀具资源位于 资料/3D材料/3D材料/材料/,按铰刀、丝锥、铣刀、钻头等类型分层。资源通常包括二维 CAD 图纸(DWG)、刀具参数/模型描述(JSON)和三维模型(STL)。新增资源时,应保持同一刀具名称在各类型目录间对应,并沿用所在类别已有的目录大小写及命名方式。