BaseTree

BaseTree

这类节点拥有树的结构,除了根以外每个节点拥有一个父节点。我们把每个节点的父节点称为其前驱,把每个节点的子节点称为其后继

树上的每个节点被抽象为一个个 node,树上的每条连边被抽象为一条条 link。

这是一个抽象类,不应该被实例化。

API 列表

class BaseTree extends SD2DNode {
    // 与 node 有关的查询
    nodes(): Array<SDNode>;
    nodesId(): Array<string>;
    element(node: number | string | SDNode): SDNode | undefined;
    root(): SDNode;
    rootId(): string;
    nodeId(node: number | string | SDNode): string | undefined;
    
    // 与 link 有关的查询
    links(): Array<SDNode>;
    element(source: number | string | SDNode, target: number | string | SDNode): SDNode | undefined;
    source(link: SDNode): SDNode | undefined;
    target(link: SDNode): SDNode | undefined;
    sourceId(link: SDNode): string | undefined;
    targetId(link: SDNode): string | undefined;

    // 筛查
    findNode(condition: (node: SDNode, id: string) => boolean): SDNode | undefined;
    findNodes(condition: (node: SDNode, id: string) => boolean): Array<SDNode>;
    findLink(condition: (link: SDNode, sourceId: string, targetId: string) => boolean): SDNode | undefined;
    findLinks(condition: (link: SDNode, sourceId: string, targetId: string) => boolean): Array<SDNode>;
    findNodeById(id: number | string): SDNode | undefined;
    findLinkById(sourceId: number | string, targetId: number | string): SDNode | undefined;

    // 基础信息查询
    inLink(node: number | string | SDNode): SDNode | undefined;
    outLinks(node: number | string | SDNode): Array<SDNode>;
    children(node: number | string | SDNode): Array<SDNode>;
    father(node: number | string | SDNode): SDNode | undefined;
    fatherId(node: number | string | SDNode): string | undefined;
    depth(): number;
    depth(node: number | string | SDNode): number;
    ancestor(node: number | string | SDNode, kth: number): SDNode | undefined;
    ancestorId(node: number | string | SDNode, kth: number): string;

    // 高级信息查询
    lca(x: number | string | SDNode, y: number | string | SDNode): SDNode;
    lcaId(x: number | string | SDNode, y: number | string | SDNode): string;
    nodesInSubtree(node: number | string | SDNode): Array<SDNode>;
    linksInSubtree(node: number | string | SDNode): Array<SDNode>;
    forEachNodeInSubtree(node: number | string | SDNode, callback: (node: SDNode, id: string) => void): this;
    forEachLinkInSubtree(node: number | string | SDNode, callback: (link: SDNode, sourceId: string, targetId: string) => void): this;
    nodesOnPath(source: number | string | SDNode, target: number | string | SDNode): Array<SDNode>;
    linksOnPath(source: number | string | SDNode, target: number | string | SDNode): Array<SDNode>;
    forEachNodeOnPath(source: number | string | SDNode, target: number | string | SDNode, callback: (node: SDNode, id: string) => void): this;
    forEachLinkOnPath(source: number | string | SDNode, target: number | string | SDNode, callback: (link: SDNode, sourceId: string, targetId: string) => void): this;
    forEachNode(callback: (node: SDNode, id: string) => void): this;
    forEachLink(callback: (link: SDNode, sourceId: string, targetId: string) => void): this;

    // 树的结构管理
    root(id: number | string, value?: any): this;
    link(sourceId: number | string, targetId: number | string, value?: any): this;
    newNode(id: number | string, value?: any): this;
    newNodeFromExistValue(id: number | string, value: SDNode): this;
    newNodeFromExistElement(id: number | string, element: SDNode): this;
    newLink(sourceId: number | string, targetId: number | string, value?: any): this;
    newLinkFromExistValue(sourceId: number | string, targetId: number | string, value: SDNode): this;
    newLinkFromExistElement(sourceId: number | string, targetId: number | string, element: SDNode): this;
    cut(sourceId: number | string, targetId: number | string): this;

