Created
December 14, 2023 22:25
-
-
Save Hugoberry/752e79ef7b1d634b16a9e7020da164ff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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