Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7017e08
draw lines between related heap objects
MaKhandare Jul 8, 2025
1870b0b
add a nested class to see if it behaves correctly
MaKhandare Jul 8, 2025
59f79ff
instead of fixed offset, get size of node and adjust
MaKhandare Jul 8, 2025
6e9988e
add datatype and some basic styling
MaKhandare Jul 8, 2025
f44d4c0
try to memoize with useMemo
MaKhandare Jul 8, 2025
31168ce
refac connection drawing to improve performance
MaKhandare Jul 8, 2025
974f1b5
change render order of text so it does not randomly hide behind the node
MaKhandare Jul 8, 2025
df041e3
some color adjustments
MaKhandare Jul 8, 2025
cd5d94f
Update playground/csharp/Program.cs
MaKhandare Jul 10, 2025
08856ab
Update server/src/HeapConnections.tsx
MaKhandare Jul 10, 2025
ce7dde4
Update playground/csharp/Program.cs
MaKhandare Jul 10, 2025
3b40270
Update server/src/HeapConnections.tsx
MaKhandare Jul 11, 2025
573ff7a
Update server/src/HeapConnections.tsx
MaKhandare Jul 11, 2025
214493d
Update server/src/HeapConnections.tsx
MaKhandare Jul 11, 2025
d5844c8
rename HeapConnections to HeapConnectionsProvider
MaKhandare Jul 17, 2025
3401bab
use a themeprovider instead of hardcoded colors
MaKhandare Jul 17, 2025
13784f6
invert if for readability
MaKhandare Jul 17, 2025
538be14
helper hook for connections provider
MaKhandare Jul 17, 2025
f008a5c
rename pChildren and rChildren, use else instead of else if
MaKhandare Jul 17, 2025
5cc36d3
move variables into correct scope
MaKhandare Jul 17, 2025
8449457
early check return
MaKhandare Jul 17, 2025
1f6827f
remove as cast by introducing a VariableWithParent t ype
MaKhandare Jul 17, 2025
3dc598a
ref can be null, with proper null checks no as casts needed
MaKhandare Jul 17, 2025
c12b60a
use a map to store children and pass to heapnode
MaKhandare Jul 17, 2025
5562a61
remove theme from props
MaKhandare Jul 17, 2025
9a0e63c
simplify useHeapConnections
MaKhandare Jul 17, 2025
b8c17bf
remove unnecessary if statement
MaKhandare Jul 17, 2025
7246d7d
give body temporarily a gruvbox color
MaKhandare Jul 17, 2025
74b5507
gruvbox colors
MaKhandare Jul 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions playground/csharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,37 @@ int test(int a, int b)
Z = 37,
};

var nested = new NestedClass
{
mat = new Matrix()
{
Row0 = new Vector
{
X = 32,
Y = 64,
Z = 128,
},

Row1 = new Vector
{
X = 1,
Y = 2,
Z = 3,
},

Row2 = new Vector
{
X = 5,
Y = 6,
Z = 9,
},
},

name = "mat",
age = 33
};


var result = mat.Apply(vec);

for (int i = 0; i < 10; ++i)
Expand Down Expand Up @@ -77,3 +108,11 @@ public Vector Apply(Vector target)
};
}
}

class NestedClass
{
public required Matrix mat { get; init; }
public required string name { get; init; }
public required int age { get; init; }
}

2 changes: 1 addition & 1 deletion server/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<title>dapviz</title>
</head>

<body style="margin: 0; padding: 0; background-color: #2e2e2e;">
<body style="margin: 0; padding: 0; background-color: #1d2021;">
<div id="app" style="height: 100vh"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
Expand Down
13 changes: 8 additions & 5 deletions server/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import DapvizProvider, { useDapviz } from "./DapvizProvider";
import Visualizer from "./Visualizer";
import Controls from "./Controls";
import { useState } from "react";
import { ThemeProvider } from "./ThemeProvider";