    // 元素属性管理
    opacity(node: number | string | SDNode): number;
    opacity(node: number | string | SDNode, opacity: number): this;
    nodeOpacity(node: number | string | SDNode): number;
    nodeOpacity(node: number | string | SDNode, opacity: number): this;
    opacity(source: number | string | SDNode, target: number | string | SDNode): number;
    opacity(source: number | string | SDNode, target: number | string | SDNode, opacity: number): this;
    linkOpacity(source: number | string | SDNode, target: number | string | SDNode): number;
    linkOpacity(source: number | string | SDNode, target: number | string | SDNode): number;
    color(color: SDColor): this;
    color(node: number | string | SDNode): PacketColor;
    color(node: number | string | SDNode, color: SDColor): this;
    color(source: number | string | SDNode, target: number | string | SDNode): PacketColor;
    color(source: number | string | SDNode, target: number | string | SDNode, color: SDColor): this;
    text(node: number | string | SDNode): string;
    text(node: number | string | SDNode, text: string): this;
    nodeText(node: number | string | SDNode): string;
    nodeText(node: number | string | SDNode, text: string): this;
    text(source: number | string | SDNode, target: number | string | SDNode): string;
    text(source: number | string | SDNode, target: number | string | SDNode, text: string): this;
    linkText(source: number | string | SDNode, target: number | string | SDNode): string;
    linkText(source: number | string | SDNode, target: number | string | SDNode, text: string): this;
    intValue(node: number | string | SDNode): number;
    intValue(source: number | string | SDNode, target: number | string | SDNode): number;
    value(node: number | string | SDNode): SDNode | undefined;
    value(node: number | string | SDNode, value: any): this;
    nodeValue(node: number | string | SDNode): SDNode | undefined;
    nodeValue(node: number | string | SDNode, value: any): this;
    value(source: number | string | SDNode, target: number | string | SDNode): SDNode | undefined;
    value(source: number | string | SDNode, target: number | string | SDNode, value: any): this;
    linkValue(source: number | string | SDNode, target: number | string | SDNode): SDNode | undefined;
    linkValue(source: number | string | SDNode, target: number | string | SDNode, value: any): this;
}

与 node 相关的查询

class BaseTree {
    nodes(): Array<SDNode>; // 获取所有的节点
    nodesId(): Array<string>; // 获取所有的节点索引
    element(node: number | string | SDNode): SDNode | undefined; // 获取目标节点
    root(): SDNode; // 获取树根节点
    rootId(): string; // 获取树根索引
    nodeId(node: number | string | SDNode): string | undefined; // 获取目标节点对应的索引
}

这类方法可以用来查询一些 node 信息。

与 link 相关的查询

class BaseTree {
    links(): Array<SDNode>; // 获取所有的连边
    element(source: number | string | SDNode, target: number | string | SDNode): SDNode | undefined; // 获取目标连边
    source(link: SDNode): SDNode | undefined; // 获取目标连边的起点(即边上的父节点)
    target(link: SDNode): SDNode | undefined; // 获取目标连边的终点(即边上的子节点)
    sourceId(link: SDNode): string | undefined; // 获取目标连边的起点索引
    targetId(link: SDNode): string | undefined; // 获取目标连边的终点索引
}

这类方法可以用来查询一些 link 信息。

筛查

class BaseTree {
    findNode(condition: (node: SDNode, id: string) => boolean): SDNode | undefined; // 查找第一个符合条件的节点
    findNodes(condition: (node: SDNode, id: string) => boolean): Array<SDNode>; // 查找所有符合条件的节点
    findLink(condition: (link: SDNode, sourceId: string, targetId: string) => boolean): SDNode | undefined; // 查找第一条符合条件的连边
    findLinks(condition: (link: SDNode, sourceId: string, targetId: string) => boolean): Array<SDNode>; // 查找所有符合条件的连边
    findNodeById(id: number | string): SDNode | undefined; // 根据节点索引查找节点
    findLinkById(sourceId: number | string, targetId: number | string): SDNode | undefined; // 根据连边索引查找连边
}

