Skip to content

Instantly share code, notes, and snippets.

@zachj0hnston
Last active December 10, 2022 02:11
Show Gist options
  • Save zachj0hnston/ece0af8fc9cfad161b2603d5390630ad to your computer and use it in GitHub Desktop.
Save zachj0hnston/ece0af8fc9cfad161b2603d5390630ad to your computer and use it in GitHub Desktop.
Dot Grid
import { Html } from "@react-three/drei";
import { Color, Matrix4, Vector3 } from "three";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import React, { useRef } from "react";
import { useIntersection } from "react-use";
const DisableRender = () => useFrame(() => null, 1);
interface DotGridProps {
height?: number;
}
export function DotGrid(props: DotGridProps) {
const { height = 1000 } = props;
const intersectionRef = useRef(null);
const intersection = useIntersection(intersectionRef, {
root: null,
rootMargin: "0px",
threshold: 0,
});
const inView = intersection?.isIntersecting ?? false;
return (
<div style={{ height: `${height}px` }} ref={intersectionRef}>
<Canvas orthographic={true} dpr={window.devicePixelRatio} linear={true}>
{!inView && <DisableRender />}
<Scene height={height} />
<color attach="background" args={["#121315"]} />
</Canvas>
</div>
);
}
// COLORS
const blue = new Color("rgba(87, 135, 255, 1)");
const teal = new Color("rgba(0, 183, 144,1)");
const orange = new Color("rgba(255, 120, 64, 1)");
const yellow = new Color("rgba(247, 163, 0, 1)");
const purple = new Color("rgba(210, 125, 255, 1)");
const white = new Color(1, 1, 1);
const black = new Color(0.25, 0.25, 0.25);
const midGray = new Color(0.5, 0.5, 0.5);
const darkGray = new Color(0.35, 0.35, 0.35);
const colors = [teal, orange, blue, yellow, purple];
interface SceneProps {
height: number;
}
function Scene(props: SceneProps) {
const { height } = props;
const width = window.innerWidth;
const { dots, resources } = getGrid(width, height);
return (
<>
<GridDots nodes={dots} resources={resources} />
<ResourceCubes nodes={resources} />
<Labels nodes={resources} />
<CursorLight />
<directionalLight color={0xffffff} intensity={lighting.directionalIntensity} />
<ambientLight color={0xffffff} intensity={lighting.ambientIntensity} />
</>
);
}
// HELPERS
const constrain = function (n, low, high) {
return Math.max(Math.min(n, high), low);
};
const map = function (n, start1, stop1, start2, stop2, withinBounds) {
const newval = ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
if (!withinBounds) {
return newval;
}
if (start2 < stop2) {
return constrain(newval, start2, stop2);
} else {
return constrain(newval, stop2, start2);
}
};
const mouseIsInFrame = ({ mouse }) => isBetween(mouse.y, 0.99, -0.94) && isBetween(mouse.x, 0.99, -0.99);
// Warning: This function is called 60 times a second. Be careful what you do in here.
const updateMousePosition = ({ mouse, viewport, mousePos }) =>
mousePos.set((mouse.x * viewport.width) / 2, (mouse.y * viewport.height) / 2, 0);
// Warning: This function is called 60 times a second. Be careful what you do in here.
const updateNodePosition = ({ nodePos, mousePos, gravityDistance, gravityPower }) => {
// Calculate the effect of gravity
let distance = nodePos.distanceTo(mousePos);
distance = map(distance, 0, gravityDistance, gravityPower, 0, true);
nodePos.lerp(mousePos, distance);
};
const getGrid = (width, height) => {
const increments = 50;
const rows = 19;
const dots = [];
const resources = [];
const gridWidth = Math.ceil(width / increments);
const rowOffset = Math.ceil(height / increments / 2 - 1);
[...Array(rows).keys()].forEach((e, rowIndex) => {
[...Array(gridWidth).keys()].forEach((e, colIndex) => {
[...Array(2).keys()].forEach((e, sideIndex) => {
const xOffset = sideIndex % 2 ? -colIndex : colIndex;
const yOffset = rowOffset - rowIndex;
const node = { x: xOffset * increments, y: yOffset * increments };
const overlappingResources = resourceData.filter((e) => e.x === xOffset && e.y === yOffset);
if (overlappingResources.length !== 1) {
dots.push(node);
}
});
});
});
resourceData.forEach((resource) => {
const node = {
x: resource.x * increments,
y: resource.y * increments,
resource,
path: createPath({ x: resource.x * increments, y: resource.y * increments, increments, width, height }),
};
resources.push(node);
});
return { dots, resources };
};
const createPath = ({ x, y, increments, width, height }) => {
// make every point positive to avoid negative x and y weirdness
const origin = { x: Math.abs(x), y: Math.abs(y) };
// record whether x and y are psotive or negative
const sign = { x: Math.sign(x === 0 ? x + 1 : x), y: Math.sign(y === 0 ? y + 1 : y) };
// get the distance from this point to the x and y edges.
const edge = { x: width / 2, y: height / 2 };
const distance = { x: edge.x - origin.x, y: edge.y - origin.y };
// round values to nearest increment so pthe path always appears along the grid
const rounded = (x) => Math.ceil(x / increments) * increments;
// create the path
let path = [
[
{ x: edge.x, y: origin.y - increments * 2 },
{ x: origin.x + distance.x / 2, y: origin.y - increments * 2 },
],
[
{ x: origin.x + distance.x / 2, y: origin.y - increments * 2 },
{ x: origin.x + distance.x / 2, y: origin.y },
],
[
{ x: origin.x + distance.x / 2, y: origin.y },
{ x: origin.x, y: origin.y },
],
[
{ x: origin.x, y: origin.y },
{ x: origin.x, y: origin.y + distance.y / 2 },
],
[
{ x: origin.x, y: origin.y + distance.y / 2 },
{ x: origin.x - increments * 3, y: origin.y + distance.y / 2 },
],
[
{ x: origin.x - increments * 3, y: origin.y + distance.y / 2 },
{ x: origin.x - increments * 3, y: edge.y },
],
];
// round all the values and apply the origin sign
path = path.map((line) => {
return line.map((point) => {
return { x: rounded(point.x) * sign.x, y: rounded(point.y) * sign.y };
});
});
return path;
};
const getClosestResource = ({ resources, mousePos, minDistance, resourcePos }) => {
const closestResource = resources.reduce(
(acc, current) => {
resourcePos.set(current.x, current.y, 0);
const distance = mousePos.distanceTo(resourcePos);
if (acc.distance && distance > acc.distance) {
return acc;
} else {
const distancedNormalised = map(distance, minDistance, minDistance / 2, 1, 0, true);
return { coordinates: { x: current.x, y: current.y }, distance, distancedNormalised, path: current.path };
}
},
{ resource: resources[0] },
);
if (closestResource.distance < minDistance) {
return { isWithinRange: true, ...closestResource };
}
return { isWithinRange: false, ...closestResource };
};
// checks if a number is between two other numbers
const isBetween = (n, a, b) => {
return (n - a) * (n - b) <= 0;
};
// checks if a point (x, y) intersects a line between p1 and p2
const intersectsLine = ({ p1x, p1y, p2x, p2y, x, y }) => {
if (p1x === p2x) {
return x === p1x && isBetween(y, p1y, p2y);
} else if (p1y === p2y) {
return y === p1y && isBetween(x, p1x, p2x);
} else {
return false;
}
};
// checks if a point intersects a path (an array of lines)
const intersectsPath = ({ path, x, y }) => {
const pointIsOnPath = path.reduce((acc, line) => {
if (acc === true) {
return acc;
}
return intersectsLine({ p1x: line[0].x, p1y: line[0].y, p2x: line[1].x, p2y: line[1].y, x, y });
}, false);
return pointIsOnPath;
};
const graph = ({ x, y, amplitude, frequency, time }) => {
return Math.sin(frequency * (x ** 2 + y ** 2 + time)) * amplitude;
};
// DATA
const resourceData = [
{
x: -13,
y: 6,
color: blue,
resource: "Tableau",
access: "14 users assigned access",
icon: (
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1262_201994)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.9203 1.66374V2.17798H9.40777V3.84172H8.86327V2.17798H7.35076V1.66374H8.86327V0H9.40777V1.66374H10.9203ZM2.057 10.8597H1.5125V9.19592L0 9.22617V8.68168H1.5125V7.04819H2.057V8.68168H3.56951V9.19592H2.057V10.8597ZM13.4006 7.35213H14.2475V4.93212H16.5162V4.20612H14.2475V1.75586H13.4006V4.20612H11.1621V4.93212H13.4006V7.35213ZM9.61942 12.161H8.62115V9.43852H6.14062V8.47052H8.62115V5.74805H9.61942V8.47052H12.1302V9.43852H9.61942V12.161ZM4.84039 16.0934H3.99339V13.6733H1.75488V12.9171H3.99339V10.4668H4.84039V12.9171H7.07889V13.6733H4.84039V16.0934ZM9.49831 17.9996H8.77229V16.2754H7.25977V15.6402H8.77229V13.916H9.49831V15.6402H11.0411V16.2754H9.49831V17.9996ZM4.02364 7.35213H4.81014V4.90187H7.07889V4.20612H4.81014V1.75586H4.02364V4.20612H1.75488V4.90187H4.02364V7.35213ZM16.6975 10.9812H15.9715V9.28724H14.459V8.62174H15.9715V6.92773H16.6975V8.62174H18.2403V9.28724H16.6975V10.9812ZM13.4006 16.0934H14.2475V13.6733H16.5162V12.9171H14.2475V10.4668H13.4006V12.9171H11.1621V13.6733H13.4006V16.0934Z"
/>
</g>
<defs>
<clipPath id="clip0_1262_201994">
<rect width="18.2408" height="18" />
</clipPath>
</defs>
</svg>
),
},
{
x: 13,
y: 6,
color: orange,
resource: "Gitlab",
access: "3 users assigned access",
icon: (
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.00228 17.2831L17.7113 10.9556C17.9516 10.781 18.0522 10.4715 17.9604 10.1889L16.9539 7.09131L14.9577 0.952257C14.8551 0.6364 14.4082 0.636314 14.3056 0.952257L12.3109 7.09131H5.69076L3.69593 0.952257C3.59333 0.636314 3.14641 0.6364 3.04382 0.952257L0.039715 10.1889C-0.0520847 10.4715 0.0484581 10.781 0.288801 10.9556L9.00228 17.2831Z" />
</svg>
),
},
{
x: 12,
y: -3,
color: purple,
resource: "Airflow",
access: "1 user assigned access",
icon: (
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.290164 17.9288L8.90873 9.09436C8.93533 9.06808 8.95182 9.03328 8.95535 8.99605C8.95887 8.95882 8.94919 8.92152 8.92799 8.89072C8.40391 8.15902 7.43676 8.03215 7.0784 7.54048C6.01679 6.08413 5.74742 5.25977 5.29122 5.31083C5.25938 5.31541 5.23011 5.33086 5.20836 5.35456L2.09507 8.54614C0.303909 10.3807 0.0464778 14.4244 1.70161e-06 17.8108C-0.000149441 17.8445 0.00977131 17.8774 0.0285177 17.9054C0.0472642 17.9334 0.073971 17.9552 0.105183 17.9679C0.136394 17.9806 0.170678 17.9836 0.203645 17.9767C0.236612 17.9697 0.266753 17.953 0.290164 17.9288V17.9288Z" />
<path d="M17.9286 17.7096L9.09417 9.09042C9.06787 9.06374 9.03301 9.0472 8.99571 9.04368C8.95841 9.04015 8.92106 9.04988 8.89023 9.07116C8.15854 9.59555 8.03164 10.5624 7.53997 10.9207C6.08362 11.9823 5.25928 12.2517 5.31035 12.7079C5.31499 12.7396 5.33042 12.7688 5.35405 12.7905L8.54561 15.9038C10.3802 17.6949 14.4239 17.9524 17.8103 17.9989C17.8438 17.9989 17.8766 17.9889 17.9044 17.9702C17.9322 17.9516 17.9539 17.925 17.9666 17.894C17.9793 17.8629 17.9825 17.8288 17.9757 17.796C17.9689 17.7632 17.9525 17.7331 17.9286 17.7096Z" />
<path d="M8.54566 15.9051C7.54275 14.9266 7.07798 12.9911 9.00001 9C5.87541 10.3964 4.78045 12.2319 5.3189 12.7575L8.54566 15.9051Z" />
<path d="M17.7106 0.0728669L9.09236 8.9073C9.0657 8.93355 9.04915 8.96836 9.04563 9.00561C9.0421 9.04286 9.05184 9.08016 9.0731 9.11094C9.59748 9.84263 10.564 9.96951 10.9226 10.4612C11.9843 11.9175 12.2539 12.7419 12.7098 12.6908C12.7417 12.6863 12.771 12.6708 12.7927 12.6471L15.906 9.45552C17.6971 7.62094 17.9546 3.57722 18.0011 0.190888C18.0011 0.157197 17.9911 0.124255 17.9723 0.0962882C17.9536 0.0683211 17.9269 0.0466009 17.8956 0.0339187C17.8644 0.0212364 17.8301 0.0181717 17.7972 0.02511C17.7642 0.0320483 17.734 0.0486801 17.7106 0.0728669V0.0728669Z" />
<path d="M15.9073 9.45437C14.9289 10.4573 12.9934 10.922 9.00195 9C10.3984 12.1246 12.2339 13.2195 12.7595 12.6811L15.9073 9.45437Z" />
<path d="M0.0713723 0.290483L8.90579 8.90873C8.93207 8.93533 8.96691 8.95185 9.00414 8.95537C9.04137 8.95889 9.07862 8.94921 9.10943 8.92801C9.84112 8.40362 9.96802 7.4368 10.4597 7.07844C11.916 6.01683 12.7404 5.74744 12.6893 5.29124C12.6847 5.25943 12.6692 5.23018 12.6456 5.20838L9.45405 2.09509C7.61947 0.30393 3.57573 0.0464795 0.189393 3.43271e-06C0.155634 -0.000211837 0.12258 0.00970229 0.0945136 0.0284635C0.0664471 0.0472247 0.0446655 0.0739639 0.0319574 0.10524C0.0192493 0.136517 0.0161966 0.170888 0.0232234 0.203908C0.0302503 0.236928 0.0470304 0.267091 0.0713723 0.290483V0.290483Z" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.45586 2.0957C10.4588 3.07415 10.9235 5.00963 9.00146 9.00106C12.1261 7.60464 13.221 5.76884 12.6826 5.24353L9.45586 2.0957Z"
/>
<path d="M2.09521 8.54562C3.07366 7.54271 5.00914 7.07796 9.00058 8.99999C7.60415 5.87539 5.76867 4.78045 5.24307 5.3189L2.09521 8.54562Z" />
<path d="M9.00196 9.38381C9.21474 9.38381 9.38722 9.21132 9.38722 8.99854C9.38722 8.78577 9.21474 8.61328 9.00196 8.61328C8.78919 8.61328 8.6167 8.78577 8.6167 8.99854C8.6167 9.21132 8.78919 9.38381 9.00196 9.38381Z" />
</svg>
),
},
{
x: -12,
y: -3,
color: teal,
resource: "Grafana",
access: "20 users assigned access",
icon: (
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1262_202004)">
<path d="M16.1638 7.96758C16.1347 7.67193 16.0862 7.33266 15.9893 6.95461C15.8923 6.58141 15.7469 6.16944 15.5337 5.74293C15.3204 5.31642 15.0442 4.87537 14.6855 4.44401C14.545 4.27437 14.3899 4.10958 14.2251 3.94964C14.4723 2.96576 13.9246 2.11273 13.9246 2.11273C12.9795 2.05457 12.3785 2.40838 12.1555 2.56833C12.1168 2.55379 12.0828 2.5344 12.0441 2.51986C11.8841 2.45685 11.7193 2.39384 11.5448 2.34053C11.3752 2.28722 11.2007 2.23875 11.0214 2.19513C10.8421 2.15151 10.6627 2.11758 10.4786 2.0885C10.4446 2.08365 10.4156 2.07881 10.3816 2.07396C9.96967 0.755651 8.78707 0.203125 8.78707 0.203125C7.46392 1.04161 7.21674 2.21452 7.21674 2.21452C7.21674 2.21452 7.21189 2.23875 7.2022 2.28237C7.1295 2.30176 7.0568 2.32599 6.9841 2.34538C6.88232 2.37446 6.78054 2.41323 6.6836 2.45201C6.58182 2.49078 6.48489 2.52955 6.38311 2.57317C6.18439 2.66041 5.98568 2.75735 5.79181 2.86398C5.60279 2.9706 5.41861 3.08693 5.23928 3.20809L5.19082 3.18871C3.35876 2.49078 1.73512 3.32926 1.73512 3.32926C1.58487 5.27765 2.46697 6.50387 2.64145 6.72682C2.59783 6.84798 2.55906 6.96915 2.52029 7.09032C2.38458 7.53137 2.2828 7.98212 2.21979 8.45225C2.2101 8.5201 2.2004 8.58796 2.19556 8.65581C0.49921 9.49429 0 11.2052 0 11.2052C1.41039 12.8288 3.05827 12.9306 3.05827 12.9306L3.06312 12.9258C3.27152 13.299 3.51386 13.6528 3.78527 13.9872C3.9016 14.1278 4.01792 14.2586 4.14393 14.3895C3.63018 15.8629 4.21663 17.0843 4.21663 17.0843C5.78696 17.1424 6.81931 16.396 7.03741 16.2264C7.19251 16.2797 7.35245 16.3282 7.51239 16.3669C7.99706 16.493 8.49142 16.5657 8.98579 16.5851C9.10695 16.5899 9.23297 16.5947 9.35414 16.5899H9.52862L9.60616 16.5851V16.5899C10.3477 17.6465 11.6466 17.7967 11.6466 17.7967C12.5723 16.8225 12.6257 15.8532 12.6257 15.6448V15.6302V15.6012V15.5575C12.8195 15.4218 13.0037 15.2764 13.1782 15.1165C13.5465 14.7821 13.8713 14.3992 14.1427 13.9872L14.2154 13.8709C15.2623 13.929 16.0038 13.2214 16.0038 13.2214C15.8293 12.1309 15.209 11.5978 15.0781 11.496C14.9472 11.3942 15.0732 11.4911 15.0635 11.4863C15.0539 11.4815 15.0539 11.4766 15.0539 11.4766C15.049 11.4718 15.0393 11.4669 15.0296 11.4621C15.0345 11.3942 15.0393 11.3312 15.0442 11.2633C15.0539 11.147 15.0539 11.0259 15.0539 10.9095V10.7544V10.7254L15.049 10.6527L15.0442 10.5557C15.0442 10.5218 15.0393 10.4927 15.0345 10.4636C15.0296 10.4346 15.0296 10.4006 15.0248 10.3716L15.0151 10.2795L15.0005 10.1874C14.9812 10.0662 14.9618 9.94989 14.9327 9.82872C14.8212 9.35859 14.637 8.91269 14.3996 8.51041C14.1572 8.10813 13.8567 7.75432 13.5126 7.45382C13.1733 7.15333 12.7904 6.91099 12.393 6.73166C11.9907 6.55233 11.5739 6.43601 11.1571 6.3827C10.9487 6.35362 10.7403 6.34392 10.5319 6.34877H10.3768L10.2992 6.35362C10.2702 6.35362 10.2411 6.35846 10.2168 6.35846C10.1102 6.36816 10.0036 6.3827 9.90181 6.40208C9.485 6.47963 9.09241 6.62988 8.7483 6.83829C8.40418 7.0467 8.10369 7.30358 7.86135 7.59438C7.61902 7.88518 7.42999 8.20991 7.29913 8.54434C7.16827 8.87876 7.09557 9.22773 7.07619 9.56215C7.07134 9.64454 7.07134 9.73178 7.07134 9.81418V9.87719L7.07619 9.94504C7.08103 9.98381 7.08103 10.0274 7.08588 10.0662C7.10042 10.2358 7.13435 10.4006 7.17797 10.5557C7.27005 10.8708 7.41545 11.1567 7.59478 11.3991C7.77411 11.6414 7.99221 11.8401 8.22001 12.0001C8.4478 12.1551 8.69498 12.2666 8.93732 12.3393C9.17965 12.412 9.42199 12.4411 9.64978 12.4411H9.7758C9.79034 12.4411 9.80488 12.4411 9.81942 12.4363C9.84365 12.4363 9.86789 12.4314 9.89212 12.4314C9.89697 12.4314 9.90666 12.4314 9.91151 12.4266L9.93574 12.4217C9.95028 12.4217 9.96482 12.4169 9.97936 12.4169C10.0084 12.412 10.0327 12.4072 10.0618 12.4023C10.0908 12.3975 10.1151 12.3926 10.1393 12.3829C10.1926 12.3732 10.2411 12.3539 10.2895 12.3393C10.3865 12.3054 10.4834 12.2666 10.5658 12.223C10.6531 12.1794 10.7306 12.1261 10.8081 12.0776C10.8275 12.0631 10.8518 12.0485 10.8712 12.0291C10.9487 11.9661 10.9632 11.8498 10.9002 11.7723C10.8469 11.7044 10.75 11.685 10.6724 11.7286L10.6143 11.7577C10.5464 11.7916 10.4786 11.8207 10.4059 11.845C10.3332 11.8692 10.2556 11.8886 10.1781 11.9031C10.1393 11.908 10.1005 11.9128 10.0569 11.9177C10.0375 11.9177 10.0181 11.9225 9.9939 11.9225H9.87758C9.85335 11.9225 9.82911 11.9225 9.80488 11.9177H9.7758C9.7661 11.9177 9.75156 11.9177 9.74187 11.9128C9.71764 11.908 9.69825 11.908 9.67402 11.9031C9.49469 11.8789 9.31536 11.8256 9.14573 11.748C8.97125 11.6705 8.80646 11.5638 8.65621 11.4281C8.50596 11.2924 8.3751 11.1325 8.27332 10.9483C8.17154 10.7641 8.09884 10.5606 8.06491 10.3473C8.05037 10.2407 8.04068 10.1292 8.04553 10.0226C8.04553 9.99351 8.05037 9.96443 8.05037 9.93535V9.89657C8.05037 9.88203 8.05522 9.86749 8.05522 9.85295C8.06007 9.79479 8.06976 9.73663 8.07945 9.67847C8.16185 9.21319 8.39449 8.75759 8.75314 8.41348C8.84523 8.32623 8.94217 8.24869 9.04395 8.17599C9.14573 8.10329 9.2572 8.04028 9.37352 7.98696C9.48984 7.93365 9.60616 7.89003 9.73218 7.8561C9.85335 7.82218 9.97936 7.80279 10.1102 7.78825C10.1732 7.7834 10.2362 7.77855 10.3041 7.77855H10.4543L10.5077 7.7834C10.6482 7.79309 10.7839 7.81248 10.9196 7.84641C11.191 7.90457 11.4576 8.00635 11.7048 8.14206C12.1991 8.41832 12.6208 8.84483 12.8777 9.35859C13.0085 9.61546 13.1006 9.89173 13.1443 10.1777C13.154 10.2504 13.1636 10.3231 13.1685 10.3958L13.1733 10.4491L13.1782 10.5024V10.7108C13.1782 10.7448 13.1733 10.8029 13.1733 10.8368C13.1685 10.9144 13.1588 10.9968 13.1491 11.0743C13.1394 11.1519 13.1249 11.2294 13.1103 11.307C13.0958 11.3845 13.0764 11.4621 13.057 11.5348C13.0182 11.685 12.9698 11.8353 12.9116 11.9855C12.7953 12.2763 12.6402 12.5574 12.456 12.8143C12.0828 13.3281 11.5739 13.7449 10.9923 14.0114C10.7015 14.1423 10.3962 14.2392 10.0811 14.2877C9.92605 14.3168 9.7661 14.3313 9.60616 14.3362H9.36383C9.27659 14.3362 9.19419 14.3313 9.10695 14.3216C8.76768 14.2974 8.43326 14.2344 8.10369 14.1423C7.77896 14.0502 7.46392 13.9194 7.16343 13.7643C6.56728 13.4444 6.0293 13.0082 5.61248 12.4799C5.40407 12.2182 5.2199 11.9322 5.06965 11.6365C4.9194 11.3409 4.79823 11.0259 4.71099 10.7108C4.62375 10.3909 4.57044 10.0662 4.54621 9.73663L4.54136 9.67362V9.39736V9.36343V9.24227C4.54621 9.08232 4.56075 8.91269 4.58013 8.7479C4.59952 8.58311 4.6286 8.41348 4.66253 8.24869C4.69645 8.0839 4.73523 7.91911 4.78369 7.75432C4.87578 7.42959 4.9921 7.11455 5.12781 6.8189C5.40407 6.2276 5.76273 5.69931 6.19409 5.27765C6.30071 5.17102 6.41219 5.07408 6.52851 4.97715C6.64483 4.88506 6.766 4.79782 6.89201 4.71543C7.01318 4.63303 7.14404 4.56033 7.2749 4.49248C7.33791 4.45855 7.40576 4.42462 7.47361 4.39554L7.5754 4.35192L7.67718 4.3083C7.81288 4.25014 7.95344 4.20167 8.09884 4.15805C8.13277 4.14836 8.17154 4.13867 8.20547 4.12412C8.23939 4.11443 8.27817 4.10474 8.31209 4.09504C8.38479 4.07566 8.4575 4.05627 8.5302 4.04173C8.56412 4.03204 8.6029 4.02719 8.64167 4.0175C8.68044 4.0078 8.71437 4.00296 8.75314 3.99326C8.79192 3.98842 8.82584 3.97872 8.86462 3.97388L8.91793 3.96418L8.97609 3.95449C9.01487 3.94964 9.04879 3.9448 9.08757 3.93995C9.13119 3.9351 9.16996 3.93026 9.21358 3.92541C9.24751 3.92056 9.30567 3.91572 9.33959 3.91087C9.36383 3.90602 9.39291 3.90602 9.41714 3.90118L9.47046 3.89633L9.49469 3.89148H9.52377C9.56739 3.88664 9.60616 3.88664 9.64978 3.88179L9.71279 3.87694H9.76126C9.79518 3.87694 9.83396 3.8721 9.86789 3.8721C10.0084 3.86725 10.1538 3.86725 10.2944 3.8721C10.5755 3.88179 10.8518 3.91572 11.1183 3.96418C11.6563 4.06596 12.1604 4.2356 12.6208 4.4634C13.0812 4.68634 13.4884 4.96261 13.847 5.26311C13.8713 5.28249 13.8906 5.30188 13.9149 5.32127C13.9343 5.34065 13.9585 5.36004 13.9779 5.37943C14.0215 5.4182 14.0603 5.45697 14.1039 5.49575C14.1475 5.53452 14.1863 5.5733 14.2251 5.61207C14.2638 5.65084 14.3026 5.68962 14.3414 5.73324C14.4916 5.89318 14.6322 6.05312 14.7582 6.21791C15.0102 6.54264 15.2138 6.87222 15.3737 7.18241L15.4028 7.24057L15.4319 7.29873C15.4513 7.3375 15.4707 7.37628 15.4852 7.41505C15.5046 7.45382 15.5191 7.48775 15.5385 7.52653C15.5531 7.5653 15.5725 7.59923 15.587 7.638C15.6452 7.7834 15.7033 7.92396 15.7469 8.05482C15.8196 8.26807 15.8729 8.4571 15.9166 8.62188C15.9311 8.68974 15.9941 8.73336 16.062 8.72367C16.1347 8.71882 16.188 8.66066 16.188 8.58796C16.1928 8.41348 16.188 8.20507 16.1638 7.96758V7.96758Z" />
</g>
<defs>
<clipPath id="clip0_1262_202004">
<rect width="16.1928" height="18" />
</clipPath>
</defs>
</svg>
),
},
];
// CONFIG
const cubes = {
size: 15,
effectDistance: 75,
rotationMaximum: 0.78,
emissiveLimit: 0.35,
emissiveDefault: 1,
};
const dots = {
size: 2.5,
};
const gravity = {
distance: 100,
power: 0.2,
};
const lighting = {
directionalIntensity: 0.15,
ambientIntensity: 0.25,
pointIntensity: 200,
pointDistance: 250,
cursorIntensity: 1,
cursorDistance: 150,
};
const labels = {
visibilityDistance: 50,
};
const pulse = {
amplitude: 1,
frequency: 0.005,
interval: 8,
distance: 35,
};
const layout = {
dotZ: -40,
cubeZ: -40,
gridIncrements: 50,
};
// CURSORLIGHT
function CursorLight() {
const { viewport } = useThree();
const light = useRef();
useFrame(({ mouse }) => {
if (mouseIsInFrame({ mouse })) {
const x = (mouse.x * viewport.width) / 2;
const y = (mouse.y * viewport.height) / 2;
light.current.position.set(x, y, layout.cubeZ);
}
});
return (
<pointLight
ref={light}
color={0xffffff}
intensity={lighting.cursorIntensity}
distance={lighting.cursorDistance}
decay={2}
/>
);
}
// GRIDDOTS
function GridDots({ nodes, resources }) {
const ref = useRef();
const materialRef = useRef();
const { viewport } = useThree();
let t = 0;
let i = 0;
let graphed = 1;
let normal = 1;
// initialise vectors
let x = 0;
let y = 0;
const nodePos = new Vector3(0, 0, 0);
const mousePos = new Vector3(0, 0, 0);
const resourcePos = new Vector3(0, 0, 0);
const color = new Color(0.35, 0.35, 0.35);
const color2 = new Color(0.35, 0.35, 0.35);
const matrix = new Matrix4();
useFrame(({ mouse, clock }) => {
// Warning: This runs 60 times a second. Avoid var declarations and logging
t += pulse.interval;
// Fade inx
// opacity = Math.min(delta / 1000, 1);
materialRef.current.opacity = Math.min(clock.elapsedTime, 1);
// End fade in
if (mouseIsInFrame({ mouse })) {
updateMousePosition({ mouse, viewport, mousePos });
const { coordinates, distancedNormalised, path, isWithinRange } = getClosestResource({
resources,
mousePos,
minDistance: pulse.distance,
resourcePos,
});
for (i = 0; i < nodes.length; ++i) {
// reset the node position
x = nodes[i].x;
y = nodes[i].y;
nodePos.set(x, y, layout.dotZ);
// calculate positions
updateNodePosition({
nodePos,
mousePos,
gravityDistance: gravity.distance,
gravityPower: gravity.power,
});
// use node position before it is updated with gravity (x,y)
if (isWithinRange && intersectsPath({ path: path, x, y })) {
graphed = graph({
x: nodePos.x - coordinates.x,
y: nodePos.y - coordinates.y,
amplitude: pulse.amplitude,
frequency: pulse.frequency,
time: t,
});
normal = map(graphed, -1, 1, 0, 1, true);
color.set(darkGray);
color.lerpHSL(white, normal);
ref.current.setColorAt(i, color);
ref.current.instanceColor.needsUpdate = true;
} else if (isWithinRange) {
color2.lerpHSL(darkGray, distancedNormalised);
ref.current.setColorAt(i, color2);
ref.current.instanceColor.needsUpdate = true;
} else {
ref.current.setColorAt(i, midGray);
ref.current.instanceColor.needsUpdate = true;
}
matrix.setPosition(nodePos.x, nodePos.y, layout.dotZ);
ref.current.setMatrixAt(i, matrix);
ref.current.instanceMatrix.needsUpdate = true;
}
// gl.render(scene, camera);
}
});
return (
<instancedMesh layers={0} ref={ref} args={[null, null, 10000]}>
<circleGeometry attach="geometry" args={[dots.size]} />
<meshToonMaterial ref={materialRef} attach="material" transparent={true} opacity={0} />
</instancedMesh>
);
}
// RESOURCE CUBES
function ResourceCubes({ nodes }) {
return nodes.map((data, i) => {
return <ResourceCube key={i} i={i} data={data} />;
});
}
function ResourceCube({
i,
data: {
x,
y,
resource: { color },
},
}) {
const ref = useRef();
const materialRef = useRef();
const lightRef = useRef();
const { viewport } = useThree();
let t = i;
// initialise vectors
const nodePos = new Vector3(x, y, 0);
const mousePos = new Vector3(0, 0, 0);
let scaleFactor = 0,
rotationFactor = 0,
emissiveIntensity = 0,
distance = 0;
useFrame(({ mouse, clock }) => {
// Warning: This runs 60 times a second. Avoid var declarations and logging
// advance variables
t += 0.015;
// Fade in
materialRef.current.opacity = Math.min(clock.elapsedTime, 1);
// End fade in
// reset the node position
nodePos.set(x, y, 0);
if (mouseIsInFrame({ mouse })) {
// calculate positions
distance = nodePos.distanceTo(mousePos);
updateMousePosition({ mouse, viewport, mousePos });
updateNodePosition({
nodePos,
mousePos,
gravityDistance: gravity.distance,
gravityPower: gravity.power,
});
// calculate effects
scaleFactor = map(distance, cubes.effectDistance, 0, 1, 2, true);
rotationFactor = map(distance, cubes.effectDistance, 20, 0, cubes.rotationMaximum, true);
emissiveIntensity = map(distance, cubes.effectDistance, 0, cubes.emissiveLimit, 0, true);
// apply effects
ref.current.position.set(nodePos.x, nodePos.y, layout.cubeZ);
ref.current.scale.set(scaleFactor, scaleFactor, scaleFactor);
ref.current.rotation.y = rotationFactor;
ref.current.rotation.x = rotationFactor;
materialRef.current.emissiveIntensity = emissiveIntensity;
lightRef.current.intensity = scaleFactor;
}
lightRef.current.distance = Math.sin(t) * 10 + lighting.pointDistance;
});
return (
<>
<pointLight
ref={lightRef}
color={color}
position={[x, y, 40]}
intensity={lighting.pointIntensity}
distance={lighting.pointDistance}
decay={2}
/>
<mesh ref={ref} geometry={box}>
<meshPhongMaterial
ref={materialRef}
color={color}
emissive={color}
emissiveIntensity={cubes.emissiveDefault}
opacity={0}
transparent={true}
/>
</mesh>
</>
);
}
// RESOURCE LABELS
function Labels({ nodes }) {
return nodes.map((data, index) => <Label key={index} data={data} />);
}
function Label({
data: {
x,
y,
resource: { color, resource, access, icon },
},
}) {
const group = useRef();
const html = useRef();
const tooltip = useRef();
const mousePos = new Vector3(0, 0, 0);
const nodePos = new Vector3(x, y, 0);
let opacity = 0;
let distance = 0;
let hittingRightViewportEdge;
const { viewport } = useThree();
useFrame(({ mouse }) => {
if (mouseIsInFrame({ mouse })) {
updateMousePosition({ mouse, viewport, mousePos });
nodePos.set(x, y, 0);
distance = mousePos.distanceTo(nodePos);
if (distance < labels.visibilityDistance) {
hittingRightViewportEdge = viewport.width / 2 - tooltip.current.offsetWidth > mousePos.x + 20;
group.current.position.set(
hittingRightViewportEdge ? mousePos.x + 20 : viewport.width / 2 - tooltip.current.offsetWidth - 20,
mousePos.y - 20,
0,
);
opacity = map(distance, labels.visibilityDistance, 1, -0.2, 1, true);
tooltip.current.style.display = "grid";
tooltip.current.style.opacity = opacity;
} else {
tooltip.current.style.display = "none";
tooltip.current.style.opacity = 0;
}
}
});
return (
<group ref={group} position={[x, y, 0]}>
<Html style={{ pointerEvents: "none" }} ref={html} position={[0, 0, 0]}>
<div
ref={tooltip}
style={{
opacity: 0,
pointerEvents: "none",
fontFamily: `"InterVariable", sans-serif`,
fontSize: "12px",
display: "grid",
gridTemplateColumns: "auto auto 1fr",
gridGap: "8px",
alignItems: "center",
overflow: "hidden",
whiteSpace: "nowrap",
borderRadius: "4px",
backgroundColor: "#131415",
color: "#F4F4F4",
padding: "8px",
border: "1px solid #A1A1AA",
}}
>
{React.cloneElement(icon, { fill: `#${color.getHexString()}` })}
<p style={{ margin: 0 }}>
<strong>{resource}</strong> {access}
</p>
</div>
</Html>
</group>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment