Skip to content

Commit

Permalink
feat: update graph
Browse files Browse the repository at this point in the history
  • Loading branch information
pomelo-nwu committed Aug 22, 2024
1 parent 59601ff commit 878cc84
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 41 deletions.
16 changes: 10 additions & 6 deletions packages/studio-graph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,29 @@
"author": "",
"license": "ISC",
"dependencies": {
"3d-force-graph": "^1.73.3",
"@antv/g6": "latest",
"@graphscope/studio-components": "workspace:*",
"@tweenjs/tween.js": "^25.0.0",
"@types/d3-selection": "^3.0.10",
"d3-brush": "^3.0.0",
"d3-force": "^3.0.0",
"d3-force-3d": "^3.0.5",
"d3-hierarchy": "^3.1.2",
"d3-force": "^3.0.0",
"d3-selection": "^3.0.0",
"force-graph": "^1.43.5",
"mitt": "^3.0.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-intl": "^6.6.1",
"uuidv4": "latest",
"valtio": "latest",
"3d-force-graph": "^1.73.3",
"@tweenjs/tween.js": "^25.0.0",
"force-graph": "^1.43.5",
"mitt": "^3.0.1"
"valtio": "latest"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/d3-brush": "^3.0.6",
"@types/d3-force": "^3.0.10",
"@types/d3-hierarchy": "^3.1.7"
}
Expand Down
9 changes: 9 additions & 0 deletions packages/studio-graph/src/app/query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
ContextMenu,
NeighborQuery,
DeleteNode,
CommonNeighbor,
Brush,
} from '../components';

import { Divider } from 'antd';
Expand Down Expand Up @@ -61,6 +63,11 @@ const QueryGraph: React.FunctionComponent<QueryGraphProps> = props => {
children: <StyleSetting />,
},
];

const onSelectNodes = values => {
console.log(values);
};

