UNPKG

@kieler/klighd-core

Version:

Core KLighD diagram visualization with Sprotty

741 lines 35.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_LINE_CAP_SVG = exports.DEFAULT_CORNER_HEIGHT = exports.DEFAULT_CORNER_WIDTH = exports.DEFAULT_SHADOW_DEF = exports.DEFAULT_SHADOW = exports.DEFAULT_K_VERTICAL_ALIGNMENT = exports.DEFAULT_VERTICAL_ALIGNMENT = exports.DEFAULT_FOREGROUND = exports.DEFAULT_CLICKABLE_FILL = exports.DEFAULT_FILL = exports.DEFAULT_K_LINE_WIDTH = exports.DEFAULT_LINE_WIDTH = exports.DEFAULT_K_LINE_STYLE = exports.DEFAULT_LINE_STYLE = exports.DEFAULT_K_LINE_JOIN = exports.DEFAULT_MITER_LIMIT = exports.DEFAULT_LINE_JOIN = exports.DEFAULT_K_LINE_CAP = exports.DEFAULT_LINE_CAP = exports.DEFAULT_K_INVISIBILITY = exports.DEFAULT_INVISIBILITY = exports.DEFAULT_K_HORIZONTAL_ALIGNMENT = exports.DEFAULT_HORIZONTAL_ALIGNMENT = exports.DEFAULT_K_FONT_SIZE = exports.DEFAULT_FONT_SIZE = exports.DEFAULT_K_FONT_NAME = exports.DEFAULT_FONT_NAME = exports.DEFAULT_K_FONT_ITALIC = exports.DEFAULT_FONT_ITALIC = exports.DEFAULT_K_FONT_BOLD = exports.DEFAULT_FONT_BOLD = exports.K_VERTICAL_ALIGNMENT = exports.K_TEXT_UNDERLINE = exports.K_TEXT_STRIKEOUT = exports.K_STYLE_REF = exports.K_SHADOW = exports.K_ROTATION = exports.K_LINE_WIDTH = exports.K_LINE_STYLE = exports.K_LINE_JOIN = exports.K_LINE_CAP = exports.K_INVISIBILITY = exports.K_HORIZONTAL_ALIGNMENT = exports.K_FONT_SIZE = exports.K_FONT_NAME = exports.K_FONT_ITALIC = exports.K_FONT_BOLD = exports.K_FOREGROUND = exports.K_BACKGROUND = exports.K_COLORING = void 0; exports.getSvgTextStyles = exports.getSvgLineStyles = exports.isInvisible = exports.getSvgColorStyle = exports.getSvgColorStyles = exports.getSvgShadowStyles = exports.shadowDefinition = exports.colorDefinition = exports.copyStyles = exports.applyKStyle = exports.getDefaultNonTextSelectionStyles = exports.getDefaultTextSelectionStyles = exports.getKStyles = exports.KStyles = exports.DEFAULT_MITER_LIMIT_SVG = exports.DEFAULT_LINE_JOIN_SVG = void 0; const sprotty_1 = require("sprotty"); // eslint-disable-line @typescript-eslint/no-unused-vars const render_options_registry_1 = require("./options/render-options-registry"); const skgraph_models_1 = require("./skgraph-models"); const views_common_1 = require("./views-common"); // ----------------------------- type string definitions for all styles ------------------------------------- // exports.K_COLORING = 'KColoringImpl'; exports.K_BACKGROUND = 'KBackgroundImpl'; exports.K_FOREGROUND = 'KForegroundImpl'; exports.K_FONT_BOLD = 'KFontBoldImpl'; exports.K_FONT_ITALIC = 'KFontItalicImpl'; exports.K_FONT_NAME = 'KFontNameImpl'; exports.K_FONT_SIZE = 'KFontSizeImpl'; exports.K_HORIZONTAL_ALIGNMENT = 'KHorizontalAlignmentImpl'; exports.K_INVISIBILITY = 'KInvisibilityImpl'; exports.K_LINE_CAP = 'KLineCapImpl'; exports.K_LINE_JOIN = 'KLineJoinImpl'; exports.K_LINE_STYLE = 'KLineStyleImpl'; exports.K_LINE_WIDTH = 'KLineWidthImpl'; exports.K_ROTATION = 'KRotationImpl'; exports.K_SHADOW = 'KShadowImpl'; exports.K_STYLE_REF = 'KStyleRefImpl'; exports.K_TEXT_STRIKEOUT = 'KTextStrikeoutImpl'; exports.K_TEXT_UNDERLINE = 'KTextUnderlineImpl'; exports.K_VERTICAL_ALIGNMENT = 'KVerticalAlignmentImpl'; // constants for string building const GRADIENT_UNIT_OBJECT_BOUNDING_BOX = 'objectBoundingBox'; const RGB_START = 'rgb('; const RGB_END = ')'; const URL_START = 'url(#'; const URL_END = ')'; // Default values for most Styles, that are used if no style is given Default values taken from PNodeController.java exports.DEFAULT_FONT_BOLD = false; exports.DEFAULT_K_FONT_BOLD = { bold: exports.DEFAULT_FONT_BOLD, }; exports.DEFAULT_FONT_ITALIC = false; exports.DEFAULT_K_FONT_ITALIC = { italic: exports.DEFAULT_FONT_ITALIC, }; exports.DEFAULT_FONT_NAME = 'Overpass, sans-serif'; exports.DEFAULT_K_FONT_NAME = { name: exports.DEFAULT_FONT_NAME, }; exports.DEFAULT_FONT_SIZE = 10; exports.DEFAULT_K_FONT_SIZE = { size: exports.DEFAULT_FONT_SIZE, scaleWithZoom: false, // TODO: implement this }; exports.DEFAULT_HORIZONTAL_ALIGNMENT = skgraph_models_1.HorizontalAlignment.CENTER; exports.DEFAULT_K_HORIZONTAL_ALIGNMENT = { horizontalAlignment: exports.DEFAULT_HORIZONTAL_ALIGNMENT, }; exports.DEFAULT_INVISIBILITY = false; exports.DEFAULT_K_INVISIBILITY = { invisible: exports.DEFAULT_INVISIBILITY, }; exports.DEFAULT_LINE_CAP = skgraph_models_1.LineCap.CAP_FLAT; exports.DEFAULT_K_LINE_CAP = { lineCap: exports.DEFAULT_LINE_CAP, }; exports.DEFAULT_LINE_JOIN = skgraph_models_1.LineJoin.JOIN_MITER; exports.DEFAULT_MITER_LIMIT = 10; exports.DEFAULT_K_LINE_JOIN = { lineJoin: exports.DEFAULT_LINE_JOIN, miterLimit: exports.DEFAULT_MITER_LIMIT, }; exports.DEFAULT_LINE_STYLE = skgraph_models_1.LineStyle.SOLID; exports.DEFAULT_K_LINE_STYLE = { lineStyle: exports.DEFAULT_LINE_STYLE, dashOffset: 0, dashPattern: [0], }; exports.DEFAULT_LINE_WIDTH = 1; exports.DEFAULT_K_LINE_WIDTH = { lineWidth: exports.DEFAULT_LINE_WIDTH, }; exports.DEFAULT_FILL = { color: 'none', }; exports.DEFAULT_CLICKABLE_FILL = { color: `${RGB_START}0,0,0${RGB_END}`, opacity: '0', }; exports.DEFAULT_FOREGROUND = { color: 'black', }; exports.DEFAULT_VERTICAL_ALIGNMENT = skgraph_models_1.VerticalAlignment.CENTER; exports.DEFAULT_K_VERTICAL_ALIGNMENT = { verticalAlignment: exports.DEFAULT_VERTICAL_ALIGNMENT, }; exports.DEFAULT_SHADOW = undefined; exports.DEFAULT_SHADOW_DEF = undefined; exports.DEFAULT_CORNER_WIDTH = 0; exports.DEFAULT_CORNER_HEIGHT = 0; exports.DEFAULT_LINE_CAP_SVG = 'butt'; exports.DEFAULT_LINE_JOIN_SVG = 'miter'; exports.DEFAULT_MITER_LIMIT_SVG = '4'; /** * Data class to hold each possible KStyle of any rendering. Defaults each style to undefined or its default value from PNodeController.java */ class KStyles { constructor(initialize) { if (initialize !== false) { this.kBackground = undefined; this.kForeground = undefined; this.kFontBold = exports.DEFAULT_K_FONT_BOLD; this.kFontItalic = exports.DEFAULT_K_FONT_ITALIC; this.kFontName = exports.DEFAULT_K_FONT_NAME; this.kFontSize = exports.DEFAULT_K_FONT_SIZE; this.kHorizontalAlignment = exports.DEFAULT_K_HORIZONTAL_ALIGNMENT; this.kInvisibility = exports.DEFAULT_K_INVISIBILITY; this.kLineCap = exports.DEFAULT_K_LINE_CAP; this.kLineJoin = exports.DEFAULT_K_LINE_JOIN; this.kLineStyle = exports.DEFAULT_K_LINE_STYLE; this.kLineWidth = exports.DEFAULT_K_LINE_WIDTH; this.kRotation = undefined; this.kShadow = exports.DEFAULT_SHADOW; this.kStyleRef = undefined; this.kTextStrikeout = undefined; this.kTextUnderline = undefined; this.kVerticalAlignment = exports.DEFAULT_K_VERTICAL_ALIGNMENT; } } } exports.KStyles = KStyles; /** * Calculates the renderings for all styles contained in styleList into an object. * @param styleList The list of all styles that should have their rendering calculated. * @param propagatedStyles The styles propagated from parent elements that should be taken into account. * @param stylesToPropagate The optional styles object that should be propagated further to children. It is modified in this method. */ function getKStyles(parent, styleHolder, propagatedStyles, context, stylesToPropagate) { // TODO: not all of these are implemented yet // Style Priority in KLighD: // 1. Styles propagated from immedeate parent renderings // 2. Styles propagated from recursive parent rendering (deeper down in the hierarchy first) // 3. Styles explicitly given to the rendering // 4. Default styles // Caution: Styles that are propagated do NOT apply to the rendering itself, if there are parent propagated styles. Only the children will have this propagated style as their first priority. // The styles to propagate start as the current propagated styles to be overwritten by new styles. const styles = new KStyles(); if (stylesToPropagate !== undefined) { copyStyles(propagatedStyles, stylesToPropagate); } let styleList = styleHolder.styles; if (styleList === undefined) { return styles; } // First, check if we need to incorporate default selection styles. // That is the case if the parend is selected and no selection styles are available. if ((0, sprotty_1.isSelectable)(parent) && parent.selected) { if (styleList.filter((style) => style.selection === true).length === 0) { // ...if no selection styles are available, apply default ones. if ((0, skgraph_models_1.isKText)(styleHolder)) { styleList = styleList.concat(getDefaultTextSelectionStyles()); } else if (styleHolder === (0, views_common_1.getKRendering)(parent.data, context)) { // For non-text renderings this only applies to the root rendering styleList = styleList.concat(getDefaultNonTextSelectionStyles()); } } } // Then, apply all styles in order of appereance in the style list. for (const style of styleList) { // Only apply selection styles if the parent is selected. if (style.selection === false || ((0, sprotty_1.isSelectable)(parent) && parent.selected)) { applyKStyle(style, styles, stylesToPropagate); } } // Finally, override with propagated styles. copyStyles(propagatedStyles, styles); return styles; } exports.getKStyles = getKStyles; /** * The default selection styles for text renderings. * @returns A list of default selection text styles. */ function getDefaultTextSelectionStyles() { return [ { type: exports.K_BACKGROUND, propagateToChildren: false, selection: true, color: { red: 190, green: 190, blue: 190, }, alpha: 255, gradientAngle: 0, }, { type: exports.K_FONT_BOLD, propagateToChildren: false, selection: true, bold: true, }, ]; } exports.getDefaultTextSelectionStyles = getDefaultTextSelectionStyles; /** * The default selection styles for non-text renderings. * @returns A list of default selection non-text styles. */ function getDefaultNonTextSelectionStyles() { return [ { type: exports.K_BACKGROUND, propagateToChildren: false, selection: true, color: { red: 190, green: 190, blue: 190, }, alpha: 255, gradientAngle: 0, }, { type: exports.K_LINE_STYLE, propagateToChildren: false, selection: true, lineStyle: skgraph_models_1.LineStyle.DASH, dashOffset: 0, }, ]; } exports.getDefaultNonTextSelectionStyles = getDefaultNonTextSelectionStyles; /** * Apply the given style to the given styles object. If it should be propagated, also apply it to the stylesToPropagage object. * @param style The style to apply. * @param styles The styles object the style should be applied to. * @param stylesToPropagage The styles object that gets propagated. */ function applyKStyle(style, styles, stylesToPropagage) { switch (style.type) { case exports.K_COLORING: { console.error(`A style can not be a ${style.type} by itself, it needs to be a subclass of it.`); break; } case exports.K_BACKGROUND: { styles.kBackground = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kBackground = styles.kBackground; } break; } case exports.K_FOREGROUND: { styles.kForeground = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kForeground = styles.kForeground; } break; } case exports.K_FONT_BOLD: { styles.kFontBold = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kFontBold = styles.kFontBold; } break; } case exports.K_FONT_ITALIC: { styles.kFontItalic = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kFontItalic = styles.kFontItalic; } break; } case exports.K_FONT_NAME: { styles.kFontName = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kFontName = styles.kFontName; } break; } case exports.K_FONT_SIZE: { styles.kFontSize = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kFontSize = styles.kFontSize; } break; } case exports.K_HORIZONTAL_ALIGNMENT: { styles.kHorizontalAlignment = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kHorizontalAlignment = styles.kHorizontalAlignment; } break; } case exports.K_INVISIBILITY: { styles.kInvisibility = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kInvisibility = styles.kInvisibility; } break; } case exports.K_LINE_CAP: { styles.kLineCap = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kLineCap = styles.kLineCap; } break; } case exports.K_LINE_JOIN: { styles.kLineJoin = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kLineJoin = styles.kLineJoin; } break; } case exports.K_LINE_STYLE: { styles.kLineStyle = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kLineStyle = styles.kLineStyle; } break; } case exports.K_LINE_WIDTH: { styles.kLineWidth = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kLineWidth = styles.kLineWidth; } break; } case exports.K_ROTATION: { styles.kRotation = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kRotation = styles.kRotation; } break; } case exports.K_SHADOW: { styles.kShadow = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kShadow = styles.kShadow; } break; } case exports.K_STYLE_REF: { console.error(`The style ${style.type} is not implemented yet.`); // style as KStyleRef // special case! TODO: how to handle this? Never seen this in any rendering break; } case exports.K_TEXT_STRIKEOUT: { console.error(`The style ${style.type} is not implemented yet.`); styles.kTextStrikeout = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kTextStrikeout = styles.kTextStrikeout; } break; } case exports.K_TEXT_UNDERLINE: { styles.kTextUnderline = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kTextUnderline = styles.kTextUnderline; } break; } case exports.K_VERTICAL_ALIGNMENT: { styles.kVerticalAlignment = style; if (style.propagateToChildren === true && stylesToPropagage !== undefined) { stylesToPropagage.kVerticalAlignment = styles.kVerticalAlignment; } break; } default: { console.error(`Unexpected Style found while rendering: ${style.type}`); break; } } } exports.applyKStyle = applyKStyle; /** * Copies the content from one to the other KStyles object. * @param from The KStyles to copy from. * @param to The KStyles to copy to. */ function copyStyles(from, to) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t; to.kBackground = (_a = from.kBackground) !== null && _a !== void 0 ? _a : to.kBackground; to.kForeground = (_b = from.kForeground) !== null && _b !== void 0 ? _b : to.kForeground; to.kFontBold = (_c = from.kFontBold) !== null && _c !== void 0 ? _c : to.kFontBold; to.kFontItalic = (_d = from.kFontItalic) !== null && _d !== void 0 ? _d : to.kFontItalic; to.kFontName = (_e = from.kFontName) !== null && _e !== void 0 ? _e : to.kFontName; to.kFontSize = (_f = from.kFontSize) !== null && _f !== void 0 ? _f : to.kFontSize; to.kHorizontalAlignment = (_g = from.kHorizontalAlignment) !== null && _g !== void 0 ? _g : to.kHorizontalAlignment; to.kInvisibility = (_h = from.kInvisibility) !== null && _h !== void 0 ? _h : to.kInvisibility; to.kLineCap = (_j = from.kLineCap) !== null && _j !== void 0 ? _j : to.kLineCap; to.kLineJoin = (_k = from.kLineJoin) !== null && _k !== void 0 ? _k : to.kLineJoin; to.kLineStyle = (_l = from.kLineStyle) !== null && _l !== void 0 ? _l : to.kLineStyle; to.kLineWidth = (_m = from.kLineWidth) !== null && _m !== void 0 ? _m : to.kLineWidth; to.kRotation = (_o = from.kRotation) !== null && _o !== void 0 ? _o : to.kRotation; to.kShadow = (_p = from.kShadow) !== null && _p !== void 0 ? _p : to.kShadow; to.kStyleRef = (_q = from.kStyleRef) !== null && _q !== void 0 ? _q : to.kStyleRef; to.kTextStrikeout = (_r = from.kTextStrikeout) !== null && _r !== void 0 ? _r : to.kTextStrikeout; to.kTextUnderline = (_s = from.kTextUnderline) !== null && _s !== void 0 ? _s : to.kTextUnderline; to.kVerticalAlignment = (_t = from.kVerticalAlignment) !== null && _t !== void 0 ? _t : to.kVerticalAlignment; } exports.copyStyles = copyStyles; // ----------------------------- Functions for rendering different KStyles as VNodes in svg -------------------------------------------- /** * SVG element for color gradient definition. * @param colorId The unique identifying string for this color. * @param start The SVG data for the start color of the gradient. * @param end The SVG data for the end color of the gradient. * @param angle The angle at which the gradient should flow. */ function colorDefinition(colorId, start, end, angle) { const startColorStop = ((0, sprotty_1.svg)("stop", { offset: 0, style: Object.assign({ 'stop-color': start.color }, (start.opacity ? { 'stop-opacity': start.opacity } : {})) })); const endColorStop = ((0, sprotty_1.svg)("stop", { offset: 1, style: Object.assign({ 'stop-color': end.color }, (end.opacity ? { 'stop-opacity': end.opacity } : {})) })); let angleFloat = angle === undefined ? 0 : angle; // Calculate the x and y lengths a line of angle 'angle' would have in a 1x1 box. // First, normalize the angle to be 0<=angle<360 angleFloat %= 360; if (angleFloat < 0) { angleFloat += 360; } // Convert the angle to radians const angleRad = (angleFloat / 180) * Math.PI; let x; let y; if (angleRad <= (1 / 4) * Math.PI || angleRad > (7 / 4) * Math.PI) { x = 1; y = -Math.tan((0 / 2) * Math.PI - angleRad); } else if (angleRad <= (3 / 4) * Math.PI) { x = Math.tan((1 / 2) * Math.PI - angleRad); y = 1; } else if (angleRad <= (5 / 4) * Math.PI) { x = -1; y = Math.tan((2 / 2) * Math.PI - angleRad); } else { // or: else if (angleRad <= 7/4 * Math.PI) { x = -Math.tan((3 / 2) * Math.PI - angleRad); y = -1; } // Now, turn these lengths into x1/x2 and y1/y2 coordinates within the box such that 0<=var<=1, // centered within the box. let x1; let x2; let y1; let y2; if (x >= 0) { const halfRemain = (1 - x) / 2; x1 = halfRemain; x2 = halfRemain + x; } else { const halfRemain = (1 + x) / 2; x1 = halfRemain - x; x2 = halfRemain; } if (y >= 0) { const halfRemain = (1 - y) / 2; y1 = halfRemain; y2 = halfRemain + y; } else { const halfRemain = (1 + y) / 2; y1 = halfRemain - y; y2 = halfRemain; } const gradientAttributes = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id: colorId }, (angleFloat === 0 ? {} : { gradientUnits: GRADIENT_UNIT_OBJECT_BOUNDING_BOX })), (angleFloat === 0 ? {} : { x1 })), (angleFloat === 0 ? {} : { x2 })), (angleFloat === 0 ? {} : { y1 })), (angleFloat === 0 ? {} : { y2 })); return ((0, sprotty_1.svg)("linearGradient", Object.assign({}, gradientAttributes), startColorStop, endColorStop)); } exports.colorDefinition = colorDefinition; /** * SVG element for a shadow definition. * @param shadowId The unique identifying string for this shadow. * @param color The color of the shadow. * @param blur The amount of blur of the shadow. * @param xOffset The x-offset of the shadow. * @param yOffset The y-offset of the shadow. */ function shadowDefinition(shadowId, color, blur, xOffset, yOffset) { // stdDev of 1 looks closest to KIELER style shadows, but looks nicer with this blur // TODO: ultimately, this should be using the blur parameter again. // TODO: use the color given in the shadow. // TODO: maybe calculate the blurClip depending on the calculated size of the rendering and the x- and y-offset. const STD_DEV = 1; const blurClip = 25; return ((0, sprotty_1.svg)("filter", { id: shadowId, // Extend the region around the element in which the shadow should be rendered. x: `-${blurClip}%`, y: `-${blurClip}%`, width: `${100 + 2 * blurClip}%`, height: `${100 + 2 * blurClip}%` }, (0, sprotty_1.svg)("feGaussianBlur", { in: "SourceAlpha", stdDeviation: STD_DEV }), (0, sprotty_1.svg)("feOffset", { // A smaller offset causes the blur not to overlap too much. dx: xOffset / 4, dy: yOffset / 4, result: "offsetblur" }), (0, sprotty_1.svg)("feFlood", null), (0, sprotty_1.svg)("feComposite", { in2: "offsetblur", operator: "in" }), (0, sprotty_1.svg)("feMerge", null, (0, sprotty_1.svg)("feMergeNode", null), (0, sprotty_1.svg)("feMergeNode", { in: "SourceGraphic" })))); // The above definition is equivalent to this shorthand SVG, but not all SVG renderers support and understand this (such as Inkscape). // As every shadow is defined exactly once in the final SVG, this additional code does not add too much to the overall file size. // <feDropShadow // dx={ xOffset / 4 } // dy={ yOffset / 4 } // stdDeviation={ STD_DEV } // /> } exports.shadowDefinition = shadowDefinition; /** * Returns the identifying string for the given shadow style, that can be put into the SVG 'filter' attribute. * Also remembers the shadow definition in the rendering context to be added to the top of the final SVG. * @param styles The KStyles of the rendering. * @param context The rendering context. */ function getSvgShadowStyles(styles, context) { const shadow = styles.kShadow; if (shadow === undefined) { return undefined; } // Every shadow ID should start with an 's'. let shadowId = 's'; let color; const { blur } = shadow; const { xOffset } = shadow; const { yOffset } = shadow; // Extract the color and also put it in the ID. if (shadow.color !== undefined) { const shadowColor = `${shadow.color.red},${shadow.color.green},${shadow.color.blue}`; shadowId += shadowColor; color = RGB_START + shadowColor + RGB_END; } // Separator for unique identification. shadowId += '$'; // Add the blur to the ID. if (blur !== undefined) { shadowId += blur; } shadowId += '$'; // Add the x- and y-offset to the ID. if (xOffset !== undefined) { shadowId += xOffset; } shadowId += '$'; if (yOffset !== undefined) { shadowId += yOffset; } // Remember the shadow definition to be added at the top level of the SVG, if the same shadow has not been defined previously. if (!context.renderingDefs.has(shadowId)) { context.renderingDefs.set(shadowId, shadowDefinition(shadowId, color, blur, xOffset, yOffset)); } // Return the reference of the above defined ID to be put in the filter attribute of any SVG element. return URL_START + shadowId + URL_END; } exports.getSvgShadowStyles = getSvgShadowStyles; /** * Returns the identifying strings for the given foreground- and background styles that can be put in the SVG 'stroke' and 'fill' attributes, * depending on the rendering the styles have to be applied for. * The identifying string can either be a simple rgb color reference (such as rgb(0,0,0) for black), a rgba color reference (such as rgba(0,0,0,128) for a transparent black) * or a url for a gradient color definition that is remembered in the rendering context and has to be added to the SVG later. * @param styles The KStyles of the rendering. * @param context The rendering context. */ function getSvgColorStyles(styles, context, parent) { const foreground = getSvgColorStyle(styles.kForeground, context); const background = getSvgColorStyle(styles.kBackground, context); const grayedOutColor = { color: 'grey', opacity: '255' }; if (parent instanceof skgraph_models_1.SKEdge && parent.moved) { // edge should be greyed out return { foreground: grayedOutColor, background: background === undefined ? exports.DEFAULT_FILL : grayedOutColor, opacity: String(parent.opacity), }; } if (parent instanceof skgraph_models_1.SKNode && parent.shadow) { // colors of the shadow node return { foreground: grayedOutColor, background: background === undefined ? exports.DEFAULT_FILL : { color: 'gainsboro', opacity: '255' }, opacity: String(parent.opacity), }; } if (parent instanceof skgraph_models_1.SKNode && parent.highlight) { return { foreground: { color: '#03A9F4', opacity: '255' }, background: background === undefined ? exports.DEFAULT_FILL : background, opacity: parent.opacity.toString(), }; } return { foreground: foreground === undefined ? exports.DEFAULT_FOREGROUND : foreground, background: background === undefined ? exports.DEFAULT_FILL : background, opacity: String(parent.opacity), }; } exports.getSvgColorStyles = getSvgColorStyles; /** * The same as getSvgColorStyles, only that it only handles one of the two styles. * @param coloring The KColoring of which the color string should be returned. * @param context The rendering context. * @see getSvgColorStyles */ function getSvgColorStyle(coloring, context) { if (coloring === undefined || coloring.color === undefined) { return undefined; } // If the color is a single color, just return its corresponding rgb resp. rgba color. if ((0, views_common_1.isSingleColor)(coloring)) { return (0, views_common_1.fillSingleColor)(coloring); } // Otherwise, build an ID for the gradient color to refer to the definition described below. // Every color ID should start with a 'c'. let colorId = 'c'; const start = {}; const end = {}; let angle; if (coloring.alpha !== undefined && coloring.alpha !== 255) { start.opacity = (coloring.alpha / 255).toString(); } const startColor = `${coloring.color.red},${coloring.color.green},${coloring.color.blue}`; colorId += startColor; start.color = RGB_START + startColor + RGB_END; // Separate the individual parts in the ID to guarantee uniqueness. colorId += '$'; // Do the same for the end color. if (coloring.targetAlpha !== undefined && coloring.targetAlpha !== 255) { end.opacity = (coloring.targetAlpha / 255).toString(); } const endColor = `${coloring.targetColor.red},${coloring.targetColor.green},${coloring.targetColor.blue}`; colorId += endColor; end.color = RGB_START + endColor + RGB_END; // Add the angle of the gradient to the ID. if (coloring.gradientAngle !== 0) { angle = coloring.gradientAngle; colorId += `$${angle}`; } // Remember the color definition to be added at the top level of the SVG, if the same color has not been defined previously. if (!context.renderingDefs.has(colorId)) { context.renderingDefs.set(colorId, colorDefinition(colorId, start, end, angle)); } // Return the reference of the above defined ID to be put in the fill or stroke attribute of any SVG element. return { color: URL_START + colorId + URL_END, // no opacity needed here as it is already in the gradient color definition. }; } exports.getSvgColorStyle = getSvgColorStyle; /** * Returns if the rendering should be rendered or if it is invisible and only its children are relevant. * @param styles The KStyles of the rendering. */ function isInvisible(styles) { return styles.kInvisibility !== undefined && styles.kInvisibility.invisible; } exports.isInvisible = isInvisible; /** * Returns the SVG strings for line styles that can be applied to the following SVG attributes: * 'stroke-linecap' has to be set to the lineCap style, * 'stroke-linejoin' has to be set to the lineJoin style, * 'stroke-width' has to be set to the lineWidth style, * 'stroke-dasharray' has to be set to the dashArray style, * 'stroke-miterlimit' has to be set to the miterLimit style. (This is not a string, but a number.) * @param styles The KStyles of the rendering. * @param target The target of the line * @param context The current rendering context */ function getSvgLineStyles(styles, target, context) { var _a; // The line width as requested by the element let lineWidth = styles.kLineWidth === undefined ? exports.DEFAULT_LINE_WIDTH : styles.kLineWidth.lineWidth; const useLineWidthOption = context.renderOptionsRegistry.getValue(render_options_registry_1.UseMinimumLineWidth); // Only enable, if option is found. const useMinimumLineWidth = useLineWidthOption !== null && useLineWidthOption !== void 0 ? useLineWidthOption : false; if (!context.forceRendering && useMinimumLineWidth) { // The line witdh in px that the drawn line should not be less than. const minimumLineWidth = context.renderOptionsRegistry.getValueOrDefault(render_options_registry_1.MinimumLineWidth); // The line width the requested one would have when rendered in the current zoom level. const realLineWidth = lineWidth * (0, sprotty_1.getZoom)(target); if (realLineWidth !== 0 && realLineWidth < minimumLineWidth) { // scale the used line width up to appear as big as the minimum line width requested. lineWidth *= minimumLineWidth / realLineWidth; } } const lineCap = styles.kLineCap === undefined ? undefined : (0, views_common_1.lineCapText)(styles.kLineCap); const lineJoin = styles.kLineJoin === undefined ? undefined : (0, views_common_1.lineJoinText)(styles.kLineJoin); const miterLimit = ((_a = styles.kLineJoin) === null || _a === void 0 ? void 0 : _a.miterLimit) === undefined ? exports.DEFAULT_MITER_LIMIT : styles.kLineJoin.miterLimit; return { lineWidth: lineWidth === exports.DEFAULT_LINE_WIDTH ? undefined : `${lineWidth}px`, lineCap: lineCap === exports.DEFAULT_LINE_CAP_SVG ? undefined : lineCap, lineJoin: lineJoin === exports.DEFAULT_LINE_JOIN_SVG ? undefined : lineJoin, dashArray: styles.kLineStyle === undefined ? undefined : (0, views_common_1.lineStyleText)(styles.kLineStyle, lineWidth), // Note: Here the miter limit value is also omitted if the value equals KGraph's default value of 10, because otherwise the resulting SVG would // always contain the miterLimit style to be set to 10, even though it is not intended by the creator of the KGraph model and it would not // even make any difference in the rendering. Here I cannot distinguish if the model creator really wanted to have the specific miter limit of 10 // or if he just does not care. As the first case seems rare, I prefer a cleaner resulting svg here. miterLimit: lineJoin !== 'miter' || String(miterLimit) === exports.DEFAULT_MITER_LIMIT_SVG || miterLimit === exports.DEFAULT_MITER_LIMIT ? undefined : String(miterLimit), }; } exports.getSvgLineStyles = getSvgLineStyles; /** * Returns the SVG strings for text styles that can be applied to the following SVG attributes: * 'dominant-baseline' has to be set to the dominantBaseline style, * 'font-family' has to be set to the fontFamily style, * 'font-size' has to be set to the fontSize style, * 'font-style' has to be set to the fontStyle style, * 'font-weight' has to be set to the fontWeight style, * 'text-decoration-line' has to be set to the textDecorationLine style, * 'text-decoration-style' has to be set to the textDecorationStyle style. * @param styles The KStyles of the rendering. */ function getSvgTextStyles(styles) { var _a; return { dominantBaseline: (0, views_common_1.verticalAlignmentText)(((_a = styles.kVerticalAlignment) === null || _a === void 0 ? void 0 : _a.verticalAlignment) === undefined ? exports.DEFAULT_VERTICAL_ALIGNMENT : styles.kVerticalAlignment.verticalAlignment), fontFamily: styles.kFontName === undefined ? undefined : (0, views_common_1.camelToKebab)(styles.kFontName.name), // Convert pt to px here with a default value of 96 dpi(px/in) and 72pt/in, making this a conversion from in to px. fontSize: styles.kFontSize === undefined ? undefined : `${(styles.kFontSize.size * 96) / 72}px`, fontStyle: styles.kFontItalic === undefined || styles.kFontItalic.italic === exports.DEFAULT_FONT_ITALIC ? undefined : 'italic', fontWeight: styles.kFontBold === undefined || styles.kFontBold.bold === exports.DEFAULT_FONT_BOLD ? undefined : 'bold', textDecorationLine: styles.kTextUnderline === undefined || styles.kTextUnderline.underline === skgraph_models_1.Underline.NONE ? undefined : 'underline', textDecorationStyle: styles.kTextUnderline === undefined ? undefined : (0, views_common_1.textDecorationStyleText)(styles.kTextUnderline), // textDecorationColor: styles.kTextUnderline === undefined ? undefined : textDecorationColor(styles.kTextUnderline as KTextUnderline), // TODO: textDecorationColorDefinition: }; } exports.getSvgTextStyles = getSvgTextStyles; //# sourceMappingURL=views-styles.js.map