const NoConnectionError = () => (
<h1 style={{ margin: "auto", textAlign: "center" }}>No Connection</h1>
Expand All @@ -24,11 +25,13 @@ const DapvizApp = () => {
}

const App = () => (
<DapvizProvider
noConnection={<NoConnectionError />}
>
<DapvizApp />
</DapvizProvider>
<ThemeProvider>
<DapvizProvider
noConnection={<NoConnectionError />}
>
<DapvizApp />
</DapvizProvider>
</ThemeProvider>
);

export default App;
Expand Down
141 changes: 141 additions & 0 deletions server/src/HeapConnectionsProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { Variable } from "./DapvizProvider";
import { QuadraticBezierLine, QuadraticBezierLineRef } from "@react-three/drei";
import React, { createContext, useContext, useMemo, useRef } from "react";
import { useCallback, useState } from "react";
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { useTheme } from "./ThemeProvider";

export type HeapConnectionContextType = {
registerNode: (id: number, ref: React.RefObject<THREE.Group | null>) => void;
unregisterNode: (id: number) => void;
};

export const HeapConnectionContext = createContext<HeapConnectionContextType | null>(null);

Check warning on line 14 in server/src/HeapConnectionsProvider.tsx

View workflow job for this annotation

GitHub Actions / Run CI for server

Fast refresh only works when a file only exports components. Move your React context(s) to a separate file

export const useHeapConnections = () => {

Check warning on line 16 in server/src/HeapConnectionsProvider.tsx

View workflow job for this annotation

GitHub Actions / Run CI for server

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
const context = useContext(HeapConnectionContext);
if (!context) {
throw new Error("useHeapConnections called outside of HeapConnectionsProvider");
}
return context;
}

interface ConnectionLineProps {
parentRef: React.RefObject<THREE.Group | null>;
childRef: React.RefObject<THREE.Group | null>;
}

const ConnectionLine = ({ parentRef, childRef }: ConnectionLineProps) => {
const theme = useTheme();

const lineRef = useRef<QuadraticBezierLineRef>(null);

const startCircleRef = useRef<THREE.Mesh>(null);
const endCircleRef = useRef<THREE.Mesh>(null);

useFrame(() => {
if (!parentRef.current || !childRef.current || !lineRef.current) return;

const startPos = new THREE.Vector3();
const endPos = new THREE.Vector3();
const midPos = new THREE.Vector3();
const box = new THREE.Box3();
const size = new THREE.Vector3();

parentRef.current.getWorldPosition(startPos);
childRef.current.getWorldPosition(endPos);

box.setFromObject(parentRef.current);
box.getSize(size);
const startOffset = size.x / 2;

box.setFromObject(childRef.current);
box.getSize(size);
const endOffset = size.x / 2;

const padding = 3;

const finalStart = startPos.clone();
finalStart.x += startOffset + padding;

const finalEnd = endPos.clone();
finalEnd.x -= endOffset + padding;

midPos.addVectors(finalStart, finalEnd).multiplyScalar(0.5);
midPos.y += 80;

lineRef.current.setPoints(finalStart, finalEnd, midPos);
startCircleRef.current?.position.copy(finalStart);
endCircleRef.current?.position.copy(finalEnd);
});

return (
<>
<QuadraticBezierLine
ref={lineRef}
start={[0, 0, 0]}
end={[0, 0, 0]}
color={theme.connection.line}
lineWidth={1}
/>

<mesh ref={startCircleRef}>
<circleGeometry args={[2.5, 16]} />
<meshBasicMaterial color={theme.connection.start} />
</mesh>
<mesh ref={endCircleRef}>
<circleGeometry args={[2.5, 16]} />
<meshBasicMaterial color={theme.connection.end} />
</mesh>
</>
);
}

type VariableWithParent = Variable & { parent: NonNullable<Variable["parent"]> };

function hasParent(v: Variable): v is VariableWithParent {
return !!v.parent && v.reference > 0;
}

export const HeapConnectionsProvider = ({ children, allVariables }: { children: React.ReactNode, allVariables: Variable[] }) => {
const [nodeRefs, setNodeRefs] = useState<Map<number, React.RefObject<THREE.Group | null>>>(new Map());

const registerNode = useCallback((id: number, ref: React.RefObject<THREE.Group | null>) => {
setNodeRefs(prev => new Map(prev).set(id, ref));
}, [setNodeRefs]);

const unregisterNode = useCallback((id: number) => {
setNodeRefs(prev => {
const newMap = new Map(prev);
newMap.delete(id);
return newMap;
});
}, [setNodeRefs]);

const connections = useMemo(() =>
allVariables.filter(hasParent), [allVariables]
);

return (
<HeapConnectionContext.Provider value={{ registerNode, unregisterNode }}>
{children}

<group>
{connections.map(variable => {
const parentRef = nodeRefs.get(variable.parent);
const childRef = nodeRefs.get(variable.reference);

if (!parentRef || !childRef) return null;
return (
<ConnectionLine
key={`${variable.parent}-${variable.reference}`}
parentRef={parentRef}
childRef={childRef}
/>
);
})}
</group>
</HeapConnectionContext.Provider>
);
}
63 changes: 63 additions & 0 deletions server/src/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { createContext, ReactNode, useContext } from "react"

interface AppTheme {
grid: {
cell: string,
section: string,
},
text: {
primary: string,
secondary: string,
type: string,
},
node: {
background: string,
backgroundHover: string,
border: string,
divider: string,
},
connection: {
line: string,
start: string,
end: string,
},
}

export const gruvboxTheme: AppTheme = {

Check warning on line 26 in server/src/ThemeProvider.tsx

View workflow job for this annotation

GitHub Actions / Run CI for server

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
grid: {
cell: "#3c3836",
section: "#504945",
},
text: {
primary: "#ebdbb2",
secondary: "#a89984",
type: "#d65d0e",
},
node: {
background: "#282828",
backgroundHover: "#504945",
border: "#504945",
divider: "#4a4a5a",
},
connection: {
line: "#a89984",
start: "#689d6a",
end: "#d79921",
}
}

const ThemeContext = createContext(gruvboxTheme);

export const useTheme = () => {

Check warning on line 51 in server/src/ThemeProvider.tsx

View workflow job for this annotation

GitHub Actions / Run CI for server

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme called outside of ThemeProvider");
}
return context;
}

export const ThemeProvider = ({ children }: { children: ReactNode }) => {
return <ThemeContext.Provider value={gruvboxTheme}>
{children}
</ThemeContext.Provider>;
};
Loading
Loading