return (
<div
style={{
Expand All @@ -86,9 +93,11 @@ const QueryGraph: React.FunctionComponent<QueryGraphProps> = props => {
<Prepare data={data} schema={schema} graphId={graphId} />
<Canvas />
<ClearStatatus />
{/* <Brush onSelectNodes={onSelectNodes} /> */}

<ContextMenu>
<NeighborQuery onQuery={onQuery} />
<CommonNeighbor onQuery={onQuery} />
<DeleteNode />
</ContextMenu>
<Toolbar style={{ position: 'absolute', top: '20px', right: '20px', left: 'unset' }}>
Expand Down
60 changes: 60 additions & 0 deletions packages/studio-graph/src/components/Brush/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useRef, useEffect } from 'react';

import { useContext } from '../../hooks/useContext';
import { brush as d3Brush } from 'd3-brush';
import { select } from 'd3-selection';
interface IBrushProps {
onSelectNodes: (e: any) => void;
}

const Brush: React.FunctionComponent<IBrushProps> = props => {
const { onSelectNodes } = props;
const { id, store } = useContext();
const { graph, height, width, data } = store;
const svgRef = useRef(null);
const selectedNodesRef = useRef([]);

useEffect(() => {
if (svgRef.current) {
const handleBrush = selection => {
if (!selection) return;

const [[x0, y0], [x1, y1]] = selection;
const newSelectedNodes = data.nodes.filter(node => {
//@ts-ignore
const { x, y, z } = graph?.graph2ScreenCoords(node.x, node.y, 0);
return x0 <= x && x <= x1 && y0 <= y && y <= y1;
});
console.log(newSelectedNodes);
};
const svg = select(svgRef.current);
const brush = d3Brush()
.extent([
[0, 0],
[width, height],
])
.on('start', () => {
selectedNodesRef.current = [];
})
.on('brush', event => handleBrush(event.selection))
.on('end', () => onSelectNodes(selectedNodesRef.current));

svg.selectAll('g.brush').remove(); // Remove any existing brush group
svg
.append('g')
.attr('class', 'brush')
.call(brush)
.append('rect')
.attr('width', width)
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all');

svg.style('pointer-events', 'none');
}
}, [width, height, onSelectNodes]);

return <svg ref={svgRef} width={width} height={height} style={{ position: 'absolute', top: '0px' }}></svg>;
};

export default Brush;
4 changes: 3 additions & 1 deletion packages/studio-graph/src/components/Canvas/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const transform = (data): { nodes: NodeData[]; edges: EdgeData[] } => {
};

const Canvas: React.FunctionComponent<ICanvasProps> = props => {
const { store, updateStore } = useContext();
const { id, store, updateStore } = useContext();
const { data, render, nodeStyle, edgeStyle, nodeStatus, edgeStatus } = store;
const onInit = (graph, emitter, { width, height }) => {
updateStore(draft => {
Expand All @@ -75,6 +75,7 @@ const Canvas: React.FunctionComponent<ICanvasProps> = props => {
return (
<div style={{ width: '100%', height: '100%' }}>
<Graph
id={id}
nodeStyle={nodeStyle}
edgeStyle={edgeStyle}
nodeStatus={nodeStatus}
Expand All @@ -83,6 +84,7 @@ const Canvas: React.FunctionComponent<ICanvasProps> = props => {
//@ts-ignore
data={data}
render={render}
//@ts-ignore
onInit={onInit}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as React from 'react';
import { Typography, Button, Menu } from 'antd';
import { useContext } from '../../../hooks/useContext';
import { Utils } from '@graphscope/studio-components';
import { getDataMap } from '../../Prepare/utils';

interface INeighborQueryProps {
onQuery: (params: any) => Promise<any>;
}

const CommonNeighbor: React.FunctionComponent<INeighborQueryProps> = props => {
const { onQuery } = props;
const { store, updateStore } = useContext();
const { nodeStatus, schema, dataMap, emitter } = store;

const selectId =
Object.keys(nodeStatus).filter(key => {
return nodeStatus[key].selected;
})[0] || '';
const selectNode = dataMap[selectId] || {};

const relatedEdges = schema.edges.filter(item => {
return item.source === selectNode.label;
});
const itemChildren = relatedEdges.map(item => {
const { source, target, label } = item;
if (source === target) {
return {
key: `(a:${source})-[b:${label}]-(c:${target})`,
label: `[${label}]-(${target})`,
};
}
return {
key: `(a:${source})-[b:${label}]->(c:${target})`,
label: `[${label}]->(${target})`,
};
});

const items = [
{
key: 'CommonNeighbor',
// icon: <ShareAltOutlined />,
label: 'CommonNeighbor',
children: itemChildren,
},
];

const onClick = async ({ key }) => {
const { name, title } = selectNode.properties;
let script = '';
if (name) {
script = `
MATCH ${key}
WHERE a.name = "${name}"
RETURN a, b, c
`;
}
if (title) {
script = `
MATCH ${key}
WHERE a.title = "${title}"
RETURN a, b, c
`;
}

console.log(script);
/**
*
*
* MATCH (p1:Paper)<-[c1:Cite]-(p2:Paper)
WHERE p1.title = 'Parallel Subgraph Listing in a Large-Scale Graph'
RETURN p1, c1, p2
*/
const res = await onQuery({
script,
language: 'cypher',
});
console.log(res);
if (res.nodes.length > 0) {
updateStore(draft => {
draft.data = Utils.handleExpand(draft.data, res);
draft.dataMap = getDataMap(draft.data);
});
}
emitter?.emit('canvas:click');
};
return (
<Menu onClick={onClick} style={{ margin: '0px', padding: '0px', width: '103%' }} mode="vertical" items={items} />
);
};

export default CommonNeighbor;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface INeighborQueryProps {
onQuery: (params: any) => Promise<any>;
}

const NeighborQuery: React.FunctionComponent<INeighborQueryProps> = props => {
const CommonNeighbor: React.FunctionComponent<INeighborQueryProps> = props => {
const { onQuery } = props;
const { store, updateStore } = useContext();
const { nodeStatus, schema, dataMap, emitter } = store;
Expand All @@ -24,6 +24,12 @@ const NeighborQuery: React.FunctionComponent<INeighborQueryProps> = props => {
});
const itemChildren = relatedEdges.map(item => {
const { source, target, label } = item;
if (source === target) {
return {
key: `(a:${source})-[b:${label}]-(c:${target})`,
label: `[${label}]-(${target})`,
};
}
return {
key: `(a:${source})-[b:${label}]->(c:${target})`,
label: `[${label}]->(${target})`,
Expand Down Expand Up @@ -83,4 +89,4 @@ const NeighborQuery: React.FunctionComponent<INeighborQueryProps> = props => {
);
};

export default NeighborQuery;
export default CommonNeighbor;
1 change: 0 additions & 1 deletion packages/studio-graph/src/components/Prepare/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const Prepare: React.FunctionComponent<IPrepareProps> = props => {
React.useEffect(() => {
if (data) {
const style = getStyleConfig(schema, graphId);
console.log('Prepare >>>>>>', style);
updateStore(draft => {
draft.data = data;
draft.source = data;
Expand Down
10 changes: 1 addition & 9 deletions packages/studio-graph/src/components/ZoomFit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,10 @@ export interface IZoomFitProps {}

const ZoomFit: React.FunctionComponent<IZoomFitProps> = props => {
const { store } = useContext();
const { graph, data } = store;
const { graph } = store;
const handleClick = () => {
graph?.zoomToFit(1000);
if (data && data.nodes && data.nodes[0]) {
console.log('data', data);
}
};
React.useEffect(() => {
setTimeout(() => {
handleClick();
}, 200);
}, []);

return <Button onClick={handleClick} icon={<Icons.ZoomFit />} type="text"></Button>;
};
Expand Down
2 changes: 2 additions & 0 deletions packages/studio-graph/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ export { default as SliderFilter } from './SliderFilter';
export { default as RunCluster } from './RunCluster';
export { default as LayoutSetting } from './LayoutSetting';
export { default as NeighborQuery } from './ContextMenu/NeighborQuery';
export { default as CommonNeighbor } from './ContextMenu/CommonNeighbor';
export { default as DeleteNode } from './ContextMenu/DeleteNode';
export { default as Brush } from './Brush';
38 changes: 18 additions & 20 deletions packages/studio-graph/src/graph/custom-node/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { drawText } from './draw';
export const nodeCanvasObject =
(node: NodeObject, ctx: CanvasRenderingContext2D, globalScale: number) =>
(nodeStyle: StyleConfig, nodeStatus: any) => {
if (!node.x || !node.y) {
if (node.x === undefined || node.y === undefined) {
return;
}

Expand Down Expand Up @@ -44,8 +44,6 @@ export const nodeCanvasObject =
//@TODO
}

const showTextInCircle = R * globalScale > 14;

if (captionMode === 'inner') {
const fontSize = 16 / globalScale;
ctx.font = `${fontSize}px Sans-Serif`;
Expand All @@ -60,24 +58,24 @@ export const nodeCanvasObject =
maxWidth: R * 2 * 0.8, //预留 20% pandding
lineHeight: fontSize * 1.2,
});
} else if (captionStatus !== 'hidden' && textLabel) {
const fontSize = Math.min(0.5 * globalScale, 14 / globalScale);
// console.log(globalScale, 12 / globalScale, fontSize);
if (true) {
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(textLabel).width;
const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
ctx.fillStyle = NODE_TEXT_COLOR;
//@ts-ignore
ctx.fillRect(node.x - bckgDimensions[0] / 2, 1.2 * R + node.y - bckgDimensions[1] / 2, ...bckgDimensions);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = color;
ctx.fillText(textLabel, node.x, node.y + 1.2 * R);
// @ts-ignore
node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint
}
}

// } else if (captionStatus !== 'hidden' && textLabel) {
const fontSize = 12 / globalScale;
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(textLabel).width;
const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
ctx.fillStyle = NODE_TEXT_COLOR;
//@ts-ignore
ctx.fillRect(node.x - bckgDimensions[0] / 2, 1.2 * R + node.y - bckgDimensions[1] / 2, ...bckgDimensions);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = color;
ctx.fillText(textLabel, node.x, node.y + 1.2 * R);
// @ts-ignore
node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint

// }
ctx.restore();
return;
};
4 changes: 2 additions & 2 deletions packages/studio-graph/src/graph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ type GraphRef = any | null;

const Graph = memo(
forwardRef<GraphRef, PropsWithChildren<GraphProps>>((props, ref) => {
const { style, children, ...restProps } = props;
const { id, style, children, ...restProps } = props;
const { graph, containerRef, isReady } = useGraph<GraphProps>(restProps);
useImperativeHandle(ref, () => graph!, [isReady]);
const containerStyle: CSSProperties = {
height: 'inherit',
position: 'relative',
...style,
};
return <div ref={containerRef} style={containerStyle}></div>;
return <div id={`GRAPH_${id}`} ref={containerRef} style={containerStyle}></div>;
}),
);

Expand Down
Loading

0 comments on commit 878cc84

Please sign in to comment.