diff --git a/packages/klighd-core/src/views-common.ts b/packages/klighd-core/src/views-common.ts
index b5370dcd..1563d1e5 100644
--- a/packages/klighd-core/src/views-common.ts
+++ b/packages/klighd-core/src/views-common.ts
@@ -570,6 +570,42 @@ export function transformationToSVGString(transformation: Transformation): strin
}
}
+/**
+ * Reverses an array of transformations such that applying the transformation and its reverse counterpart will result in the identity transformation.
+ * @param transformations The transformations to reverse.
+ * @returns The reversed transformations.
+ */
+export function reverseTransformations(transformations: Transformation[]): Transformation[] {
+ return transformations.map(transformation => reverseTransformation(transformation)).reverse()
+}
+
+/**
+ * Reverses a transformation such that applying the transformation and its reverse counterpart will result in the identity transformation.
+ * @param transformation The transformation to reverse.
+ * @returns The reversed transformation.
+ */
+export function reverseTransformation(transformation: Transformation): Transformation {
+ if (isTranslation(transformation)) {
+ return {
+ kind: 'translate',
+ x: -transformation.x,
+ y: -transformation.y
+ } as Translation
+ } else if (isRotation(transformation)) {
+ return {
+ kind: 'rotate',
+ angle: -transformation.angle,
+ x: transformation.x,
+ y: transformation.y
+ } as Rotation
+ } else {
+ return {
+ kind: 'scale',
+ factor: 1 / (transformation as Scale).factor
+ } as Scale
+ }
+}
+
/**
* Calculates the SVG transformation string that has to be applied to the SVG element.
* @param bounds The bounds of the rendering.
diff --git a/packages/klighd-core/src/views-rendering.tsx b/packages/klighd-core/src/views-rendering.tsx
index 08bb9662..fc4fb0e2 100644
--- a/packages/klighd-core/src/views-rendering.tsx
+++ b/packages/klighd-core/src/views-rendering.tsx
@@ -28,7 +28,7 @@ import {
K_ROUNDED_BENDS_POLYLINE, K_ROUNDED_RECTANGLE, K_SPLINE, K_TEXT, SKEdge, SKGraphElement, SKLabel, SKNode, VerticalAlignment
} from './skgraph-models';
import { hasAction } from './skgraph-utils';
-import { BoundsAndTransformation, calculateX, findBoundsAndTransformationData, getKRendering, getPoints, isRotation, isTranslation, Rotation, Scale, Transformation, transformationToSVGString, Translation } from './views-common';
+import { BoundsAndTransformation, calculateX, findBoundsAndTransformationData, getKRendering, getPoints, isRotation, isTranslation, reverseTransformations, Rotation, Scale, Transformation, transformationToSVGString, Translation } from './views-common';
import {
ColorStyles, DEFAULT_CLICKABLE_FILL, DEFAULT_FILL, getKStyles, getSvgColorStyle, getSvgColorStyles, getSvgLineStyles, getSvgShadowStyles, getSvgTextStyles, isInvisible,
KStyles, LineStyles
@@ -43,7 +43,30 @@ import {
* @param propagatedStyles The styles propagated from parent elements that should be taken into account.
* @param context The rendering context for this element.
*/
-export function renderChildArea(rendering: KChildArea, parent: SKGraphElement, propagatedStyles: KStyles, context: SKGraphModelRenderer): VNode {
+export function renderChildArea(
+ rendering: KChildArea,
+ parent: SKGraphElement,
+ boundsAndTransformation: BoundsAndTransformation,
+ context: SKGraphModelRenderer): VNode {
+
+ // Sprotty expects the graph elements to always be relative to the parent element, while KLighD usually has the graph elements relative to the child area.
+ // Here we expect the graph elements to behave as Sprotty expects, thus requiring to reverse offset the transformation towards this child area again.
+
+ // First, we need to find the total transformations that were applied to the child area.
+ const totalTansformation = [...context.titleStorage.getTransformations()]
+
+ // Second, we need to find the transformation only applicable to the current child area.
+ const childAreaTransformation = boundsAndTransformation.transformation
+
+ // Finally, we need to reverse the translation that was applied to every element hierarchially above of the child area.
+ // Note that this causes a little difference in what the coordinates are relative to the parent graph element, as entire child area rotations that are possible in KLighD are
+ // not possible in Sprotty.
+ totalTansformation.splice(totalTansformation.length - childAreaTransformation.length, childAreaTransformation.length)
+ const reverseTranslation: Transformation[] = reverseTransformations(totalTansformation.filter(transformation => isTranslation(transformation)))
+
+ const gAttrs = {
+ ...(reverseTranslation.length !== 0 ? { transform: reverseTranslation.map(transformationToSVGString).join('') } : {})
+ }
if (parent.areChildAreaChildrenRendered) {
console.error('This element contains multiple child areas, skipping this one.')
return
@@ -51,7 +74,7 @@ export function renderChildArea(rendering: KChildArea, parent: SKGraphElement, p
// remember, that this parent's children are now already rendered
parent.areChildAreaChildrenRendered = true
- const element =
+ const element =
{context.renderChildAreaChildren(parent)}
@@ -166,7 +189,7 @@ export function renderRectangularShape(
// eslint-disable-next-line
case K_ELLIPSE: {
element =
- {...renderSVGEllipse(boundsAndTransformation.bounds, lineStyles, colorStyles, shadowStyles, styles.kShadow)}
+ {...renderSVGEllipse(boundsAndTransformation.bounds, styles.kLineWidth.lineWidth, lineStyles, colorStyles, shadowStyles, styles.kShadow)}
{renderChildRenderings(rendering, parent, stylesToPropagate, context, childOfNodeTitle)}
break
@@ -181,7 +204,7 @@ export function renderRectangularShape(
const ry = (rendering as KRoundedRectangle).cornerHeight
element =
- {...renderSVGRect(boundsAndTransformation.bounds, rx, ry, lineStyles, colorStyles, shadowStyles, styles.kShadow)}
+ {...renderSVGRect(boundsAndTransformation.bounds, styles.kLineWidth.lineWidth, rx, ry, lineStyles, colorStyles, shadowStyles, styles.kShadow)}
{renderChildRenderings(rendering, parent, stylesToPropagate, context, childOfNodeTitle)}
break
@@ -567,6 +590,7 @@ export function renderWithShadow(
* Renders a rectangle with all given information.
*
* @param bounds bounds data calculated for this rectangle.
+ * @param lineWidth width of the line to offset the rectangle's position and size by.
* @param rx rx parameter of SVG rect
* @param ry ry parameter of SVG rect
* @param lineStyles style information for lines (stroke etc.)
@@ -575,8 +599,8 @@ export function renderWithShadow(
* @param kShadow general shadow information.
* @returns An array of SVG resulting from this. Only multiple s if a simple shadow effect should be applied.
*/
-export function renderSVGRect(bounds: Bounds, rx: number, ry: number, lineStyles: LineStyles, colorStyles: ColorStyles, shadowStyles: string | undefined, kShadow: KShadow | undefined): VNode[] {
- return renderWithShadow(kShadow, shadowStyles, renderSingleSVGRect, bounds, rx, ry, lineStyles, colorStyles)
+export function renderSVGRect(bounds: Bounds, lineWidth: number, rx: number, ry: number, lineStyles: LineStyles, colorStyles: ColorStyles, shadowStyles: string | undefined, kShadow: KShadow | undefined): VNode[] {
+ return renderWithShadow(kShadow, shadowStyles, renderSingleSVGRect, bounds, rx, ry, lineWidth, lineStyles, colorStyles)
}
/**
@@ -589,18 +613,27 @@ export function renderSVGRect(bounds: Bounds, rx: number, ry: number, lineStyles
* @param shadowStyles specific shadow filter ID, if this element should be drawn with a smooth shadow and no simple one.
* @param kShadow shadow information. Controls what this method does.
* @param bounds bounds data calculated for this rectangle.
+ * @param lineWidth width of the line to offset the rectangle's position and size by.
* @param rx rx parameter of SVG rect
* @param ry ry parameter of SVG rect
* @param lineStyles style information for lines (stroke etc.)
* @param colorStyles style information for color
* @returns A single SVG .
*/
-export function renderSingleSVGRect(x: number | undefined, y: number | undefined, shadowStyles: string | undefined, kShadow: KShadow | undefined, bounds: Bounds, rx: number, ry: number, lineStyles: LineStyles, colorStyles: ColorStyles): VNode {
+export function renderSingleSVGRect(x: number | undefined, y: number | undefined, shadowStyles: string | undefined, kShadow: KShadow | undefined, bounds: Bounds, rx: number, ry: number, lineWidth: number, lineStyles: LineStyles, colorStyles: ColorStyles): VNode {
+ // Offset the x/y by the lineWidth.
+ let theX: number | undefined = x ? x : 0
+ theX += lineWidth / 2
+ theX = theX === 0 ? undefined : theX
+ let theY: number | undefined = y ? y : 0
+ theY += lineWidth / 2
+ theY = theY === 0 ? undefined : theY
+
return s resulting from this. Only multiple s if a simple shadow effect should be applied.
*/
-export function renderSVGEllipse(bounds: Bounds, lineStyles: LineStyles, colorStyles: ColorStyles, shadowStyles: string | undefined, kShadow: KShadow | undefined): VNode[] {
- return renderWithShadow(kShadow, shadowStyles, renderSingleSVGEllipse, bounds, lineStyles, colorStyles)
+export function renderSVGEllipse(bounds: Bounds, lineWidth: number, lineStyles: LineStyles, colorStyles: ColorStyles, shadowStyles: string | undefined, kShadow: KShadow | undefined): VNode[] {
+ return renderWithShadow(kShadow, shadowStyles, renderSingleSVGEllipse, bounds, lineWidth, lineStyles, colorStyles)
}
/**
@@ -736,17 +770,18 @@ export function renderSVGEllipse(bounds: Bounds, lineStyles: LineStyles, colorSt
* @param shadowStyles specific shadow filter ID, if this element should be drawn with a smooth shadow and no simple one.
* @param kShadow shadow information. Controls what this method does.
* @param bounds bounds data calculated for this ellipse.
+ * @param lineWidth width of the line to offset the ellipse's position and size by.
* @param lineStyles style information for lines (stroke etc.)
* @param colorStyles style information for color
* @returns A single SVG .
*/
-export function renderSingleSVGEllipse(x: number | undefined, y: number | undefined, shadowStyles: string | undefined, kShadow: KShadow | undefined, bounds: Bounds, lineStyles: LineStyles, colorStyles: ColorStyles): VNode {
+export function renderSingleSVGEllipse(x: number | undefined, y: number | undefined, shadowStyles: string | undefined, kShadow: KShadow | undefined, bounds: Bounds, lineWidth: number, lineStyles: LineStyles, colorStyles: ColorStyles): VNode {
return