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

@faicad/animation

v0.2.0

Published

FAICAD animation: assembly schema + three.js scene builder + MIMIC runtime (define + play in one package)

Downloads

119

Readme

@faicad/animation

CAD 装配 → 浏览器实时动画,一个包搞定。

faijs 建模 → 装配定义 → three.js 嵌套枢轴树 → MIMIC 数值求值 → 实时播放。 定义与播放在同一个 TypeScript 工程里,没有 GLB 中间格式,没有服务端。

model.fai.js(几何 + 装配 + 运动学)  →  buildModel  →  three 场景树  →  播放
   建模端(anim.* 子包)               @faicad/animation(播放端)

写模型脚本

建模端 = 一个 .fai.js:几何 + 零件装配 + 运动学全写在同一个脚本里 (对标 python 侧的 gen.py)。渲染它(buildModel)是播放端的事, 播放时长 duration 也属播放端,模型脚本里没有。

import * as anim from 'fai-animation'

let base = cad.cylinder(15 * MM, 8 * MM, { centered: true })
let arm  = cad.box(60 * MM, 8 * MM, 8 * MM, { centered: true })

base.setColor('#555555')                       // 外观是几何对象的方法,不是装配 API
arm.setColor('#ff8800')

// 安装位姿的唯一来源:cad.assembly 约束解算(脚本不写任何安装位置)
let shoulder_asm = cad.assembly({
  name: 'shoulder_asm',
  members: [base, arm],
  constraints: [
    { type: 'fixed', part: 'base' },            // 基座锚定在建模位姿
    { type: 'coincident',                       // 臂根铰点贴基座顶面 (0,0,4)
      a: { part: 'arm', point: [0, 0, 0] },
      b: { part: 'base', point: [0, 0, 4 * MM] } },
    { type: 'fixedRotation', part: 'arm' },     // 按最终姿态建模 → 钉恒等朝向
  ],
})

// 运动:anim.joint 只规定结构与运动参数;rest pose 由上面的装配解回填
anim.joint('shoulder', 'REVOLUTE', {           // child 可传变量(等价 child: 'arm')
  child: arm, axis: [0, 0, 1],
})
anim.declareMotions('wave', [anim.driver('shoulder', 90)])

脚本三节形态:几何 cad.* → 安装 cad.assembly → 运动 anim.*。 anim.joint 不再接受 basePosition(传已删字段当场抛错)。

完整契约见 docs/faijs-script-contract.md (anim.* 签名表、Shape 方法与颜色入参、faijs 方言的「能/不能」、单位与坐标、 安装位姿的约束配方、宿主调用序)。可直接运行的最小样例:demos/belt/model.fai.js。

写脚本时最容易踩的五条:颜色只收 hex 字符串或 0–1 三元组(数字 0xff8800 抛错); 一行一条语句;顶层 op 实参里不能出现 Math.*;cad.assembly(...) 必须赋值给变量 (裸语句的结果不被收集);裸 # 注释行过不了解析器(用 //)。


30 秒上手

import {
  buildAssemblyScene, compileMotion, GSAPMotionController, MotionControlPanel,
} from '@faicad/animation'
import type { AssemblySpec, MotionSpec } from '@faicad/animation'

// 1. 装配拓扑:关节连接谁、绕哪根轴
const assembly: AssemblySpec = {
  version: 2,
  joints: [
    { name: 'pinion_joint', type: 'REVOLUTE', parent: 'root', child: 'pinion', axis: [0, 0, 1] },
    { name: 'rack_joint',   type: 'PRISMATIC', parent: 'root', child: 'rack',   axis: [0, 1, 0],
      basePosition: [20, -62.83, 0] },
  ],
}

// 2. 运动行为:一个 DRIVER,其余 MIMIC
const motions: MotionSpec[] = [
  { name: 'pinion_joint', type: 'DRIVER', totalDegrees: 360 },
  { name: 'rack_joint',   type: 'MIMIC',
    inputs: [{ joint: 'pinion_joint', multiplier: Math.PI / 9 }] },  // πmz/360
]

// 3. 几何(faijs Shape → THREE.Mesh,这里假设已有)
const meshes = new Map([['pinion', pinionMesh], ['rack', rackMesh]])

// 4. 编译 + 建树
const { bundle, diagnostics } = compileMotion(assembly, motions, [], { duration: 4 })
if (diagnostics.length) console.warn(diagnostics)
const { root, nodeMap } = buildAssemblyScene({ meshes, assembly })
scene.add(root)

// 5. 播放
const controller = new GSAPMotionController(root, bundle, { nodeMap, autoPlay: true })
new MotionControlPanel({ container: document.getElementById('panel') })
  .setController(controller)

