Skip to content

Commit

Permalink
feat: support delete node
Browse files Browse the repository at this point in the history
  • Loading branch information
zxch3n committed Jun 13, 2022
1 parent 027d7a3 commit 9e6d923
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 25 deletions.
14 changes: 14 additions & 0 deletions rust/crates/tidy-tree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ impl TidyTree {
self.map.insert(id, ptr);
}

pub fn remove_node(&mut self, id: usize) {
if self.is_empty() {
return;
}

if let Some(node) = self.map.get(&id) {
let node = unsafe { &mut *node.as_ptr() };
node.pre_order_traversal(|node| {
self.map.remove(&node.id);
});
node.parent_mut().unwrap().remove_child(id);
}
}

pub fn data(&mut self, id: &[usize], width: &[Coord], height: &[Coord], parent_id: &[usize]) {
for (i, &id) in id.iter().enumerate() {
let width = width[i];
Expand Down
7 changes: 7 additions & 0 deletions rust/crates/tidy-tree/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,13 @@ impl Node {
}
}

pub fn remove_child(&mut self, id: usize) {
let pos = self.children.iter().position(|node| node.id == id);
if let Some(index) = pos {
self.children.remove(index);
}
}

pub fn pre_order_traversal_with_depth_mut<F>(&mut self, mut f: F)
where
F: FnMut(&mut Node, usize),
Expand Down
4 changes: 4 additions & 0 deletions rust/crates/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ impl Tidy {
}
}

pub fn remove_node(&mut self, id: usize) {
self.0.remove_node(id);
}

pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
Expand Down
7 changes: 7 additions & 0 deletions src/TidyComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,15 @@ export const TidyComponent = ({ root, layoutType, updateTrigger }: Props) => {
};

