Skip to content

Commit 32710e6

Browse files
committed
Fixed neighbour map issues - joinOverlappingNeighbourGroups being used overzealously
1 parent 38be62f commit 32710e6

File tree

9 files changed

+148
-137
lines changed

9 files changed

+148
-137
lines changed

kite9-visualization/src/commonMain/kotlin/org/kite9/diagram/visualization/compaction2/C2Compaction.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ interface C2Compaction {
1818
*/
1919
fun getNeighbours(along: C2Slideable, perp: C2Slideable) : Set<C2Slideable>
2020

21-
/**
22-
* This makes sure that neighbours that arrive at a slideable can also leave
23-
*/
24-
fun invertNeighbours(perp: C2Slideable?)
25-
2621
fun getNeighbourSetsOn(s: C2Slideable) : Set<Set<C2Slideable>>
2722

2823
/**
@@ -31,9 +26,6 @@ interface C2Compaction {
3126
fun getLocationsOn(s: C2Slideable) : Set<C2Slideable>
3227

3328
fun checkConsistency()
34-
35-
fun joinOverlappingNeighbourGroups()
36-
3729
/**
3830
* Duplicates neighbours arriving on one slideable to the other
3931
*/

kite9-visualization/src/commonMain/kotlin/org/kite9/diagram/visualization/compaction2/C2CompactionImpl.kt

Lines changed: 26 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package org.kite9.diagram.visualization.compaction2
33
import org.kite9.diagram.common.elements.Dimension
44
import org.kite9.diagram.logging.LogicException
55
import org.kite9.diagram.model.Diagram
6-
import kotlin.math.max
7-
import kotlin.math.min
86

97
/**
108
* Flyweight class that handles the state of the compaction as it goes along.
@@ -29,7 +27,7 @@ class C2CompactionImpl(private val diagram: Diagram) : C2Compaction {
2927
return diagram
3028
}
3129

32-
private val neighbourDetails = mutableMapOf<C2Slideable, MutableSet<Set<C2Slideable>>>()
30+
val neighbourDetails = mutableMapOf<C2Slideable, MutableSet<Set<C2Slideable>>>()
3331

3432
override fun addNeighbour(along: C2Slideable?, n1: C2Slideable?, n2: C2Slideable?) {
3533
if ((n1 == null) || (n2==null) || (along==null)) {
@@ -90,23 +88,6 @@ class C2CompactionImpl(private val diagram: Diagram) : C2Compaction {
9088
return out
9189
}
9290

93-
override fun invertNeighbours(perp: C2Slideable?) {
94-
if (perp == null) {
95-
throw LogicException("Was expecting perp to be set")
96-
}
97-
val all = neighbourDetails
98-
.filterValues { it.flatMap { it }.contains(perp) }
99-
.keys
100-
101-
val superSet = neighbourDetails.getOrPut(perp) { mutableSetOf() }
102-
if (superSet.size == 1) {
103-
val newContents = all + superSet.first()
104-
superSet.clear()
105-
superSet.add(newContents)
106-
}
107-
}
108-
109-
11091
override fun getNeighbours(along: C2Slideable, perp: C2Slideable) : Set<C2Slideable> {
11192
val superSet = neighbourDetails.getOrPut(along) { mutableSetOf() }
11293
val neighbourSet = superSet.firstOrNull { it.contains(perp) } ?: emptySet()
@@ -188,72 +169,34 @@ class C2CompactionImpl(private val diagram: Diagram) : C2Compaction {
188169
return getSlideablesIncidentWith(s)
189170
}
190171

191-
override fun joinOverlappingNeighbourGroups() {
192-
193-
fun calculateExtent(ss: Set<C2Slideable>) : Pair<Int, Int> {
194-
return Pair(ss.minOf { it.minimumPosition }, ss.maxOf { it.minimumPosition })
195-
}
196-
197-
fun inside(x: Int, a: Pair<Int, Int>) : Boolean {
198-
return x>=a.first && x<=a.second
199-
}
200-
201-
fun overlaps(a: Pair<Int, Int>, b: Pair<Int, Int>) : Boolean {
202-
return inside(a.first, b)
203-
|| inside(a.second, b)
204-
|| inside(b.first, a)
205-
|| inside(b.second, a)
206-
}
207-
208-
fun mergeExtents(a: Pair<Int, Int>, b: Pair<Int, Int>) : Pair<Int, Int> {
209-
return Pair(min(a.first, b.first), max(a.second, b.second))
210-
}
211172

212-
neighbourDetails.keys.forEach { k ->
213-
val oldGroups = neighbourDetails[k]!!
214-
val extents = oldGroups.associateBy { calculateExtent(it) }
215-
val newGroups = mutableMapOf<Pair<Int, Int>, Set<C2Slideable>>()
216-
extents.forEach { (e, ss) ->
217-
val overlapGroups = newGroups.filter { (k, _) -> overlaps(e, k) }
218-
if (overlapGroups.isEmpty()) {
219-
newGroups[e] = ss
220-
} else {
221-
val combinedExtent = overlapGroups.keys.reduce { a, b -> mergeExtents(a, b) }
222-
val combinedSet = overlapGroups.values.reduce { a, b -> a + b }
223-
overlapGroups.keys.forEach { newGroups.remove(it) }
224-
newGroups[mergeExtents(combinedExtent, e)] = combinedSet + ss
225-
}
226-
}
227-
neighbourDetails[k] = newGroups.values.toMutableSet()
228-
}
229-
}
230173

231174
override fun copyNeighbourMap(from: C2Slideable?, to: C2Slideable?) {
232-
if ((from != null) && (to != null)) {
233-
val toIsOrbit = to.getOrbitAnchors().isNotEmpty()
234-
val fromIsOrbit = from.getOrbitAnchors().isNotEmpty()
235-
236-
val toGroup = mutableSetOf<C2Slideable>()
237-
238-
// add "to" to any neighbour set containing from
239-
this.neighbourDetails.entries.forEach { (k, ss) ->
240-
val addTo = ss.filter { it.contains(from) }
241-
ss.removeAll(addTo.toSet() )
242-
val afterAdd = addTo.map { it + to }
243-
ss.addAll(afterAdd)
244-
if (addTo.isNotEmpty()) {
245-
toGroup.add(k)
246-
}
247-
}
248-
249-
if (toIsOrbit && toGroup.isNotEmpty()) {
250-
val first = toGroup.first()
251-
val rest = toGroup - first
252-
rest.forEach { r ->
253-
addNeighbour(to, first, r)
254-
}
255-
}
256-
}
175+
// if ((from != null) && (to != null)) {
176+
// val toIsOrbit = to.getOrbitAnchors().isNotEmpty()
177+
// val fromIsOrbit = from.getOrbitAnchors().isNotEmpty()
178+
//
179+
// val toGroup = mutableSetOf<C2Slideable>()
180+
//
181+
// // add "to" to any neighbour set containing from
182+
// this.neighbourDetails.entries.forEach { (k, ss) ->
183+
// val addTo = ss.filter { it.contains(from) }
184+
// ss.removeAll(addTo.toSet() )
185+
// val afterAdd = addTo.map { it + to }
186+
// ss.addAll(afterAdd)
187+
// if (addTo.isNotEmpty()) {
188+
// toGroup.add(k)
189+
// }
190+
// }
191+
//
192+
// if (toIsOrbit && toGroup.isNotEmpty()) {
193+
// val first = toGroup.first()
194+
// val rest = toGroup - first
195+
// rest.forEach { r ->
196+
// addNeighbour(to, first, r)
197+
// }
198+
// }
199+
// }
257200
}
258201

259202
}

kite9-visualization/src/commonMain/kotlin/org/kite9/diagram/visualization/compaction2/hierarchy/AbstractC2ContainerCompactionStep.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,7 @@ abstract class AbstractC2ContainerCompactionStep(cd: CompleteDisplayer, val rr:
244244
val x1 = sx.first()
245245
val y1 = sy.first()
246246
createRoutableNeighbours(co, x1, y1, cx, cy)
247-
co.invertNeighbours(x1.bl)
248-
co.invertNeighbours(x1.br)
249-
co.invertNeighbours(y1.bl)
250-
co.invertNeighbours(y1.br)
247+
251248
}
252249

253250
}

kite9-visualization/src/commonMain/kotlin/org/kite9/diagram/visualization/compaction2/hierarchy/C2HierarchicalCompactionStep.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,25 @@ class C2HierarchicalCompactionStep(cd: CompleteDisplayer, rr: RoutableReader, g
5656
joinSides(right2, left2, c.getSlackOptimisation(Dimension.H), Dimension.H)
5757
joinSides(down2, up2, c.getSlackOptimisation(Dimension.V), Dimension.V)
5858

59-
// stitch together orbits from next-to containers
59+
// stitch together orbits from next-to containers in a grid
60+
handleGridNeighbours(c)
61+
}
62+
63+
private fun handleGridNeighbours(c: C2Compaction) {
6064
gridOrbitSlideables.keys.forEach {
6165
if (it.s == Side.START) {
62-
val counterpart = Quad(it.c, it.i-1, it.d, Side.END)
66+
val counterpart = Quad(it.c, it.i - 1, it.d, Side.END)
6367
val slideableA = C2Slideable.getNonDoneVersion(gridOrbitSlideables[counterpart])
6468
val slideableB = C2Slideable.getNonDoneVersion(gridOrbitSlideables[it])
6569
c.copyNeighbourMap(slideableB, slideableA)
6670
c.copyNeighbourMap(slideableA, slideableB)
6771
} else {
68-
val counterpart = Quad(it.c, it.i+1, it.d, Side.START)
72+
val counterpart = Quad(it.c, it.i + 1, it.d, Side.START)
6973
val slideableA = C2Slideable.getNonDoneVersion(gridOrbitSlideables[counterpart])
7074
val slideableB = C2Slideable.getNonDoneVersion(gridOrbitSlideables[it])
7175
c.copyNeighbourMap(slideableB, slideableA)
7276
c.copyNeighbourMap(slideableA, slideableB)
7377
}
74-
7578
}
7679
}
7780

@@ -138,9 +141,7 @@ class C2HierarchicalCompactionStep(cd: CompleteDisplayer, rr: RoutableReader, g
138141
val startR = startS.getOrbitingElements()
139142
val endR = endS.getOrbitingElements()
140143

141-
val sOut = so.mergeSlideables(startS, endS)
142-
so.compaction.joinOverlappingNeighbourGroups()
143-
so.compaction.invertNeighbours(sOut)
144+
so.mergeSlideables(startS, endS)
144145

145146
// ensure Separation of rectangulars
146147
val distance = if (startR.isNotEmpty() && endR.isNotEmpty()) {

kite9-visualization/src/commonMain/kotlin/org/kite9/diagram/visualization/compaction2/hierarchy/C2NeighbourBuilderStep.kt

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package org.kite9.diagram.visualization.compaction2.hierarchy
2+
3+
import org.kite9.diagram.common.elements.Dimension
4+
import org.kite9.diagram.logging.LogicException
5+
import org.kite9.diagram.visualization.compaction2.AbstractC2CompactionStep
6+
import org.kite9.diagram.visualization.compaction2.C2Compaction
7+
import org.kite9.diagram.visualization.compaction2.C2CompactionImpl
8+
import org.kite9.diagram.visualization.compaction2.C2Slideable
9+
import org.kite9.diagram.visualization.display.CompleteDisplayer
10+
import org.kite9.diagram.visualization.planarization.rhd.grouping.basic.group.Group
11+
import kotlin.collections.component1
12+
import kotlin.collections.component2
13+
import kotlin.collections.set
14+
import kotlin.math.max
15+
import kotlin.math.min
16+
17+
class C2NeighbourCheckingStep(cd: CompleteDisplayer) : AbstractC2CompactionStep(cd) {
18+
19+
override fun compact(c: C2Compaction, g: Group) {
20+
invertNeighbours(c as C2CompactionImpl)
21+
joinOverlappingNeighbourGroups(c as C2CompactionImpl)
22+
23+
}
24+
25+
26+
27+
/**
28+
* This runs a check to make sure that if you can move onto a slideable in
29+
* one direction, you can also move onto it in the other direction.
30+
*/
31+
fun invertNeighbours(c: C2CompactionImpl) {
32+
33+
fun invert(perp: C2Slideable) {
34+
if (perp == null) {
35+
throw LogicException("Was expecting perp to be set")
36+
}
37+
val all = c.neighbourDetails
38+
.filterValues { it.flatMap { it }.contains(perp) }
39+
.keys
40+
41+
val superSet = c.neighbourDetails.getOrPut(perp) { mutableSetOf() }
42+
if (superSet.size == 1) {
43+
val newContents = all + superSet.first()
44+
superSet.clear()
45+
superSet.add(newContents)
46+
}
47+
}
48+
49+
c.getSlackOptimisation(Dimension.V).getAllSlideables().forEach { invert(it) }
50+
c.getSlackOptimisation(Dimension.H).getAllSlideables().forEach { invert(it) }
51+
}
52+
53+
54+
override val prefix: String
55+
get() = "NBCS"
56+
57+
override val isLoggingEnabled: Boolean
58+
get() = true
59+
60+
companion object {
61+
62+
fun joinOverlappingNeighbourGroups(c: C2CompactionImpl) {
63+
64+
fun calculateExtent(ss: Set<C2Slideable>) : Pair<Int, Int> {
65+
return Pair(ss.minOf { it.minimumPosition }, ss.maxOf { it.minimumPosition })
66+
}
67+
68+
fun inside(x: Int, a: Pair<Int, Int>) : Boolean {
69+
return x>=a.first && x<=a.second
70+
}
71+
72+
fun overlaps(a: Pair<Int, Int>, b: Pair<Int, Int>) : Boolean {
73+
return inside(a.first, b)
74+
|| inside(a.second, b)
75+
|| inside(b.first, a)
76+
|| inside(b.second, a)
77+
}
78+
79+
fun mergeExtents(a: Pair<Int, Int>, b: Pair<Int, Int>) : Pair<Int, Int> {
80+
return Pair(min(a.first, b.first), max(a.second, b.second))
81+
}
82+
83+
c.neighbourDetails.keys.forEach { k ->
84+
val oldGroups = c.neighbourDetails[k]!!
85+
val extents = oldGroups.associateBy { calculateExtent(it) }
86+
val newGroups = mutableMapOf<Pair<Int, Int>, Set<C2Slideable>>()
87+
extents.forEach { (e, ss) ->
88+
val overlapGroups = newGroups.filter { (k, _) -> overlaps(e, k) }
89+
if (overlapGroups.isEmpty()) {
90+
newGroups[e] = ss
91+
} else {
92+
val combinedExtent = overlapGroups.keys.reduce { a, b -> mergeExtents(a, b) }
93+
val combinedSet = overlapGroups.values.reduce { a, b -> a + b }
94+
overlapGroups.keys.forEach { newGroups.remove(it) }
95+
newGroups[mergeExtents(combinedExtent, e)] = combinedSet + ss
96+
}
97+
}
98+
c.neighbourDetails[k] = newGroups.values.toMutableSet()
99+
}
100+
}
101+
}
102+
}

