Skip to content

Instantly share code, notes, and snippets.

@Hugoberry
Created December 14, 2023 22:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hugoberry/752e79ef7b1d634b16a9e7020da164ff to your computer and use it in GitHub Desktop.
Save Hugoberry/752e79ef7b1d634b16a9e7020da164ff to your computer and use it in GitHub Desktop.
// Function to wrangle data into the required format
function wrangle(data) {
let charactersMap = {};
// Helper function to get characters by ID
function characterById(id) {
if (!charactersMap[id]) {
charactersMap[id] = data.characters.find(character => character.id === id);
}
return charactersMap[id];
}
return data.scenes.map(scene => ({
characters: scene.map(characterById).filter(Boolean)
}));
}
// Main function to handle visualization
function main(container) {
const scenes = wrangle(data);
const sceneWidth = 14;
const width = scenes.length * sceneWidth * 4;
const height = 300;
// Initialize the narrative layout
const narrative = initializeNarrative(scenes, width, height);
// Set up the graph
const graph = setUpGraph(container);
// Populate the graph with data
populateGraph(graph, narrative, sceneWidth);
// Encode and log the graph
logGraph(graph);
}
// Function to initialize the narrative layout
function initializeNarrative(scenes, width, height) {
return d3.layout.narrative()
.scenes(scenes)
.size([width, height])
.pathSpace(10)
.groupMargin(20)
.labelSize([110, 15])
.scenePadding([5, width / (2 * scenes.length), 5, width / (2 * scenes.length)])
.labelPosition('left')
.layout();
}
// Function to set up the graph
function setUpGraph(container) {
mxEvent.disableContextMenu(container);
const graph = new mxGraph(container);
graph.setEnabled(false);
graph.setConnectable(false);
graph.setCellsResizable(false);
const style = {
[mxConstants.STYLE_EDGE]: mxEdgeStyle.SegmentConnector,
[mxConstants.STYLE_ENDARROW]: mxConstants.NONE,
[mxConstants.STYLE_CURVED]: 1
};
graph.getStylesheet().putDefaultEdgeStyle(style);
return graph;
}
// Function to populate the graph with data
function populateGraph(graph, narrative, sceneWidth) {
const model = graph.getModel();
const parent = graph.getDefaultParent();
model.beginUpdate();
try {
const { appearanceVertices, introVertices } = drawScenes(graph, parent, narrative, sceneWidth);
drawLinks(graph, parent, narrative, appearanceVertices, introVertices);
} finally {
model.endUpdate();
}
}
// Function to draw scenes and return vertices
function drawScenes(graph, parent, narrative, sceneWidth) {
const appearanceVertices = {};
const introVertices = {};
const scenes = narrative.scenes();
scenes.forEach(scene => {
const cluster = graph.insertVertex(
parent, null, '', Math.round(scene.x) + 0.5, Math.round(scene.y) + 0.5, sceneWidth, scene.height,
'rounded=1;arcSize=30;'
);
cluster.setConnectable(false);
scene.appearances.forEach(appearance => {
const characterId = appearance.character.id;
const appearanceVertex = graph.insertVertex(
cluster, null, '', appearance.x - 2, appearance.y, 4, 4,
'shape=ellipse;fillColor=black;strokeColor=black;'
);
if (!appearanceVertices[characterId]) {
appearanceVertices[characterId] = {};
}
appearanceVertices[characterId][scene.start] = appearanceVertex;
});
});
narrative.introductions().forEach(d => {
const intro = graph.insertVertex(
parent, null, d.character.name, Math.round(d.x), Math.round(d.y), 110, 15,
'align=right'
);
introVertices[d.character.id] = intro;
});
return { appearanceVertices, introVertices };
}
// Function to draw links between characters
function drawLinks(graph, parent, narrative, appearanceVertices, introVertices) {
const links = narrative.links();
links.forEach(link => {
const sourceCharacterId = link.source.character.id;
const targetCharacterId = link.target.character.id;
const sourceSceneStart = link.source.scene ? link.source.scene.start : null;
const targetSceneStart = link.target.scene ? link.target.scene.start : null;
const sourceVertex = getVertex(sourceCharacterId, sourceSceneStart, true, appearanceVertices, introVertices);
const targetVertex = getVertex(targetCharacterId, targetSceneStart, false, appearanceVertices, introVertices);
if (sourceVertex && targetVertex) {
graph.insertEdge(parent, null, '', sourceVertex, targetVertex, 'curved=1;endArrow=None');
}
});
}
// Helper function to get the vertex
function getVertex(characterId, sceneStart, isSource, appearanceVertices, introVertices) {
return isSource && sceneStart == null ?
introVertices[characterId] :
appearanceVertices[characterId] && appearanceVertices[characterId][sceneStart];
}
// Function to encode and log the graph
function logGraph(graph) {
const encoder = new mxCodec();
const result = encoder.encode(graph.getModel());
const xml = mxUtils.getXml(result);
console.log(xml);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment