implemented knuth-plass line-breaking algorithm

Co-authored-by: pranavm2109 <mishrap@dickinson.edu>
This commit is contained in:
Shahir Ahmed
2025-03-18 15:32:04 -04:00
parent a403f78168
commit 55e1dd0ead

View File

@@ -18,7 +18,7 @@ let maxWidth = 0;
/** @param diagram - The diagram to draw to. */ /** @param diagram - The diagram to draw to. */
function drawActorLegend(diagram) { function drawActorLegend(diagram) {
const conf = getConfig().journey; const conf = getConfig().journey;
maxWidth = 0; maxWidth = conf.maxLabelWidth; // Ensures we don't exceed this width
let yPos = 60; let yPos = 60;
Object.keys(actors).forEach((person) => { Object.keys(actors).forEach((person) => {
@@ -33,67 +33,62 @@ function drawActorLegend(diagram) {
}; };
svgDraw.drawCircle(diagram, circleData); svgDraw.drawCircle(diagram, circleData);
// Create temporary text element to measure width const words = person.split(' '); // Split text into words
const tempText = diagram.append('text').attr('visibility', 'hidden').text(person); const lines = [];
const textWidth = tempText.node().getBBox().width; let currentLine = '';
tempText.remove();
const journeyConfigObject = getConfig().journey;
const maxLineLength = journeyConfigObject.maxLabelWidth;
let lines = [];
if (textWidth > maxLineLength) {
// Break the text into chunks regardless of word boundaries
let currentText = '';
const measureText = diagram.append('text').attr('visibility', 'hidden'); const measureText = diagram.append('text').attr('visibility', 'hidden');
for (const element of person) { words.forEach((word, _index) => {
currentText += element; const testLine = currentLine ? `${currentLine} ${word}` : word;
measureText.text(currentText); measureText.text(testLine);
const currentWidth = measureText.node().getBBox().width; const textWidth = measureText.node().getBBox().width;
if (currentWidth > maxLineLength && currentText.length > 1) { if (textWidth > maxWidth) {
let lineToPush = currentText.slice(0, -1); if (currentLine) {
lines.push(currentLine); // Push previous line before adding a new word
}
currentLine = word;
// If the line ends with a space, trim it and do not add a hyphen. // If a single word is too long, break it
if (lineToPush.endsWith(' ')) { if (measureText.node().getBBox().width > maxWidth) {
lineToPush = lineToPush.trimEnd(); let brokenWord = '';
for (const char of word) {
brokenWord += char;
measureText.text(brokenWord + '-');
if (measureText.node().getBBox().width > maxWidth) {
lines.push(brokenWord.slice(0, -1) + '-'); // Break word with a hyphen
brokenWord = char;
}
}
currentLine = brokenWord;
}
} else { } else {
lineToPush = lineToPush + '-'; currentLine = testLine;
} }
lines.push(lineToPush); });
// If the breaking character is a space, start fresh; otherwise, start with the character. if (currentLine) {
currentText = element === ' ' ? '' : element; lines.push(currentLine);
}
}
if (currentText) {
lines.push(currentText);
} }
measureText.remove(); measureText.remove();
} else {
lines = [person];
}
// Draw the text lines // Draw the text lines within the fixed width
lines.forEach((line, index) => { lines.forEach((line, index) => {
const labelData = { const labelData = {
x: 40, x: 40,
y: yPos + 7 + index * 20, y: yPos + 7 + index * 20,
fill: '#666', fill: '#666',
text: line, text: line,
textMargin: conf.boxTextMargin | 5, textMargin: conf.boxTextMargin || 5,
}; };
const textElement = svgDraw.drawText(diagram, labelData); svgDraw.drawText(diagram, labelData);
const lineWidth = textElement.node().getBBox().width;
if (lineWidth > maxWidth && lineWidth > conf?.leftMargin - lineWidth) {
maxWidth = lineWidth;
}
}); });
yPos += Math.max(20, lines.length * 20); yPos += Math.max(20, lines.length * 20);
}); });
} }
// TODO: Cleanup? // TODO: Cleanup?
const conf = getConfig().journey; const conf = getConfig().journey;
let leftMargin = 0; let leftMargin = 0;