Skip to content

Commit

Permalink
lsp: improved ID generation to be consistent between syntheses, yet
Browse files Browse the repository at this point in the history
unique. With this, the LSP does not need to be run with the incremental
update strategy anymore to show animations.
  • Loading branch information
NiklasRentzCAU committed Feb 26, 2024
1 parent 582830b commit 4b6db2d
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2018,2019 by
* Copyright 2018-2023 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
Expand Down Expand Up @@ -80,7 +80,7 @@ class KGraphLayoutEngine extends ElkLayoutEngine {

synchronized (kGraphContext.viewModel) {
lightDiagramLayoutConfig.performLayout
RenderingPreparer.prepareRendering(kGraphContext.viewModel)
RenderingPreparer.prepareRendering(kGraphContext.viewModel, diagramState.getKGraphToSModelElementMap(uri))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2018,2019 by
* Copyright 2018-2023 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
Expand Down Expand Up @@ -34,8 +34,7 @@ import static extension de.cau.cs.kieler.klighd.lsp.utils.SprottyProperties.*

/**
* Class for generating unique IDs for any {@link KGraphElement}. Use a single instance of this and call getId() for all
* the elements you need IDs for. IDs will be unique, assuming that hashCode() on KGraphElements returns unique hashes
* per instance.
* the elements you need IDs for. IDs will be unique, based on the position in their parent graph element.
*
* @author nre
*/
Expand Down Expand Up @@ -109,9 +108,7 @@ class KGraphElementIdGenerator {
// the root node is just called $root
val parent = element.eContainer as KGraphElement
var String parentId = null
if (parent !== null) {
parentId = getId(parent)
} else {
if (parent === null) {
id = ID_SEPARATOR + 'root'
if (idToElementMap.get(id) !== null) {
// The graph already contains a root node, this is a connected node dangling without a parent and will
Expand All @@ -122,6 +119,7 @@ class KGraphElementIdGenerator {
idToElementMap.put(id, element)
return id
}
parentId = getId(parent)

// use a prefix depending on the class of the element + the {@link KIdentifier} as id if an identifier is
// defined, otherwise make up a new id based on the position in the model hierarchy with a Separator not
Expand All @@ -130,27 +128,32 @@ class KGraphElementIdGenerator {

val identifier = element.data.filter(KIdentifier)
var char elementSeparator
var int index

switch (element) {
KNode: {
elementSeparator = NODE_SEPARATOR
index = element.parent.children.indexOf(element)
}
KEdge: {
elementSeparator = EDGE_SEPARATOR
index = element.source.outgoingEdges.indexOf(element)
}
KLabel: {
elementSeparator = LABEL_SEPARATOR
index = element.parent.labels.indexOf(element)
}
KPort: {
elementSeparator = PORT_SEPARATOR
index = element.node.ports.indexOf(element)
}
default: {
throw new IllegalArgumentException("Can not generate an id for element of type " + element.class)
}
}

if (identifier.empty) {
elementId = "" + ID_SEPARATOR + elementSeparator + element.hashCode
elementId = "" + ID_SEPARATOR + elementSeparator + index
} else {
elementId = elementSeparator + identifier.head.id
}
Expand All @@ -169,8 +172,8 @@ class KGraphElementIdGenerator {
}

/**
* Class for generating unique IDs for any {@link KRendering}. IDs will be unique, assuming that hashCode() on
* KRenderings returns unique hashes per instance.
* Class for generating unique IDs for any {@link KRendering}. IDs will be unique, based on the position in their
* parent graph element / rendering.
*
* @author nre
*/
Expand All @@ -196,21 +199,11 @@ class KRenderingIdGenerator {
* and puts it in the {@link SprottyProperties#RENDERING_ID} property. This ID can be used for uniquely identifying
* renderings between systems.
*
* @param rendering The rendering
*/
static def void generateIdsRecursive(KRendering rendering) {
if (rendering !== null) {
generateIdsRecursive(rendering, null)
}
}

/**
* Recursive method implementing the behavior described in {@link #generateIdsRecursive(KStyleHolder)}.
*
* @param rendering The rendering that should currently get an ID.
* @paran parentRendering The parent rendering of the current rendering, for convenience.
* @param parentId The ID of the parent to be added as the ID's prefix.
* @param renderingIndex The index of this rendering in relation to the parent.
*/
private static def void generateIdsRecursive(KRendering rendering, KContainerRendering parentRendering) {
static def void generateIdsRecursive(KRendering rendering, String parentId, int renderingIndex) {
if (rendering === null) {
return
}
Expand All @@ -220,15 +213,10 @@ class KRenderingIdGenerator {
return
}

if (parentRendering === null) {
rendering.renderingId = "" + RENDERING_SEPERATOR + rendering.hashCode
} else {
val parentId = parentRendering.renderingId
// Generate a new ID based on the parent rendering's ID.
rendering.renderingId = parentId
+ ID_SEPARATOR + RENDERING_SEPERATOR
+ rendering.hashCode
}
// Generate a new ID based on the parent rendering's ID.
rendering.renderingId = parentId
+ ID_SEPARATOR + RENDERING_SEPERATOR
+ renderingIndex
if (rendering instanceof KPolyline) {
// Special case for KPolyline: It has a junctionPointRendering that also needs an ID.
// Use a new separator and think of this as a new rendering hierarchy with possible children.
Expand All @@ -240,8 +228,8 @@ class KRenderingIdGenerator {
}
if (rendering instanceof KContainerRendering) {
// Each KContainerRendering has child renderings that also need new IDs.
for (childRendering : rendering.children) {
generateIdsRecursive(childRendering, rendering)
for (var int i = 0; i < rendering.children.size; i++) {
generateIdsRecursive(rendering.children.get(i), rendering.renderingId, i)
}
}
}
Expand All @@ -255,7 +243,7 @@ class KRenderingIdGenerator {
* @return The {@link KRendering} with the given ID.
*/
static def findRenderingById(KGraphElement element, String id) {
val ids = id.split("\\" + ID_SEPARATOR)
val ids = id.split("\\" + ID_SEPARATOR + "\\" + ID_SEPARATOR + "\\" + ID_SEPARATOR).get(1).split("\\" + ID_SEPARATOR)
// Every rendering ID is built hierarchically, separated by the RENDERING_SEPERATOR symbol.

val renderings = element.data.filter(KRendering) + element.data.filter(KRenderingRef)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import java.util.ArrayList
import java.util.HashMap
import java.util.List
import java.util.Map
import org.eclipse.sprotty.SModelElement

import static com.google.common.collect.Iterables.filter

Expand All @@ -76,17 +77,20 @@ final class RenderingPreparer {
* Finally, modifiable styles defined by the synthesis are processed for the rendering.
*
* @param element The parent element containing the graph to calculate all rendering bounds for.
* @param kGraphToSGraph A map for identifying the SGraph element for each KGraph element in this graph.
*/
static def void prepareRendering(KGraphElement element) {
static def void prepareRendering(KGraphElement element, Map<KGraphElement, SModelElement> kGraphToSGraph) {
// calculate the sizes of all renderings:
for (data : element.data) {
for (var int i = 0; i < element.data.size; i++) {
val data = element.data.get(i)
switch(data) {
KRenderingLibrary: {
// The library needs to generate ids for all later KRenderingRefs to refer to, but no own bounds,
// since these are generic renderings.
for (rendering : data.renderings) {
for (var int j = 0; j < data.renderings.size; j++) {
val rendering = data.renderings.get(j)
if (rendering instanceof KRendering) {
KRenderingIdGenerator.generateIdsRecursive(rendering)
KRenderingIdGenerator.generateIdsRecursive(rendering, "$$lib$$", j)
}
}
}
Expand All @@ -101,12 +105,11 @@ final class RenderingPreparer {
// and the decorationMap
data.properties.put(CALCULATED_DECORATION_MAP, decorationMap)
// remember the id of the rendering in the reference
data.renderingId = data.rendering.renderingId

data.renderingId = kGraphToSGraph.get(element)?.id + data.rendering.renderingId
}
KRendering: {
// every rendering needs an ID, generate it here
KRenderingIdGenerator.generateIdsRecursive(data)
KRenderingIdGenerator.generateIdsRecursive(data, kGraphToSGraph.get(element)?.id + "$$", i)
handleKRendering(element, data, null, null)
}
}
Expand All @@ -117,25 +120,26 @@ final class RenderingPreparer {

if (element instanceof KLabeledGraphElement) {
for (label : element.labels) {
prepareRendering(label)
prepareRendering(label, kGraphToSGraph)
}
}
if (element instanceof KNode) {
for (node : element.children) {
prepareRendering(node)
prepareRendering(node, kGraphToSGraph)
}
for (edge : element.outgoingEdges) {
prepareRendering(edge)
prepareRendering(edge, kGraphToSGraph)
}
for (port : element.ports) {
prepareRendering(port)
prepareRendering(port, kGraphToSGraph)
}
}

// Also calculate the sizes of all proxy-renderings
val proxyRendering = element.getProperty(KlighdProperties.PROXY_VIEW_PROXY_RENDERING)
if (element.getProperty(KlighdProperties.PROXY_VIEW_RENDER_NODE_AS_PROXY) && proxyRendering !== null) {
for (data : proxyRendering) {
for (var int i = 0; i < proxyRendering.size; i++) {
val data = proxyRendering.get(i)
switch(data) {
KRenderingRef: {
// all references to KRenderings need to place a map with the ids of the renderings and their
Expand All @@ -148,12 +152,12 @@ final class RenderingPreparer {
// and the decorationMap
data.properties.put(CALCULATED_DECORATION_MAP, decorationMap)
// remember the id of the rendering in the reference
data.renderingId = data.rendering.renderingId
data.renderingId = kGraphToSGraph.get(element)?.id + data.rendering.renderingId

}
KRendering: {
// every rendering needs an ID, generate it here
KRenderingIdGenerator.generateIdsRecursive(data)
KRenderingIdGenerator.generateIdsRecursive(data, kGraphToSGraph.get(element)?.id + "$$", i)
if (data.eContainer instanceof KNode) {
// Calculate the size and layout of the proxy first.
val parent = data.eContainer as KNode
Expand Down Expand Up @@ -331,7 +335,6 @@ final class RenderingPreparer {
// to call KLighD's DecoratorPlacementUtil#evaluateDecoratorPlacement the points of the path of the
// parent rendering have to be stored.
var Point2D[] path = #[]
// var path = new KlighdPath(rendering) // TODO: Can I also only use the points of the rendering?
val parentRendering = rendering.eContainer

// Get inset from parent region
Expand Down

0 comments on commit 4b6db2d

Please sign in to comment.