Skip to content

Commit

Permalink
change webgl code
Browse files Browse the repository at this point in the history
  • Loading branch information
mj-life-is-once committed Sep 21, 2023
1 parent 7f6d197 commit 8bce226
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 113 deletions.
255 changes: 142 additions & 113 deletions src/app/components/WebGLChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
webglColor,
iterateElements,
} from "../helper/annotationHelper";
import { HathiData } from "../types/types";

//https://github.com/ColinEberhardt/d3fc-webgl-hathi-explorer/blob/master/index.js

Expand All @@ -20,7 +21,7 @@ interface ChartProps {
}
const WebGLChart = (props: ChartProps) => {
const [data, setData] = useState(props.data);
const [callDraw, setCallDraw] = useState(0);
const [callDraw, setCallDraw] = useState(false);
const chartRef = useRef<unknown>(null);

const quadtree = useMemo(
Expand All @@ -38,27 +39,117 @@ const WebGLChart = (props: ChartProps) => {
const xScaleOriginal = useMemo(() => xScale.copy(), [xScale]);
const yScaleOriginal = useMemo(() => yScale.copy(), [yScale]);

// const annotationSeries = seriesSvgAnnotation()
// .notePadding(15)
// .type(d3.annotationCallout);

// const annotations: any[] = [];

const pointSeries = useMemo(
() =>
fc
.seriesWebglPoint()
.equals(
(previousData: any, currentData: any) => previousData === currentData
)
.size(1)
.crossValue((d: any) => d.x)
.mainValue((d: any) => d.y),
[]
);

const redraw = useCallback(
async (chart: any) => {
// console.log("redraw", data);
async (data: any, chart: any) => {
// Pass over data by parameter, otherwise it would only update the subsets of data
console.log("redraw", data.length);
d3.select("#chart").datum(data).call(chart);
},
[data, callDraw]
[callDraw]
);

const createAnnotationData = useCallback(
(datapoint: any) => ({
note: {
label: datapoint.first_author_name + " " + datapoint.year,
bgPadding: 5,
title: trunc(datapoint.title, 100),
},
x: datapoint.x,
y: datapoint.y,
dx: 20,
dy: 20,
}),
[]
const chart = useMemo(
() =>
fc
.chartCartesian(xScale, yScale)
.webglPlotArea(fc.seriesWebglMulti().series([pointSeries]))
// .svgPlotArea(
// // only render the annotations series on the SVG layer
// fc
// .seriesSvgMulti()
// .series([annotationSeries])
// .mapping((d: any) => d.annotations)
// )
.decorate((sel) =>
sel
.enter()
.selectAll(".plot-area")
.on("measure.range", (event: any) => {
xScaleOriginal.range([0, event.detail.width]);
yScaleOriginal.range([event.detail.height, 0]);
})
.call(
d3
.zoom()
.scaleExtent([0.8, 10])
.on("zoom", (event: any) => {
// update the scales based on current zoom
// console.log("zoom called");
xScale.domain(
event.transform.rescaleX(xScaleOriginal).domain()
);
yScale.domain(
event.transform.rescaleY(yScaleOriginal).domain()
);
redraw(data, chart);
// setCallDraw((prev) => !prev);
}) as any
)
.call(
fc.pointer().on("point", ([coord]: any[]) => {
// annotations.pop();

if (!coord || !quadtree) {
return;
}

// find the closes datapoint to the pointer
const x = xScale.invert(coord.x);
const y = yScale.invert(coord.y);
const radius = Math.abs(
xScale.invert(coord.x) - xScale.invert(coord.x - 20)
);
const closestDatum = quadtree.find(
x,
y,
radius
) as unknown as HathiData;

// if the closest point is within 20 pixels, show the annotation
if (closestDatum) {
console.log(
"closestDatum",
closestDatum.title,
closestDatum.x,
closestDatum.y
);
// only draw when there's data to show -> separate the canvas
// setCallDraw((prev) => !prev);
console.log(data.length);
redraw(data, chart);
}
})
)
),
[
data,
pointSeries,
quadtree,
redraw,
xScale,
xScaleOriginal,
yScale,
yScaleOriginal,
]
);

useEffect(() => {
Expand All @@ -80,15 +171,6 @@ const WebGLChart = (props: ChartProps) => {
.domain([1850, 2000])
.interpolator(d3.interpolateRdYlGn);

const pointSeries = fc
.seriesWebglPoint()
.equals(
(previousData: any, currentData: any) => previousData === currentData
)
.size(1)
.crossValue((d: any) => d.x)
.mainValue((d: any) => d.y);

const languageFill = (d: any) =>
webglColor(languageColorScale((hashCode(d.language) % 10).toString()));

Expand All @@ -105,97 +187,44 @@ const WebGLChart = (props: ChartProps) => {
);
el.classList.add("active");
fillColor.value(el.id === "language" ? languageFill : yearFill);
redraw(chart);
redraw(data, chart);
});
});

// zoom needs to be called whenever data changes
const zoom = d3
.zoom()
.scaleExtent([0.8, 10])
.on("zoom", (event: any) => {
// update the scales based on current zoom
// console.log("zoom called");
xScale.domain(event.transform.rescaleX(xScaleOriginal).domain());
yScale.domain(event.transform.rescaleY(yScaleOriginal).domain());
// console.log("onzoom", data);
setCallDraw((draw) => -1 * draw); // needs to be forcefully called..
// redraw(chart);
});

// const annotationSeries = seriesSvgAnnotation()
// .notePadding(15)
// .type(d3.annotationCallout);

const chart = fc
.chartCartesian(xScale, yScale)
.webglPlotArea(fc.seriesWebglMulti().series([pointSeries]))
// .svgPlotArea(
// // only render the annotations series on the SVG layer
// fc
// .seriesSvgMulti()
// .series([annotationSeries])
// .mapping((d: any) => d.annotations)
// )
.decorate((sel) =>
sel
.enter()
.selectAll(".plot-area")
.on("measure.range", (event: any) => {
xScaleOriginal.range([0, event.detail.width]);
yScaleOriginal.range([event.detail.height, 0]);
})
.call(zoom as any)
.call(pointer)
);

// const annotations: any[] = [];

const pointer = fc.pointer().on("point", ([coord]: any[]) => {
// annotations.pop();

if (!coord || !quadtree) {
return;
}

// find the closes datapoint to the pointer
const x = xScale.invert(coord.x);
const y = yScale.invert(coord.y);
const radius = Math.abs(
xScale.invert(coord.x) - xScale.invert(coord.x - 20)
);
const closestDatum = quadtree.find(x, y, radius);

// if the closest point is within 20 pixels, show the annotation
if (closestDatum) {
console.log("closestDatum", closestDatum);
// annotations[0] = createAnnotationData(closestDatum);
}
setCallDraw((draw) => -1 * draw);
});

redraw(chart);
}, [
createAnnotationData,
data,
quadtree,
redraw,
xScale,
xScaleOriginal,
yScale,
yScaleOriginal,
]);
redraw(data, chart);
}, [chart, data, pointSeries, redraw]);
return (
<>
{/* <button
onClick={() => {
console.log("clicked", data);
setCallDraw((draw) => -1 * draw);
}}
>
data
</button> */}
<div id="chart" className={props.className ?? ""}></div>
<div id="chart" className={`relative ${props.className ?? ""}`}>
{/* <div className="absolute top-0 left-0">
<svg width="100" height="50">
<g>
<circle
fill="#000"
stroke="#fff"
r="10px"
// cx={(node as any).x}
// cy={(node as any).y}
>
<title>
<title></title>
</title>
</circle>
<text
textAnchor="middle"
alignmentBaseline="middle"
fontSize="1rem"
// x={(node as any).x}
// y={(node as any).y}
fill={"#ffffff"}
stroke="none"
>
data
</text>
</g>
</svg>
</div> */}
</div>
</>
);
};
Expand Down
12 changes: 12 additions & 0 deletions src/app/types/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export interface ZyncData {
y: number;
}

export interface HathiData {
date: string;
first_author_name: string;
id: string;
"ix ": string;
language: string;
lc1: string;
title: string;
x: number;
y: number;
year: number;
}
export interface GalaxyData {
parallax: number;
longitude: number;
Expand Down

0 comments on commit 8bce226

Please sign in to comment.