各种各样的筛选 node 和 link 的方法。

基础信息查询

class BaseTree {
    inLink(node: number | string | SDNode): SDNode | undefined; // 获取目标节点的入边(即父节点指向其的连边)
    outLinks(node: number | string | SDNode): Array<SDNode>; // 获取目标节点的出边(即其指向所有子节点的连边)
    children(node: number | string | SDNode): Array<SDNode>; // 获取目标节点的所有子节点
    father(node: number | string | SDNode): SDNode | undefined; // 获取目标节点的父节点
    fatherId(node: number | string | SDNode): string | undefined; // 获取目标节点的父节点索引
    depth(): number; // 获取树的深度(只有一个节点的树的深度为 1)
    depth(node: number | string | SDNode): number;  // 获取目标节点的深度
    ancestor(node: number | string | SDNode, kth: number): SDNode | undefined; // 获取目标节点的 k 级祖先
    ancestorId(node: number | string | SDNode, kth: number): string; // 获取目标节点的 k 级祖先的索引
}

包括深度信息,父节点信息,子节点信息,祖先信息。

高级信息查询

class BaseTree {
    lca(x: number | string | SDNode, y: number | string | SDNode): SDNode; // 查询最近公共祖先
    lcaId(x: number | string | SDNode, y: number | string | SDNode): string; // 查询最近公共祖先索引
    nodesInSubtree(node: number | string | SDNode): Array<SDNode>; // 获取子树内所有节点
    linksInSubtree(node: number | string | SDNode): Array<SDNode>; // 获取子树内所有连边
    forEachNodeInSubtree(node: number | string | SDNode, callback: (node: SDNode, id: string) => void): this; // 遍历子树内所有节点
    forEachLinkInSubtree(node: number | string | SDNode, callback: (link: SDNode, sourceId: string, targetId: string) => void): this; // 遍历子树内所有连边
    nodesOnPath(source: number | string | SDNode, target: number | string | SDNode): Array<SDNode>; // 获取路径上所有节点(并按照路径顺序依次存放)
    linksOnPath(source: number | string | SDNode, target: number | string | SDNode): Array<SDNode>; // 获取路径上所有连边(并按照路径顺序依次存放)
    forEachNodeOnPath(source: number | string | SDNode, target: number | string | SDNode, callback: (node: SDNode, id: string) => void): this; // 遍历路径上所有节点(按照路径顺序依次遍历)
    forEachLinkOnPath(source: number | string | SDNode, target: number | string | SDNode, callback: (link: SDNode, sourceId: string, targetId: string) => void): this; // 遍历路径上所有连边(按照路径顺序依次遍历)
    forEachNode(callback: (node: SDNode, id: string) => void): this; // 遍历树上所有节点
    forEachLink(callback: (link: SDNode, sourceId: string, targetId: string) => void): this; // 遍历树上所有连边
}

一些本来可能需要写一个函数完成的任务,在这里也进行了封装简化。

树的结构管理

class BaseTree {
    root(id: number | string, value?: any): this; // 设置树根
    link(sourceId: number | string, targetId: number | string, value?: any): this; // 连接两个节点
    newNode(id: number | string, value?: any): this; // 新建一个节点
    newNodeFromExistValue(id: number | string, value: SDNode): this; // 新建一个节点
    newNodeFromExistElement(id: number | string, element: SDNode): this; // 新建一个节点
    newLink(sourceId: number | string, targetId: number | string, value?: any): this; // 新建一条连边
    newLinkFromExistValue(sourceId: number | string, targetId: number | string, value: SDNode): this; // 新建一条连边
    newLinkFromExistElement(sourceId: number | string, targetId: number | string, element: SDNode): this; // 新建一条连边
    cut(sourceId: number | string, targetId: number | string): this; // 删除一条连边
}

让我们试着构建一棵树:

