场景图节点 接口文档

概述

INodeData 是场景图节点的核心数据接口,用于描述场景图中各种类型节点的完整数据结构。该接口定义了节点的基本属性、状态、渲染信息、动画数据和元数据等。

接口定义

interface INodeData {
    guid: Iguid;
    name: string;
    type: "DOCUMENT" | "VIEWER" | "LAYER" | "FEATURE" | "CONTAINER";
    parentIndex: INodeParentIndex;
    visible: boolean;
    locked: boolean;
    render?: INodeRenderData | null | undefined;
    animation?: INodeAnimationDataItem[];
    meta: Record<string, any>;
}

属性详解

guid

  • 类型: Iguid
  • 必需: 是
  • 说明: 节点的全局唯一标识符
  • 详情:
    • 由 localId(本地唯一标识符)和 sessionId(会话唯一标识符)组成
    • 用于在整个场景图中唯一标识一个节点
    • 格式:sessionId:localId

Iguid 接口定义:

interface Iguid {
    localId: number; // 本地唯一标识符
    sessionId: number; // 会话唯一标识符
}

name

  • 类型: string
  • 必需: 是
  • 说明: 节点的显示名称
  • 详情:
    • 用于在用户界面中显示节点名称
    • 可以是任意字符串,不要求唯一性
    • 建议使用有意义的描述性名称

type

  • 类型: "DOCUMENT" | "VIEWER" | "LAYER" | "FEATURE" | "CONTAINER"
  • 必需: 是
  • 说明: 节点的类型标识
  • 详情:
    • DOCUMENT: 文档根节点,通常作为场景图的顶层节点
    • VIEWER: 视图节点,代表一个三维视图实例
    • LAYER: 图层节点,用于组织和管理场景元素
    • FEATURE: 要素节点,表示具体的可视化对象(如模型、标注等)
    • CONTAINER: 容器节点,用于逻辑分组和组织子节点

parentIndex

  • 类型: INodeParentIndex
  • 必需: 是
  • 说明: 父节点索引信息,用于确定节点在场景图中的位置
  • 详情:
    • 通过父节点的 GUID 和在父节点中的位置来定位节点
    • position 使用小数索引系统,支持灵活的节点排序和插入

INodeParentIndex 接口定义:

interface INodeParentIndex {
    guid: Iguid; // 父节点的唯一标识符
    position: string; // 自身在父节点中的位置(小数索引)
}

小数索引说明:

  • 使用字符串表示的小数来确定节点顺序
  • 例如:"1", "2", "1.5" 等
  • 可以在任意两个节点之间插入新节点,无需重新排序

visible

  • 类型: boolean
  • 必需: 是
  • 说明: 节点的可见性状态
  • 详情:
    • true: 节点及其子节点可见
    • false: 节点及其子节点隐藏
    • 影响节点的渲染行为

locked

  • 类型: boolean
  • 必需: 是
  • 说明: 节点的锁定状态
  • 详情:
    • true: 节点被锁定,不可编辑或交互
    • false: 节点可以正常编辑和交互
    • 用于保护重要节点不被意外修改

render

  • 类型: INodeRenderData | null | undefined
  • 必需: 否(可选)
  • 说明: 节点的渲染数据配置
  • 详情:
    • 定义节点如何在场景中渲染
    • 包含几何信息、渲染类型和渲染属性
    • null 或 undefined 表示该节点不直接参与渲染(如容器节点)

INodeRenderData 接口定义:

interface INodeRenderData {
    type: string; // 渲染类型
    geometry?: Geometry; // 几何形状 (GeoJSON)
    properties: Record<string, any>; // 渲染属性
}

animation

  • 类型: INodeAnimationDataItem[]
  • 必需: 否(可选)
  • 说明: 节点的动画数据集合
  • 详情:
    • 存储节点的所有动画配置
    • 每个动画项包含动画名称和动作序列
    • 支持多个动画并存

INodeAnimationDataItem 接口定义:

interface INodeAnimationDataItem {
    name: string; // 动画名称
    actions: INodeAnimationActionData[]; // 动画动作序列
}

INodeAnimationActionData 接口定义:

interface INodeAnimationActionData {
    duration: number; // 动画持续时间(毫秒)
    [key: string]: any; // 其他动画属性(如 transform, keyframes 等)
}

meta

  • 类型: Record<string, any>
  • 必需: 是
  • 说明: 节点的元数据字典
  • 详情:
    • 存储与节点相关的任意自定义数据
    • 可用于扩展节点功能而无需修改接口定义
    • 常见用途:
      • 业务数据关联
      • 自定义属性
      • 临时状态存储
      • 插件数据

