Skip to content

Commit

Permalink
klighd, klighd.piccolo & friends: added 'FOREGROUND_NODE' & 'BACKGROU…
Browse files Browse the repository at this point in the history
…ND_FIGURE' to 'KlighdProperties', refactored handling of nodes and node figures to solve #151

* added corresponding convenience methods to 'DiagramSyntheses'
* added distinguished layers for regular nodes and foreground nodes in 'KChildAreaNode'
* refined 'fullPaint()' procedures for 'KChildAreaNode' and 'KNodeNode'
* refined figure filtering in 'KlighdFigureNode'
* added required state to 'KlighdPaintContext'
* added corresponding tests to 'klighd.piccolo.test' powered by the svg generator
* minor refinement in 'SemanticSVGGraphics2D' (generated white space)
  • Loading branch information
sailingKieler committed Jan 6, 2023
1 parent 90978e7 commit 5ee5843
Show file tree
Hide file tree
Showing 16 changed files with 1,405 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ protected static String getPathContent(PathIterator path) {
StringBuffer result = new StringBuffer();

double[] coords = new double[6];
result.append("d=\"");
result.append(" d=\"");
while (!path.isDone()) {
int segType = path.currentSegment(coords);

Expand Down Expand Up @@ -1628,7 +1628,7 @@ protected static String getPathContent(PathIterator path) {
protected String getPath(PathIterator path) {
StringBuffer result = new StringBuffer();

result.append("<path ");
result.append("<path");
result.append(attributes(true));
result.append(getPathContent(path));
result.append("/>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ public KlighdFigureNode(final T rendering) {

private T rendering;

private boolean isBackgroundFigure;

/**
* Configures the {@link KRendering} element being represented by this {@link KlighdFigureNode}.
*
Expand All @@ -567,6 +569,8 @@ public void setRendering(final T rendering) {
return;
}

this.isBackgroundFigure = rendering.getProperty(KlighdProperties.BACKGROUND_FIGURE);

setVisibilityOn(
rendering.getProperty(KlighdProperties.OUTLINE_INVISIBLE).booleanValue(),
rendering.getProperty(KlighdProperties.EXPORTED_IMAGE_INVISIBLE).booleanValue(),
Expand Down Expand Up @@ -678,6 +682,16 @@ protected boolean pickAfterChildren(final PPickPath pickPath) {
}
}

/**
* {@inheritDoc}
*/
@Override
public boolean isNotVisibleOn(KlighdPaintContext kpc) {
return kpc.isBackgroundFiguresOnly() && !this.isBackgroundFigure
|| kpc.isNonBackgroundFiguresOnly() && this.isBackgroundFigure
|| super.isNotVisibleOn(kpc);
}

/**
* {@inheritDoc}<br>
* <br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ enum ElementMovement {
/** whether to synchronize the rendering with the model. */
private boolean syncRendering = false;

/** A flag for capturing whether some figure description parts are tagged as background figures. */
protected boolean backgroundFiguresPresent = false;

/**
* A flag indicating the availability of {@link KStyle KStyles} with valid modifier ids in
* {@link #currentRendering}.
Expand Down Expand Up @@ -324,6 +327,9 @@ void updateRendering() {
// this call updates the 'currentRendering' field
getCurrentRendering();

// reset the flag before re-evaluating the current figure description
backgroundFiguresPresent = false;

// reset that flag as potentially available styles with a modifier might be removed now
modifiableStylesPresent = false;

Expand Down Expand Up @@ -1172,6 +1178,9 @@ protected PNodeController<?> createRendering(final KRendering rendering,
final boolean isRenderingRef =
rendering.eClass() == KRenderingPackage.eINSTANCE.getKRenderingRef();

this.backgroundFiguresPresent = this.backgroundFiguresPresent
|| rendering.getProperty(KlighdProperties.BACKGROUND_FIGURE);

final List<KStyle> renderingStyles = rendering.getStyles();

processModifiableStyles(renderingStyles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ protected PNodeController<?> internalUpdateRendering() {
handleAreaAndPointPlacementRendering(createDefaultRendering(), repNode);
}

repNode.setHasBackgroundFigures(this.backgroundFiguresPresent);

// make sure the child area is attached to something
if (childAreaNode.getParent() == null) {
// if the childArea is not part of the above created PNode rendering tree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@
*/
package de.cau.cs.kieler.klighd.piccolo.internal.nodes;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.BasicEMap;

import de.cau.cs.kieler.klighd.kgraph.KGraphPackage;
import de.cau.cs.kieler.klighd.krendering.KChildArea;
import de.cau.cs.kieler.klighd.piccolo.IKlighdNode;
import de.cau.cs.kieler.klighd.piccolo.internal.util.KlighdPaintContext;
import de.cau.cs.kieler.klighd.util.KlighdProperties;
import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.util.PBounds;
Expand All @@ -41,14 +47,17 @@ public class KChildAreaNode extends KlighdDisposingLayer implements IKlighdNode.

private final KNodeAbstractNode parentNodeNode;

private final boolean edgesFirst;
private boolean edgesFirst;

/** the node layer. */
private PLayer nodeLayer;
private PLayer belowEdgesNodeLayer;

/** the edge layer. */
private PLayer edgeLayer;

/** 2nd node layer containing nodes being drawn on top of edges. */
private PLayer aboveEdgesNodeLayer;

/** the {@link KChildArea} represented by this {@link KChildAreaNode}, may be <code>null</code>. */
private KChildArea childArea;

Expand All @@ -69,6 +78,37 @@ public KChildAreaNode(final KNodeAbstractNode parentNodeNode, final boolean edge
this.setPickable(false);
this.parentNodeNode = parentNodeNode;
this.edgesFirst = edgesFirst;

if (!edgesFirst && parentNodeNode instanceof KNodeTopNode) {
// special handling for the KNodeTopNode since the root KNode and the KNodeTopNode are
// kept for the entire diagram life time and the update strategy transfers settings on
// the root node after this constructor call;
// hence, 'edgesFirst' will always be false except KlighdProperties.EDGES_FIRST is set
// on the viewContext;
// thus, if no activation is done on the view context we need to listen for changes on
// the properties of the root node being performed by the update strategy, so...

parentNodeNode.getViewModelElement().eAdapters().add(new AdapterImpl() {

@Override
public void notifyChanged(Notification msg) {
if (msg.getFeature() == KGraphPackage.eINSTANCE.getEMapPropertyHolder_Properties()) {
final Object newValue = msg.getNewValue();
if (msg.getEventType() == Notification.REMOVE_MANY && newValue == null) {
KChildAreaNode.this.edgesFirst =
KlighdProperties.EDGES_FIRST.getDefault().booleanValue();

} else if (msg.getEventType() == Notification.ADD
&& newValue instanceof BasicEMap.Entry<?, ?>
&& KlighdProperties.EDGES_FIRST
.equals(((BasicEMap.Entry<?, ?>) newValue).getKey())) {
KChildAreaNode.this.edgesFirst =
(Boolean) ((BasicEMap.Entry<?, ?>) newValue).getValue();
}
}
}
});
}
}

/**
Expand Down Expand Up @@ -103,8 +143,8 @@ public PLayer getEdgeLayer() {
*
* @return a dedicated layer accommodating all attached {@link KNodeNode KNodeNodes}.
*/
public PLayer getNodeLayer() {
return this.nodeLayer;
public PLayer getDefaultNodeLayer() {
return this.belowEdgesNodeLayer;
}

/**
Expand All @@ -124,12 +164,22 @@ public void setClip(final boolean clip) {
* the node representation
*/
public void addNode(final KNodeNode node) {
if (nodeLayer == null) {
nodeLayer = new KlighdDisposingLayer();
addChild(edgesFirst ? getChildrenCount() : 0, nodeLayer);
if (edgesFirst || node.isTaggedAsForeground()) {
if (aboveEdgesNodeLayer == null) {
aboveEdgesNodeLayer = new KlighdDisposingLayer();
addChild(getChildrenCount(), aboveEdgesNodeLayer);
}
aboveEdgesNodeLayer.addChild(node);
node.setParentNode(parentNodeNode, true);

} else {
if (belowEdgesNodeLayer == null) {
belowEdgesNodeLayer = new KlighdDisposingLayer();
addChild(0, belowEdgesNodeLayer);
}
belowEdgesNodeLayer.addChild(node);
node.setParentNode(parentNodeNode, false);
}
nodeLayer.addChild(node);
node.setParentNode(parentNodeNode);
}

/**
Expand All @@ -141,7 +191,7 @@ public void addNode(final KNodeNode node) {
public void addEdge(final KEdgeNode edge) {
if (edgeLayer == null) {
edgeLayer = new KlighdDisposingLayer();
addChild(edgesFirst ? 0 : getChildrenCount(), edgeLayer);
addChild(belowEdgesNodeLayer == null ? 0 : 1, edgeLayer);
}
edgeLayer.addChild(edge);
}
Expand Down Expand Up @@ -264,6 +314,26 @@ public void fullPaint(final PPaintContext paintContext) {
super.validateFullBounds();
super.validateFullPaint();

super.fullPaint(paintContext);
if (this.aboveEdgesNodeLayer == null || this.aboveEdgesNodeLayer.getChildrenCount() == 0) {
// no nodes to be drawn above edges, so draw as usual
super.fullPaint(paintContext);

} else if (getVisible() && fullIntersects(paintContext.getLocalClip())) {
final KlighdPaintContext kpc = (KlighdPaintContext) paintContext;

// assigning selected nodes explicitly to the 'aboveEdgesNodeLayer' or 'belowEdgesNodeLayer'
// is not supported as of now, and therefore a mixture of nodes to be drawn underneath the edge layer
// and nodes to be drawn on top of the edge layer should not occur at the time of writing this.

kpc.pushFigureFilterBackgroundOnly();
paintContext.pushTransform(getTransformReference(false));
this.aboveEdgesNodeLayer.fullPaint(paintContext);
paintContext.popTransform(getTransformReference(false));
kpc.popFigureFilter();

kpc.pushFigureFilterNonBackgroundOnly();
super.fullPaint(paintContext);
kpc.popFigureFilter();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ public class KNodeNode extends KNodeAbstractNode implements
*/
private final PCamera childAreaCamera;

/** Flag indicating that this node is tagged to be drawn on top of the edge layer within the parent child area. */
private final boolean isTaggedAsForeground;

/** Flag indicating that this node is considered be drawn on top of the edge layer within the parent child area by its containing child area. */
private boolean isDrawnAsForeground = false;

/** Flag indicating whether this node's figure contains background parts to be drawn before drawing all the edges within the parent child area. */
private boolean hasBackgroundFigures = false;

/** this flag indicates whether this node is currently observed by the {@link KlighdMainCamera}. */
private boolean isRootLayer = false;

Expand Down Expand Up @@ -114,6 +123,8 @@ public class KNodeNode extends KNodeAbstractNode implements
public KNodeNode(final KNode node, final boolean edgesFirst) {
super(node, edgesFirst);

this.isTaggedAsForeground = node.getProperty(KlighdProperties.FOREGROUND_NODE).booleanValue();

this.visibilityHelper = KGraphElementNode.evaluateVisibilityDefinitions(node, null);

this.childAreaCamera = new PCamera() {
Expand Down Expand Up @@ -395,8 +406,9 @@ public KNodeAbstractNode getParentKNodeNode() {
* @param parentINode
* the {@link AbstractKNodeNode} being the new parent in terms of the structural nodes
*/
public void setParentNode(final KNodeAbstractNode parentINode) {
public void setParentNode(final KNodeAbstractNode parentINode, final boolean addedAsForeground) {
this.parent = parentINode;
this.isDrawnAsForeground = addedAsForeground;
}

/**
Expand All @@ -408,6 +420,27 @@ public void removeFromParent() {
this.parent = null;
}

/**
* Getter.
*
* @return the value of getProperty({@link KlighdProperties#FOREGROUND_NODE}) for the
* corresponding {@link KNode}.
*/
public boolean isTaggedAsForeground() {
return this.isTaggedAsForeground;
}

/**
* Setter.
*
* @param hasBackgroundFigures
* value denoting whether the attached node figure contains parts being tagged as
* background figure parts.
*/
public void setHasBackgroundFigures(final boolean hasBackgroundFigures) {
this.hasBackgroundFigures = hasBackgroundFigures;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -645,8 +678,17 @@ protected void notifyCameras(PBounds parentBounds) {
*/
@Override
public void fullPaint(final PPaintContext paintContext) {
final boolean isRootLayer = this.isRootLayer;
final KlighdPaintContext kpc = (KlighdPaintContext) paintContext;

final boolean backgroundFiguresOnly = kpc.isBackgroundFiguresOnly();
if (backgroundFiguresOnly && !(this.isDrawnAsForeground && this.hasBackgroundFigures)) {
// if parent child area preforms the background figure drawings and this
// node is not to be drawn in foreground or doesn't have background figure parts
// exit early!
return;
}

final boolean isRootLayer = this.isRootLayer;
if (!isRootLayer && this.visibilityHelper != null
&& this.visibilityHelper.isNotVisibleOn(kpc)) {
return;
Expand All @@ -671,20 +713,31 @@ public void fullPaint(final PPaintContext paintContext) {
paintContext.pushTransform(transform);
paintContext.pushTransparency(getTransparency());

final boolean pushAllFilter = !this.isDrawnAsForeground && kpc.isNonBackgroundFiguresOnly();
if (pushAllFilter) {
// in case this node is to be drawn as a regular (non-foreground) node but has figure parts tagged as background figures
// and we're in non-background drawing mode override the configured non-background figure filter with the all filter,
// and revert the override below, of course!
kpc.pushFigureFilterAll();
}

this.hasBeenDrawn = true;

final boolean applyScale = this.nodeScale != null;
if (applyScale) {
kpc.pushNodeScale(this.nodeScale.doubleValue());
}

if (!getOccluded()) {
if (!getOccluded() && !backgroundFiguresOnly) {
// contributes the opening group tag during SVG exports if semantic data are attached,
// don't do that during the background figure drawing
paint(paintContext);
}

final int count = getChildrenCount();
final List<?> children = getChildrenReference();
for (int i = 0; i < count; i++) {
final PNode each = (PNode) getChildrenReference().get(i);
final PNode each = (PNode) children.get(i);

if (isRootLayer) {
if (isRootAndDrawnViaMainCamera) {
Expand All @@ -705,17 +758,31 @@ public void fullPaint(final PPaintContext paintContext) {
// Hence, it must be that of the outline diagram or any further one.
continue;
}

} else if (backgroundFiguresOnly) {
if (i != 0 || each == this.childArea) {
// do not draw anything except the node's figure if just background drawing is requested
break;
}
}

each.fullPaint(paintContext);
}

paintAfterChildren(paintContext);
if (!backgroundFiguresOnly) {
// contributes the closing group tag during SVG exports if semantic data are attached,
// don't do that during the background figure drawing
paintAfterChildren(paintContext);
}

if (applyScale) {
kpc.popNodeScale();
}

if (pushAllFilter) {
kpc.popFigureFilter();
}

paintContext.popTransparency(getTransparency());
paintContext.popTransform(transform);
}
Expand Down
Loading

0 comments on commit 5ee5843

Please sign in to comment.