上手段演示的是播放层直用(宿主自带几何,不过模型脚本):basePosition / baseRotation 是 AssemblyJoint 的 rest pose 字段,手写 spec 时由作者给; 走 buildModel(.fai.js 脚本路径)时这两个字段由 cad.assembly 约束解自动 回填,脚本里不写。

跑 demo:

npm install
npm run dev            # → /demos/index.html

两条动画路径

| 路径 | 驱动方式 | 判据 | 适用 | |---|---|---|---| | A — 时间驱动 | GSAP 时钟 → TimeDrivenJoint.update(t) | 有 angularRange / linearRange,无 mimic | 独立关节摆动、滑动、钟摆 | | B — 数据流驱动 | MIMIC 引擎 + 同一时钟 | 有 mimic | 齿轮 / 皮带 / 凸轮 / 差速 / 行星轮系 |

写完整编译链时,DRIVER 也会被编译成 mimic: { inputs: [{joint: '_clock'}] }, 所以走 compileMotion() 的场景里全部关节都在 Path B。Path A 只在直接喂手写 bundle(历史 v1 .motion.json)时出现。


机构约束:传动比不手填

compileMotion() 接受声明式的 Coupling[],编译成线性约束方程组后高斯消元, 解出每个关节相对 driver 的速比,自动填进 mimic.inputs[].multiplier。

compileMotion(assembly, motions, [
  { kind: 'gearMesh', a: 'sun', b: 'planet', teethA: 18, teethB: 12, carrier: 'carrier' },
  { kind: 'gearMesh', a: 'planet', b: 'ring', teethA: 12, teethB: 42, internal: true, carrier: 'carrier' },
  { kind: 'fixed', joint: 'carrier' },
], { duration: 4 })

以 ring 为基准解出 sun = −2.3333 (= −Zr/Zs)、planet = 3.5 (= +Zr/Zp)、 carrier = 0——与手填系数一致,但不用手算。

支持的 coupling:fixed · gearMesh(外/内啮合,可带 carrier)· belt · rackPinion · cam(走 curve,不进方程组)· differential。

显式写的 MIMIC 优先级高于耦合推导的结果。约束欠定 / 过定会产生 diagnostics,但不阻断编译。

详见 docs/motion-contract.md。


目录结构

animation/
  src/
    index.ts               统一导出
    schema/
      assembly.ts          AssemblySpec  — 装配拓扑(作者层)
      motion.ts            MotionSpec / Coupling(作者层)
      bundle.ts            MotionBundle — 播放层契约 + v1/v2 兼容
    scene/
      scene-builder.ts     装配 → 嵌套 pivot 场景树
      shape-to-mesh.ts     faijs Shape → THREE.Mesh
      faijs-runtime.ts     faijs 脚本执行封装(浏览器 + node 双环境)
      fai-model-builder.ts buildModel(script):执行 .fai.js → mesh + 装配 + 运动学
    fai-animation/         faijs 扩展子包:`anim.*` 声明装配与运动学(LibLoader 注入)
    compile/
      motion-compile.ts    MotionSpec + Coupling → MotionBundle
      gear-ratios.ts       coupling → 约束方程
      gaussian-elimination.ts  约束方程组求解
    runtime/
      controller.ts        GSAPMotionController + TimeDrivenJoint
      joint-driver.ts      JointDriver(MIMIC 求值)
      control-panel.ts     MotionControlPanel(自包含播放面板)
    loader/
      glb-loader.ts        可选:读历史 GLB + FAICAD_motion 扩展
  demos/                   7 个可播放案例 + 共享骨架
  test/                    vitest 单测 + playwright e2e + GLB fixture
  docs/                    架构与契约文档
  scripts/ci.ps1           本地一键 CI

demos

npm run dev 后打开 /demos/index.html。

| Demo | 特性 | |---|---| | planetary_gear | 行星架公转,18/12/42 真 BREP 渐开线齿轮,Case1/Case2 | | planetary_gear2 | 行星架固定(背板支架),单 DRIVER + MIMIC 链 | | rack_pinion | 旋转 → 直线,πmz/360 推导系数 | | belt | 皮带 MIMIC 减速,轮上标记验证相位与转向 | | cam | 凸轮曲线表 → 挺杆位移,曲线插值 | | differential | 差速器双输入 MIMIC 叠加 | | walk_swing | Path A 纯时间驱动:重力余弦 + 往返三角波 |

建模端 = 一个 .fai.js:几何 + 零件装配 + 运动学全写在脚本里 (对标 python 侧的 gen.py)。渲染它(buildModel)是播放端的事, 播放时长 duration 也属播放端,模型脚本里没有。

每个 demo 目录四个文件:

demos/<name>/
  model.fai.js  建模端唯一文件:cad.* 出几何 + cad.assembly 定安装 + anim.joint/driver/mimic/declareMotions 定运动
  demo.ts       只 re-export:modelScript(?raw 导入)+ duration + title/subtitle
  page.ts       调 mountDemoPage(),声明标题 / 变体 / 取景
  index.html    只 import ./page.ts

