Getting closer

This commit is contained in:
chris moran 2020-06-28 08:37:26 -04:00
parent 6915634729
commit fcd2126330
No known key found for this signature in database
GPG Key ID: 7E303019E6BB02D7
4 changed files with 144 additions and 116 deletions

View File

@ -292,9 +292,9 @@ const config = {
wrap: false,
/**
* This sets the auto-wrap padding for the diagram (sides only)
* **Default value 15.
* **Default value 10.
*/
wrapPadding: 15,
wrapPadding: 10,
/**
* This sets the width of the loop-box (loop, alt, opt, par)
* **Default value 50.
@ -306,24 +306,27 @@ const config = {
*/
labelBoxHeight: 20,
messageFont: () => {
const c = getConfig();
return {
fontFamily: config.messageFontFamily,
fontSize: config.messageFontSize,
fontWeight: config.messageFontWeight
fontFamily: c.messageFontFamily,
fontSize: c.messageFontSize,
fontWeight: c.messageFontWeight
};
},
noteFont: () => {
const c = getConfig();
return {
fontFamily: config.noteFontFamily,
fontSize: config.noteFontSize,
fontWeight: config.noteFontWeight
fontFamily: c.noteFontFamily,
fontSize: c.noteFontSize,
fontWeight: c.noteFontWeight
};
},
actorFont: () => {
const c = getConfig();
return {
fontFamily: config.actorFontFamily,
fontSize: config.actorFontSize,
fontWeight: config.actorFontWeight
fontFamily: c.actorFontFamily,
fontSize: c.actorFontSize,
fontWeight: c.actorFontWeight
};
}
},

View File

