Five silhouettes share one instanced render path and the same exact pointer geometry.
Hover a mark · none active
CIRCLE · SQUARE · DIAMOND · TRIANGLE · HEXAGON
// A node's `mark` selects its silhouette while all other graph behavior stays identical.
// OGJS uses the same renderer and exact hit geometry for every shape, so applications do
// not maintain separate drawing or pointer code for circles, diamonds, or polygons.
import { Ogjs } from '../../src/index';
// Mark names are graph data, so one dataset can mix silhouettes without installing custom
// drawing callbacks or changing how nodes are selected and addressed.
const marks = ['circle', 'square', 'diamond', 'triangle', 'hexagon'];
const colors = ['#5b7fba', '#8064b5', '#c96b78', '#d49a3f', '#4f9477'];
const og = new Ogjs({ container: '#viz', theme: 'light' });
og.setGraph({
nodes: marks.map((mark, index) => ({
id: mark,
label: mark[0].toUpperCase() + mark.slice(1),
x: (index - 2) * 125,
y: 0,
size: 32,
color: colors[index],
mark,
borderWidth: 2,
borderColor: '#ffffff',
})),
edges: [],
});
og.camera.fit({ target: 'all', paddingPx: 24, maxOccupancy: .64 });
const status = document.querySelector('#mark-status');
const state = { marks, colors, hover: null };
og.on('nodehover', ({ nodeIndex }) => {
// Hover returns the normal graph index for every silhouette. OGJS performs exact shape hit
// testing and the application continues to work with stable node ids.
state.hover = String(og.graph.getNodeId(nodeIndex));
status.textContent = `Hover a mark · ${state.hover} active`;
});
og.on('nodeout', () => {
state.hover = null;
status.textContent = 'Hover a mark · none active';
});
<!doctype html>
<html lang="en"><head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Ogjs example — Analytic node marks</title>
<link rel="stylesheet" href="../site.css"/><link rel="stylesheet" href="./data-lifecycle.css"/>
<style>#viz { left: 452px; } @media (max-width: 760px) { #viz { top: 270px; left: 0; } }</style>
</head><body>
<header class="rail"><a class="wordmark" href="../index.html">OG<b>JS</b></a><nav>
<a href="../docs.html">Docs</a><a href="../demos.html">Demos</a>
<a href="./index.html" aria-current="page">Examples</a></nav></header>
<div id="viz"></div>
<section class="workflow-panel"><h1>Analytic node marks</h1>
<p>Five silhouettes share one instanced render path and the same exact pointer geometry.</p>
<div class="status" id="mark-status" aria-live="polite">Hover a mark · none active</div>
<div class="status">CIRCLE · SQUARE · DIAMOND · TRIANGLE · HEXAGON</div>
</section>
<script type="module">
// A node's `mark` selects its silhouette while all other graph behavior stays identical.
// OGJS uses the same renderer and exact hit geometry for every shape, so applications do
// not maintain separate drawing or pointer code for circles, diamonds, or polygons.
import { Ogjs } from '../../src/index';
// Mark names are graph data, so one dataset can mix silhouettes without installing custom
// drawing callbacks or changing how nodes are selected and addressed.
const marks = ['circle', 'square', 'diamond', 'triangle', 'hexagon'];
const colors = ['#5b7fba', '#8064b5', '#c96b78', '#d49a3f', '#4f9477'];
const og = new Ogjs({ container: '#viz', theme: 'light' });
og.setGraph({
nodes: marks.map((mark, index) => ({
id: mark,
label: mark[0].toUpperCase() + mark.slice(1),
x: (index - 2) * 125,
y: 0,
size: 32,
color: colors[index],
mark,
borderWidth: 2,
borderColor: '#ffffff',
})),
edges: [],
});
og.camera.fit({ target: 'all', paddingPx: 24, maxOccupancy: .64 });
const status = document.querySelector('#mark-status');
const state = { marks, colors, hover: null };
og.on('nodehover', ({ nodeIndex }) => {
// Hover returns the normal graph index for every silhouette. OGJS performs exact shape hit
// testing and the application continues to work with stable node ids.
state.hover = String(og.graph.getNodeId(nodeIndex));
status.textContent = `Hover a mark · ${state.hover} active`;
});
og.on('nodeout', () => {
state.hover = null;
status.textContent = 'Hover a mark · none active';
});
</script></body></html>