URL 参数(供 e2e / 截图取证):?variant=case2 · ?t=2.5 定格 · ?zoom=1.6 · ?nogrid=1。


坐标系

Z-up · 角度 度 · 长度 mm · 默认旋转轴 [0, 0, 1]。 axis 定义在子节点局部坐标系下。

详见 docs/coordinate-system.md。


读历史 GLB(兼容路径)

本包不产 GLB,但能读 animation_gen(python)时代生成的 GLB + FAICAD_motion 扩展 / 独立 .motion.json:

import { loadGlb } from '@faicad/animation/loader'   // 可选入口,需显式引入

const { root, meta } = await loadGlb('/model.glb', '/model.motion.json')
const ctrl = new GSAPMotionController(root, meta)

./loader 不在主入口的导出面上。红线:3d_editor 不引入此入口。 test/fixtures/glb/ 里是真实的 python 产物 fixture,test/dual-compat.test.ts 持续验证双兼容。


脚本

| 命令 | 作用 | |---|---| | npm run dev | vite dev server,开 /demos/index.html | | npm run build | tsc → dist/(库产物) | | npm run build:site | vite MPA 构建 demos 静态站 → preview-dist/ | | npm run serve:site | 静态伺服 preview-dist/ | | npm test | vitest 单测 | | npm run test:e2e | playwright e2e(会自动起 vite :4173) |

本地 CI

pwsh scripts/ci.ps1

按顺序跑 tsc → vitest → playwright,任一步失败即报 fail 并继续后续步骤, 最后汇总。GitHub Actions 见 .github/workflows/ci.yml, 跑同样的命令链。


依赖

| 包 | 角色 | |---|---| | three >=0.160.0 | peer — 场景图、四元数 / 向量 | | gsap >=3.12.0 | peer — 动画时钟 | | @faicad/faijs >=0.33.6 | peer — 建模引擎(src/scene/faijs-*):Shape / cad.* / 外观 / CadQuery 选择器 | | @faicad/faijs-gears >=0.33.4 <0.34.5 | peer(可选)— BREP 渐开线齿轮 |

@faicad/faijs 与齿轮库从本地 faijs 兄弟仓的 tgz 安装(file: 依赖, 与 3d_editor 同线)。

齿轮库的版本上限与宿主契约:@faicad/faijs-gears 0.34.3 起是平台库(occt-only), 入口经 gearKernel() 取双句柄——l1 = getBrepApi()(宿主注入的 L1 适配器)+ occt = getOcctKernel()(occt 平台面,补 L1 无法表达的 loft 壳形态 / healWire / split / projectPointOnFace)。宿主经 registerOcctBrepEngine() 登记 occt 引擎后, 两面同时可用且 config.brepEngineId='occt',齿轮正常产出 BREP solid;未登记 occt 引擎时 gearKernel() 在执行前抛 E_BREP_UNSUPPORTED(不补桩、不静默降级)。上限 >=0.33.4 <0.34.5 由 test/gears-brep.test.ts 把守:各用例先经生产宿主路径 executeFaijsScript 引导,再直调库入口读原始 Result。

runtime/ 只依赖 three + gsap,不碰 faijs——宿主自带几何可以直接用播放层。

BREP 路径需要 occt-wasm,浏览器和 node(vitest)两条路都支持 (src/scene/faijs-runtime.ts 自动分流)。


文档

长期有效的内容文档:

| 文档 | 内容 | |---|---| | docs/faijs-script-contract.md | fai.js 脚本接口契约:anim.* 签名、Shape 方法、方言约束、宿主调用序 | | docs/motion-contract.md | 三层数据契约、Coupling 约束方程、编译流程、v1/v2 兼容 | | docs/runtime-architecture.md | 控制器 / MIMIC 引擎 / 播放面板的内部实现 | | docs/auto-pivot.md | 装配 → 枢轴树的自动插入规则与约定 | | docs/coordinate-system.md | Z-up 决策与落地位置 | | docs/lessons/rack-pinion-bugs.md | 齿条齿轮的 4 个历史 bug 与当前待办 | | SKILL.md | AI 助手上的速查卡 |

开发计划与方案(过程文档,非当前契约)在 docs/plans/:

| 文档 | 内容 | |---|---| | docs/plans/faijs-frontend-motion-plan.md | V2 技术方案(本仓的存在理由) | | docs/plans/2026-10-14-plan-api-boundary-and-script-contract.md | 装配/外观 API 边界与脚本契约(2026-10-14 方案) | | docs/plans/2026-10-15-plan-faijs-assembly-junction.md | 安装归 faijs cad.assembly、运动归 animation:结合点重构(2026-10-15 方案,已执行完毕) |