func();
return () => {
layoutRef.current?.dispose();
layoutRef.current = undefined;
};
}, [root]);
useEffect(() => {
return () => {
renderRef.current?.dispose();
renderRef.current = undefined;
};
}, []);
useEffect(() => {
Expand Down
34 changes: 31 additions & 3 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,28 @@ export class Renderer extends Disposable {
this.rescale();
}

private clear() {
this.render.clear();
if (this.group) {
this.render.remove(this.group);
}
this.nodeMap.clear();
this.rectMap.clear();
}

private rescale() {
if (!this.root || !this.group) {
return;
}
const g = this.group;
const gBox = g.getBoundingRect();
const w = this.render.getWidth();
const h = this.render.getHeight();
const scale = Math.min(
this.render.getWidth() / (gBox.width + 20),
this.render.getHeight() / (gBox.height + 20),
Math.abs(w / -gBox.x / 2),
Math.abs(w / (gBox.width + gBox.x) / 2),
w / (gBox.width + 20),
h / (gBox.height + 50),
5,
);
g.animateTo({ scaleX: scale, scaleY: scale });
Expand Down Expand Up @@ -102,8 +115,9 @@ export class Renderer extends Disposable {
return;
}
const g = this.group;

const removedNodeIds = new Set<number>(this.rectMap.keys());
visit(this.root, (node) => {
removedNodeIds.delete(node.id);
if (!this.rectMap.has(node.id)) {
this.addNode(node, g, true);
return;
Expand Down Expand Up @@ -131,6 +145,20 @@ export class Renderer extends Disposable {
});
}
});

for (const id of removedNodeIds) {
const rect = this.rectMap.get(id)!;
this.group.remove(rect);
this.rectMap.delete(id);
const lines = (this.lineSourceMap.get(id) ?? []).concat(
this.lineTargetMap.get(id) ?? [],
);
for (const line of lines) {
this.group.remove(line.line);
}
this.lineSourceMap.delete(id);
this.lineTargetMap.delete(id);
}
this.rescale();
}
}
Expand Down
95 changes: 74 additions & 21 deletions src/stories/Tidy.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useCallback, useState } from 'react';
import React, {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import { Node } from '../tidy';
import { LayoutTypeStr, TidyComponent } from '../TidyComponent';
import { createNode, createTree, visit } from '../utils';
Expand All @@ -21,32 +27,35 @@ export default {
interface Props {
layoutType: LayoutTypeStr;
}

const root = createTree(200) as Node;
/**
* Primary UI component for user interaction
*/
export const TidyLayout = ({ layoutType, ...props }: Props) => {
export const TidyLayout = ({
layoutType,
num,
...props
}: Props & { num: number }) => {
const [updateTrigger, setUpdate] = useState(0);
const addNode = useCallback(() => {
let nodes: [Node, number][] = [];
visit(root, (node, depth) => {
if (node.children.length < 4) {
nodes.push([node, depth]);
}
});

nodes.sort((a, b) => -a[1] + b[1]);
if (nodes.length > 20) {
const depth = nodes[20][1];
nodes = nodes.filter(([_, d]) => d >= depth);
const [root, setRoot] = useState(() => {
return createTree(1);
});
const prevNum = useRef(0);
useLayoutEffect(() => {
if (prevNum.current == 0) {
setRoot(createTree(num));
} else if (num < prevNum.current) {
deleteRandomNode(root, prevNum.current - num);
} else if (num > prevNum.current) {
insertRandomNode(root, num - prevNum.current);
}
const node = nodes[(Math.random() * nodes.length) | 0][0];
const child = createNode();
child.parentId = node.id;
node.children.push(child);

setUpdate((updateTrigger) => updateTrigger + 1);
}, []);
prevNum.current = num;
}, [num]);
const addNode = useCallback(() => {
insertRandomNode(root, 1);
setUpdate((updateTrigger) => updateTrigger + 1);
}, [root]);

return (
<div onClick={addNode}>
Expand All @@ -59,6 +68,13 @@ export const TidyLayout = ({ layoutType, ...props }: Props) => {
);
};

TidyLayout.argTypes = {
num: {
control: { type: 'range', min: 0, max: 400 },
defaultValue: 200,
},
};

export const Example0 = () => {
return (
<TidyComponent
Expand Down Expand Up @@ -93,6 +109,43 @@ export const Example0 = () => {
);
};

function deleteRandomNode(root: Node, num: number) {
while (num > 0) {
visit(root, (node, depth) => {
for (let i = 0; i < node.children.length; i++) {
if (node.children[i].children.length === 0) {
node.children.splice(i, 1);
num--;
if (num === 0) {
break;
}
}
}
});
}
}

function insertRandomNode(root: Node, num: number = 1) {
let nodes: [Node, number][] = [];
visit(root, (node, depth) => {
if (node.children.length < 4) {
nodes.push([node, depth]);
}
});

nodes.sort((a, b) => -a[1] + b[1]);
if (nodes.length > 20) {
const depth = nodes[20][1];
nodes = nodes.filter(([_, d]) => d >= depth);
}
for (let i = 0; i < num; i++) {
const node = nodes[(Math.random() * nodes.length) | 0][0];
const child = createNode();
child.parentId = node.id;
node.children.push(child);
}
}

function node(width: number, height: number, children: Node[] = []): Node {
return {
x: 0,
Expand Down
9 changes: 9 additions & 0 deletions src/tidy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export class TidyLayout extends Disposable {
this.tidy = TidyWasm.with_basic_layout();
} else if (type === LayoutType.Tidy) {
this.tidy = TidyWasm.with_tidy_layout();
} else if (type === LayoutType.LayeredTidy) {
this.tidy = TidyWasm.with_layered_tidy();
} else {
throw new Error('not implemented');
}
Expand All @@ -81,7 +83,9 @@ export class TidyLayout extends Disposable {

layout(updated = false) {
if (updated) {
const removedNodeId = new Set(this.idToNode.keys());
visit(this.root!, (node) => {
removedNodeId.delete(node.id);
if (this.idToNode.has(node.id)) {
return;
}
Expand All @@ -94,6 +98,11 @@ export class TidyLayout extends Disposable {
node.parentId ?? NULL_ID(),
);
});

for (const nodeId of removedNodeId) {
this.tidy.remove_node(nodeId);
this.idToNode.delete(nodeId);
}
}

this.tidy.layout();
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function createNode(): Node {
export function createTree(num: number): Node {
const root = createNode();
let arr = [root];
const MAX_CHOSEN_SIZE = 1000;
const MAX_CHOSEN_SIZE = 30;
for (let i = 0; i < num; i++) {
let parentIndex = 0;
if (arr.length < MAX_CHOSEN_SIZE) {
Expand Down

0 comments on commit 9e6d923

Please sign in to comment.