@ -136,7 +136,7 @@ export const bounds = {
},
newActivation: function(message, diagram, actors) {
const actorRect = actors[message.from.actor];
const stackedSize = actorActivations(message.from.actor).length;
const stackedSize = actorActivations(message.from.actor).length || 0;
const x = actorRect.x + actorRect.width / 2 + ((stackedSize - 1) * conf.activationWidth) / 2;
this.activations.push({
startx: x,
@ -248,13 +248,16 @@ const drawNote = function(elem, noteModel) {
* @param msgModel - the model containing fields describing a message
*/
const drawMessage = function(g, msgModel) {
bounds.bumpVerticalPos(conf.messageMargin);
msgModel.height += conf.messageMargin;
msgModel.starty = bounds.getVerticalPos();
const { startx, stopx, starty: verticalPos, message, type, sequenceIndex, wrap } = msgModel;
const { startx, stopx, starty, message, type, sequenceIndex, wrap } = msgModel;
const lines = message.split(common.lineBreakRegex).length;
let textDims = utils.calculateTextDimensions(message, conf);
const lineHeight = textDims.height / lines;
msgModel.height += lineHeight;
bounds.bumpVerticalPos(lineHeight);
const textObj = svgDraw.getTextObj();
textObj.x = startx;
textObj.y = verticalPos;
textObj.y = starty;
textObj.width = stopx - startx;
textObj.class = 'messageText';
textObj.dy = '1em';
@ -268,18 +271,11 @@ const drawMessage = function(g, msgModel) {
textObj.tspan = false;
textObj.wrap = wrap;
let textElem = drawText(g, textObj);
const lineHeight = (textElem[0]._groups || textElem[0])[0][0].getBBox().height;
textElem.forEach(te => te.attr('y', verticalPos - 7 - lineHeight / 2));
drawText(g, textObj);
const lines = message.split(common.lineBreakRegex).length - 1;
let totalOffset = textDims.height;
let totalOffset = Math.round(lineHeight + lines * lineHeight);
let textWidth = Math.max.apply(
null,
textElem.map(te => (te._groups || te)[0][0].getBBox().width)
);
let textWidth = textDims.width;
let line;
if (startx === stopx) {
@ -288,7 +284,8 @@ const drawMessage = function(g, msgModel) {
.append('path')
.attr(
'd',
`M ${startx},${verticalPos + totalOffset} H ${startx + conf.width / 2} V ${verticalPos +
`M ${startx},${bounds.getVerticalPos() + totalOffset} H ${startx +
Math.max(conf.width / 2, textWidth / 2)} V ${bounds.getVerticalPos() +
25 +
totalOffset} H ${startx}`
);
@ -302,49 +299,43 @@ const drawMessage = function(g, msgModel) {
'M ' +
startx +
',' +
(verticalPos + totalOffset) +
(bounds.getVerticalPos() + totalOffset) +
' C ' +
(startx + 60) +
',' +
(verticalPos - 10 + totalOffset) +
(bounds.getVerticalPos() - 10 + totalOffset) +
' ' +
(startx + 60) +
',' +
(verticalPos + 30 + totalOffset) +
(bounds.getVerticalPos() + 30 + totalOffset) +
' ' +
startx +
',' +
(verticalPos + 20 + totalOffset)
(bounds.getVerticalPos() + 20 + totalOffset)
);
}
bounds.bumpVerticalPos(30);
msgModel.height += 30;
const dx = Math.max(textWidth / 2, 100);
totalOffset += 30;
const dx = Math.max(textWidth / 2, conf.width / 2);
bounds.insert(
startx - dx,
bounds.getVerticalPos() - 10 + totalOffset,
stopx + dx,
bounds.getVerticalPos() + 30 + totalOffset
);
bounds.bumpVerticalPos(10);
msgModel.height += 10;
} else {
totalOffset += conf.boxMargin;
line = g.append('line');
line.attr('x1', startx);
line.attr('y1', verticalPos + totalOffset);
line.attr('y1', bounds.getVerticalPos() + totalOffset);
line.attr('x2', stopx);
line.attr('y2', verticalPos + totalOffset);
bounds.bumpVerticalPos(10);
msgModel.height += 10;
line.attr('y2', bounds.getVerticalPos() + totalOffset);
bounds.insert(
startx,
bounds.getVerticalPos() - 10 + totalOffset,
stopx,
bounds.getVerticalPos() + totalOffset
);
msgModel.height += 10;
bounds.bumpVerticalPos(10);
}
// Make an SVG Container
// Draw the line
@ -387,7 +378,7 @@ const drawMessage = function(g, msgModel) {
line.attr('marker-start', 'url(' + url + '#sequencenumber)');
g.append('text')
.attr('x', startx)
.attr('y', verticalPos + 4 + totalOffset)
.attr('y', bounds.getVerticalPos() + 4 + totalOffset)
.attr('font-family', 'sans-serif')
.attr('font-size', '12px')
.attr('text-anchor', 'middle')
@ -395,9 +386,10 @@ const drawMessage = function(g, msgModel) {
.attr('class', 'sequenceNumber')
.text(sequenceIndex);
}
bounds.bumpVerticalPos(totalOffset);
msgModel.height += totalOffset;
msgModel.stopy = msgModel.starty + msgModel.height;
bounds.insert(msgModel.fromBounds, msgModel.starty, msgModel.toBounds, msgModel.stopy);
logger.debug(`mm.h:${msgModel.height} vs c.h:${msgModel.stopy - msgModel.starty}`);
};
export const drawActors = function(diagram, actors, actorKeys, verticalPos) {
@ -472,8 +464,11 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
msg.message = utils.wrapLabel(`[${msg.message}]`, loopWidth - 2 * conf.wrapPadding, textConf);
msg.width = loopWidth;
const textHeight = utils.calculateTextHeight(msg.message, textConf);
heightAdjust += textHeight;
// const lines = msg.message.split(common.lineBreakRegex).length;
const textDims = utils.calculateTextDimensions(msg.message, textConf);
const totalOffset = textDims.height - conf.labelBoxHeight;
heightAdjust = postMargin + totalOffset;
logger.debug(`${totalOffset} - ${msg.message}`);
}
addLoopFn(msg);
bounds.bumpVerticalPos(heightAdjust);
@ -488,8 +483,9 @@ export const draw = function(text, id) {
parser.yy.clear();
parser.yy.setWrap(conf.wrap);
parser.parse(text + '\n');
bounds.init();
logger.debug(`C:${JSON.stringify(conf, null, 2)}`);
const diagram = select(`[id="${id}"]`);
// Fetch data from the parsing
@ -553,7 +549,7 @@ export const draw = function(text, id) {
break;
case parser.yy.LINETYPE.LOOP_END:
loopModel = bounds.endLoop();
svgDraw.drawLoop(diagram, loopModel, 'loop', conf, bounds);
svgDraw.drawLoop(diagram, loopModel, 'loop', conf);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
bounds.models.addLoop(loopModel);
break;
@ -635,6 +631,7 @@ export const draw = function(text, id) {
try {
// lastMsg = msg
msgModel = msg.msgModel;
msgModel.starty = bounds.getVerticalPos();
msgModel.sequenceIndex = sequenceIndex;
drawMessage(diagram, msgModel);
bounds.models.addMessage(msgModel);
@ -706,7 +703,7 @@ export const draw = function(text, id) {
' ' +
(height + extraVertForTitle)
);
logger.debug(`models: ${JSON.stringify(bounds.models, null, 2)}`);
logger.debug(`models:`, bounds.models);
};
/**
@ -767,6 +764,11 @@ const getMaxMessageWidthPerActor = function(actors, messages) {
maxMessageWidthPerActor[msg.to] || 0,
messageWidth
);
} else if (isMessage && msg.from === actor.prevActor) {
maxMessageWidthPerActor[msg.from] = Math.max(
maxMessageWidthPerActor[msg.from] || 0,
messageWidth
);
} else if (msg.placement === parser.yy.PLACEMENT.RIGHTOF) {
maxMessageWidthPerActor[msg.from] = Math.max(
maxMessageWidthPerActor[msg.from] || 0,
@ -861,7 +863,6 @@ const buildNoteModel = function(msg, actors) {
shouldWrap ? utils.wrapLabel(msg.message, conf.width, conf.noteFont()) : msg.message,
conf.noteFont()
);
logger.debug(`TD:[${textDimensions.width},${textDimensions.height}]`);
let noteModel = {
width: shouldWrap
? conf.width
@ -892,14 +893,18 @@ const buildNoteModel = function(msg, actors) {
} else if (msg.to === msg.from) {
textDimensions = utils.calculateTextDimensions(
shouldWrap
? utils.wrapLabel(msg.message, Math.max(conf.width, actors[msg.to].width), conf.noteFont())
? utils.wrapLabel(
msg.message,
Math.max(conf.width, actors[msg.from].width),
conf.noteFont()
)
: msg.message,
conf.noteFont()
);
noteModel.width = shouldWrap
? Math.max(conf.width, actors[msg.to].width)
: Math.max(actors[msg.to].width, conf.width, textDimensions.width + 2 * conf.noteMargin);
noteModel.startx = startx + (actors[msg.to].width - noteModel.width) / 2;
? Math.max(conf.width, actors[msg.from].width)
: Math.max(actors[msg.from].width, conf.width, textDimensions.width + 2 * conf.noteMargin);
noteModel.startx = startx + (actors[msg.from].width - noteModel.width) / 2;
} else {
noteModel.width =
Math.abs(startx + actors[msg.from].width / 2 - (stopx + actors[msg.to].width / 2)) +
@ -916,6 +921,9 @@ const buildNoteModel = function(msg, actors) {
conf.noteFont()
);
}
logger.debug(
`NM:[${noteModel.startx},${noteModel.stopx},${noteModel.starty},${noteModel.stopy}:${noteModel.width},${noteModel.height}=${msg.message}]`
);
return noteModel;
};
@ -957,7 +965,7 @@ const buildMessageModel = function(msg, actors) {
if (msg.wrap && msg.message && !common.lineBreakRegex.test(msg.message)) {
msgModel.message = utils.wrapLabel(
msg.message,
Math.max(msgModel.width, conf.width),
Math.max(msgModel.width - 2 * conf.wrapPadding, conf.width),
conf.messageFont()
);
}
@ -1028,44 +1036,31 @@ const calculateLoopBounds = function(messages, actors) {
if (isNote) {
noteModel = buildNoteModel(msg, actors);
msg.noteModel = noteModel;
let depth = 0;
stack.forEach(stk => {
current = stk;
current.from = Math.min(current.from, noteModel.startx);
current.to = Math.max(current.to, noteModel.startx + noteModel.width);
current.width =
Math.max(current.width, Math.abs(current.from - current.to)) -
50 -
conf.boxMargin * depth;
depth++;
Math.max(current.width, Math.abs(current.from - current.to)) - conf.labelBoxWidth;
});
} else {
msgModel = buildMessageModel(msg, actors);
msg.msgModel = msgModel;
if (msg.from && msg.to && stack.length > 0) {
let depth = 0;
if (msgModel.startx && msgModel.stopx && stack.length > 0) {
stack.forEach(stk => {
current = stk;
let from = actors[msg.from];
let to = actors[msg.to];
if (from.x === to.x) {
current.from = Math.min(current.from, from.x);
current.to = Math.max(current.to, to.x);
current.width = Math.max(current.width, from.width) - 50 - conf.boxMargin * depth;
} else {
if (from.x < to.x) {
current.from = Math.min(current.from, from.x);
current.to = Math.max(current.to, to.x);
} else {
current.from = Math.min(current.from, to.x);
current.to = Math.max(current.to, from.x);
}
if (msgModel.startx === msgModel.stopx) {
let from = actors[msg.from];
let to = actors[msg.to];
current.from = Math.min(from.x - from.width / 2, current.from);
current.to = Math.max(to.x + from.width / 2, current.to);
current.width =
Math.max(current.width, Math.abs(current.from - current.to)) -
50 -
conf.boxMargin * depth;
Math.max(current.width, Math.abs(current.to - current.from)) - conf.labelBoxWidth;
} else {
current.from = Math.min(msgModel.startx, current.from);
current.to = Math.max(msgModel.stopx, current.to);
current.width = Math.max(current.width, msgModel.width) - conf.labelBoxWidth;
}
depth++;
});
}
}

View File

@ -1,4 +1,6 @@
import common from '../common/common';
import utils from '../../utils';
import { logger } from '../../logger';
export const drawRect = function(elem, rectData) {
const rectElem = elem.append('rect');
@ -36,18 +38,21 @@ export const drawText = function(elem, textData) {
switch (textData.valign) {
case 'top':
case 'start':
yfunc = () => textData.y + textData.textMargin;
yfunc = () => Math.round(textData.y + textData.textMargin);
break;
case 'middle':
case 'center':
yfunc = () => textData.y + (prevTextHeight + textHeight + textData.textMargin) / 2;
yfunc = () =>
Math.round(textData.y + (prevTextHeight + textHeight + textData.textMargin) / 2);
break;
case 'bottom':
case 'end':
yfunc = () =>
textData.y +
(prevTextHeight + textHeight + 2 * textData.textMargin) -
textData.textMargin;
Math.round(
textData.y +
(prevTextHeight + textHeight + 2 * textData.textMargin) -
textData.textMargin
);
break;
}
}
@ -59,21 +64,21 @@ export const drawText = function(elem, textData) {
switch (textData.anchor) {
case 'left':
case 'start':
textData.x = textData.x + textData.textMargin;
textData.x = Math.round(textData.x + textData.textMargin);
textData.anchor = 'start';
textData.dominantBaseline = 'text-after-edge';
textData.alignmentBaseline = 'middle';
break;
case 'middle':
case 'center':
textData.x = textData.x + textData.width / 2;
textData.x = Math.round(textData.x + textData.width / 2);
textData.anchor = 'middle';
textData.dominantBaseline = 'middle';
textData.alignmentBaseline = 'middle';
break;
case 'right':
case 'end':
textData.x = textData.x + textData.width - textData.textMargin;
textData.x = Math.round(textData.x + textData.width - textData.textMargin);
textData.anchor = 'end';
textData.dominantBaseline = 'text-before-edge';
textData.alignmentBaseline = 'middle';
@ -256,6 +261,15 @@ export const drawActivation = function(elem, bounds, verticalPos, conf, actorAct
* @param conf - diagrom configuration
*/
export const drawLoop = function(elem, loopModel, labelText, conf) {
const {
boxMargin,
boxTextMargin,
labelBoxHeight,
labelBoxWidth,
messageFontFamily: fontFamily,
messageFontSize: fontSize,
messageFontWeight: fontWeight
} = conf;
const g = elem.append('g');
const drawLoopLine = function(startx, starty, stopx, stopy) {
return g
@ -283,45 +297,51 @@ export const drawLoop = function(elem, loopModel, labelText, conf) {
txt.text = labelText;
txt.x = loopModel.startx;
txt.y = loopModel.starty;
const msgFont = conf.messageFont();
txt.fontFamily = msgFont.fontFamily;
txt.fontSize = msgFont.fontSize;
txt.fontWeight = msgFont.fontWeight;
txt.fontFamily = fontFamily;
txt.fontSize = fontSize;
txt.fontWeight = fontWeight;
txt.anchor = 'middle';
txt.valign = 'middle';
txt.tspan = false;
txt.width = conf.labelBoxWidth || 50;
txt.height = conf.labelBoxHeight || 20;
txt.textMargin = conf.boxTextMargin;
txt.width = labelBoxWidth || 50;
txt.height = labelBoxHeight || 20;
txt.textMargin = boxTextMargin;
txt.class = 'labelText';
drawLabel(g, txt);
txt = getTextObj();
txt.text = loopModel.title;
txt.x = loopModel.startx + conf.labelBoxWidth / 2 + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.starty + conf.boxMargin + conf.boxTextMargin;
txt.x = loopModel.startx + labelBoxWidth / 2 + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.starty + boxMargin + boxTextMargin;
txt.anchor = 'middle';
txt.valign = 'middle';
txt.textMargin = boxTextMargin;
txt.class = 'loopText';
txt.fontFamily = msgFont.fontFamily;
txt.fontSize = msgFont.fontSize;
txt.fontWeight = msgFont.fontWeight;
txt.fontFamily = fontFamily;
txt.fontSize = fontSize;
txt.fontWeight = fontWeight;
txt.wrap = true;
let textElem = drawText(g, txt);
let textHeight = Math.round(
textElem.map(te => (te._groups || te)[0][0].getBBox().height).reduce((acc, curr) => acc + curr)
);
const textDims = utils.calculateTextDimensions(txt.text, txt);
logger.debug(`loop: ${textHeight} vs ${textDims.height} ${txt.text}`, textDims);
if (typeof loopModel.sectionTitles !== 'undefined') {
loopModel.sectionTitles.forEach(function(item, idx) {
if (item.message) {
txt.text = item.message;
txt.x = loopModel.startx + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.sections[idx].y + conf.boxMargin + conf.boxTextMargin;
txt.y = loopModel.sections[idx].y + boxMargin + boxTextMargin;
txt.class = 'loopText';
txt.anchor = 'middle';
txt.valign = 'middle';
txt.tspan = false;
txt.fontFamily = msgFont.fontFamily;
txt.fontSize = msgFont.fontSize;
txt.fontWeight = msgFont.fontWeight;
txt.fontFamily = fontFamily;
txt.fontSize = fontSize;
txt.fontWeight = fontWeight;
txt.wrap = loopModel.wrap;
textElem = drawText(g, txt);
let sectionHeight = Math.round(
@ -329,7 +349,7 @@ export const drawLoop = function(elem, loopModel, labelText, conf) {
.map(te => (te._groups || te)[0][0].getBBox().height)
.reduce((acc, curr) => acc + curr)
);
loopModel.sections[idx].height += sectionHeight - (conf.boxMargin + conf.boxTextMargin);
loopModel.sections[idx].height += sectionHeight - (boxMargin + boxTextMargin);
}
});
}

View File

@ -627,21 +627,21 @@ export const calculateTextDimensions = function(text, config) {
// of sans-serif.
const fontFamilies = ['sans-serif', fontFamily];
const lines = text.split(common.lineBreakRegex);
let maxWidth = 0,
height = 0;
let dims = [];
const body = select('body');
// We don't want to leak DOM elements - if a removal operation isn't available
// for any reason, do not continue.
if (!body.remove) {
return { width: 0, height: 0 };
return { width: 0, height: 0, lineHeight: 0 };
}
const g = body.append('svg');
for (let line of lines) {
for (let fontFamily of fontFamilies) {
let cheight = 0;
for (let fontFamily of fontFamilies) {
let dim = { width: 0, height: 0, lineHeight: 0 };
for (let line of lines) {
const textObj = getTextObj();
textObj.text = line;
const textElem = drawSimpleText(g, textObj)
@ -650,16 +650,26 @@ export const calculateTextDimensions = function(text, config) {
.style('font-family', fontFamily);
let bBox = (textElem._groups || textElem)[0][0].getBBox();
maxWidth = Math.max(maxWidth, bBox.width);
cheight = Math.max(bBox.height, cheight);
dim.width = Math.round(Math.max(dim.width, bBox.width));
cheight = Math.round(bBox.height);
dim.height += cheight;
dim.lineHeight = Math.round(Math.max(dim.lineHeight, cheight));
}
height += cheight;
dims.push(dim);
}
g.remove();
// Adds some padding, so the text won't sit exactly within the actor's borders
const result = { width: Math.round(maxWidth), height: Math.round(height) };
let index =
isNaN(dims[1].height) ||
isNaN(dims[1].width) ||
isNaN(dims[1].lineHeight) ||
(dims[0].height > dims[1].height &&
dims[0].width > dims[1].width &&
dims[0].lineHeight > dims[1].lineHeight)
? 0
: 1;
const result = dims[index];
calculateTextDimensions[cacheKey] = result;
return result;
};