常见 meta 字段示例:

{
    // 业务 ID
    businessId: "building-001",

    // 创建信息
    createdAt: "2026-01-16T10:00:00Z",
    createdBy: "user123",

    // 自定义标签
    tags: ["important", "checked"],

    // 扩展属性
    customProperties: {
        floor: 3,
        area: 120.5,
        category: "residential"
    }
}

相关类型

Geometry (from GeoJSON)

type Geometry
    = | Point
        | MultiPoint
        | LineString
        | MultiLineString
        | Polygon
        | MultiPolygon
        | GeometryCollection;

来自 GeoJSON 规范,用于描述地理或空间几何形状。


使用示例

示例 1: 创建文档根节点

const documentNode: INodeData = {
    guid: {
        localId: 0,
        sessionId: 1
    },
    name: "根文档",
    type: "DOCUMENT",
    parentIndex: {
        guid: { localId: -1, sessionId: -1 }, // 根节点没有父节点
        position: "0"
    },
    visible: true,
    locked: false,
    meta: {
        createdAt: new Date().toISOString(),
        version: "1.0.0"
    }
};

示例 2: 创建图层节点

const layerNode: INodeData = {
    guid: {
        localId: 1,
        sessionId: 1
    },
    name: "建筑图层",
    type: "LAYER",
    parentIndex: {
        guid: { localId: 0, sessionId: 1 }, // 父节点是文档根节点
        position: "1"
    },
    visible: true,
    locked: false,
    render: {
        type: "3DTILES",
        properties: {
            url: "xxxx"
        }
    },
    meta: {
        description: "所有建筑模型",
        category: "building"
    }
};

示例 3: 创建带渲染数据的要素节点

const featureNode: INodeData = {
    guid: {
        localId: 2,
        sessionId: 1
    },
    name: "点测量",
    type: "FEATURE",
    parentIndex: {
        guid: { localId: 1, sessionId: 1 }, // 父节点是图层
        position: "1"
    },
    visible: true,
    locked: false,
    render: {
        type: "PointMeasurement",
        geometry: {
            type: "Point",
            coordinates: [116.397, 39.908, 0]
        },
        properties: {}
    },
    meta: {
        buildingId: "BLD-001",
        height: 50.0,
        floors: 15
    }
};
### 示例 4: 创建容器节点(逻辑分组) ```typescript const containerNode: INodeData = { guid: { localId: 4, sessionId: 1 }, name: "一楼房间组", type: "CONTAINER", parentIndex: { guid: { localId: 2, sessionId: 1 }, position: "1" }, visible: true, locked: false, render: undefined, // 容器节点通常不需要渲染数据 meta: { floor: 1, roomCount: 8 } };

节点类型关系

DOCUMENT (文档根节点) └── VIEWER (视图节点) ├── FEATURE (要素节点) └── CONTAINER (容器节点) └── LAYER (图层节点)

最佳实践

1. GUID 管理

  • 使用 GUID.gen() 生成新的唯一标识符
  • 避免手动创建 GUID,以防冲突
  • 在序列化/反序列化时正确处理 GUID

2. 小数索引使用

  • 建议使用fractional-indexing库生成

3. 渲染数据优化

  • 仅在实际需要渲染的节点上设置 render 属性
  • 容器和组织节点应将 render 设为 undefined
  • 复用相同的渲染配置以节省内存

4. 元数据设计

  • 使用清晰的命名约定
  • 避免在 meta 中存储大量数据
  • 考虑使用嵌套结构组织相关元数据

5. 可见性和锁定

  • 父节点的 visible 为 false 时,所有子节点都不可见
  • 锁定状态不自动继承,需在业务逻辑中处理
  • 修改节点状态时,考虑对子节点的影响

类型检查辅助

// 类型守卫函数
function isValidNodeType(type: string): type is INodeData["type"] {
    return ["DOCUMENT", "VIEWER", "LAYER", "FEATURE", "CONTAINER"].includes(type);
}

// 节点验证函数
function validateNodeData(data: any): data is INodeData {
    return (
        data
        && typeof data === "object"
        && "guid" in data
        && "name" in data
        && typeof data.name === "string"
        && "type" in data
        && isValidNodeType(data.type)
        && "parentIndex" in data
        && "visible" in data
        && typeof data.visible === "boolean"
        && "locked" in data
        && typeof data.locked === "boolean"
        && "meta" in data
        && typeof data.meta === "object"
    );
}