kite9-visualization/src/commonMain/kotlin/org/kite9/diagram/visualization/compaction2/routing/C2ConnectionRouterCompactionStep.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.kite9.diagram.visualization.compaction2.*
1111
import org.kite9.diagram.visualization.compaction2.anchors.AnchorType
1212
import org.kite9.diagram.visualization.compaction2.anchors.ConnAnchor
1313
import org.kite9.diagram.visualization.compaction2.hierarchy.AbstractC2BuilderCompactionStep
14+
import org.kite9.diagram.visualization.compaction2.hierarchy.C2NeighbourCheckingStep
1415
import org.kite9.diagram.visualization.display.CompleteDisplayer
1516
import org.kite9.diagram.visualization.planarization.rhd.grouping.basic.group.CompoundGroup
1617
import org.kite9.diagram.visualization.planarization.rhd.grouping.basic.group.Group
@@ -184,9 +185,8 @@ class C2ConnectionRouterCompactionStep(cd: CompleteDisplayer, gp: GridPositioner
184185
}
185186

186187
if (replacements.isNotEmpty()) {
187-
// since we have re-ordered the diagram, recalculate neighbour map
188-
//c2.buildNeighbours()
189-
c2.joinOverlappingNeighbourGroups()
188+
// since we have re-ordered the diagram, re-check neighbour map
189+
C2NeighbourCheckingStep.joinOverlappingNeighbourGroups(c2 as C2CompactionImpl)
190190
}
191191

192192
c2.checkConsistency()

0 commit comments

Comments
 (0)