const svg = sd.svg();
const tree1 = new sd.Tree(svg).root(1).link(1, 2).link(1, 3); // 可以先设置树根再连边
const tree2 = new sd.Tree(svg).link(1, 2).link(1, 3).dx(250); // 如果直接连边,则会自动推断树根
const tree3 = new sd.Tree(svg).newNode(1).newNode(2).newLink(1, 2).newNode(3).newLink(1, 3).dx(500); // 也可以先建立节点再连边

试一试,三棵树的构建都是正确的。link 和 newLink 的区别在于,对 link 而言,它会检查连边两端的节点是否存在,如果不存在则会自动创建,而 newLink 只完成连边的创建这一个任务。link 相比于 newLink 更加智能。

如果我们希望在创建节点的时候,每个节点上显示的不是其索引,而是别的什么东西,则可以这样做:

const svg = sd.svg();
const C = sd.color();
const tree = new sd.Tree(svg);
tree.newNode(1, new sd.Rect(svg).color(C.orange));
tree.newNode(2, new sd.Circle(svg).color(C.blue));
tree.newNode(3, new sd.Image(svg).href("/img/gift.png"));
tree.link(1, 2).link(1, 3);

同样,我们可以指定连边上显示一些额外信息:

const svg = sd.svg();
const tree = new sd.Tree(svg);
tree.link(1, 2, new sd.Image(svg).href("/img/gift.png").scale(0.5));
tree.newNode(3).newLink(1, 3, new sd.Image(svg).href("/img/snowflake.png").scale(0.5));

目前树的结构错误检测尚不智能,需要用户自行保证输入的树的结构是正确的。

元素属性管理

这里提供了一些方法,对节点和连边的各项属性进行快捷操作。

opacity

class BaseTree {
    opacity(node: number | string | SDNode): number;
    opacity(node: number | string | SDNode, opacity: number): this;
    nodeOpacity(node: number | string | SDNode): number;
    nodeOpacity(node: number | string | SDNode, opacity: number): this;
    opacity(source: number | string | SDNode, target: number | string | SDNode): number;
    opacity(source: number | string | SDNode, target: number | string | SDNode, opacity: number): this;
    linkOpacity(source: number | string | SDNode, target: number | string | SDNode): number;
    linkOpacity(source: number | string | SDNode, target: number | string | SDNode): number;
}

color

class BaseTree {
    color(color: SDColor): this;
    color(node: number | string | SDNode): PacketColor;
    color(node: number | string | SDNode, color: SDColor): this;
    color(source: number | string | SDNode, target: number | string | SDNode): PacketColor;
    color(source: number | string | SDNode, target: number | string | SDNode, color: SDColor): this;
}

text

class BaseTree {
    text(node: number | string | SDNode): string;
    text(node: number | string | SDNode, text: string): this;
    nodeText(node: number | string | SDNode): string;
    nodeText(node: number | string | SDNode, text: string): this;
    text(source: number | string | SDNode, target: number | string | SDNode): string;
    text(source: number | string | SDNode, target: number | string | SDNode, text: string): this;
    linkText(source: number | string | SDNode, target: number | string | SDNode): string;
    linkText(source: number | string | SDNode, target: number | string | SDNode, text: string): this;
}

intValue

class BaseTree {
    intValue(node: number | string | SDNode): number;
    intValue(source: number | string | SDNode, target: number | string | SDNode): number;
}

value

class BaseTree {
    value(node: number | string | SDNode): SDNode | undefined;
    value(node: number | string | SDNode, value: any): this;
    nodeValue(node: number | string | SDNode): SDNode | undefined;
    nodeValue(node: number | string | SDNode, value: any): this;
    value(source: number | string | SDNode, target: number | string | SDNode): SDNode | undefined;
    value(source: number | string | SDNode, target: number | string | SDNode, value: any): this;
    linkValue(source: number | string | SDNode, target: number | string | SDNode): SDNode | undefined;
    linkValue(source: number | string | SDNode, target: number | string | SDNode, value: any): this;
}