diff --git a/src/main/kotlin/com/jaytux/altgraph/layout/Geometry.kt b/src/main/kotlin/com/jaytux/altgraph/layout/Geometry.kt index 1ef44d1..091e764 100644 --- a/src/main/kotlin/com/jaytux/altgraph/layout/Geometry.kt +++ b/src/main/kotlin/com/jaytux/altgraph/layout/Geometry.kt @@ -1,13 +1,4 @@ package com.jaytux.altgraph.layout -interface ISize> { - fun width(): Float - fun height(): Float - fun copy(width: Float, height: Float): S -} - -interface IPoint

> { - fun x(): Float - fun y(): Float - fun copy(x: Float, y: Float): P -} \ No newline at end of file +data class GSize(var width: Float, var height: Float) +data class GPoint(var x: Float, var y: Float) \ No newline at end of file diff --git a/src/main/kotlin/com/jaytux/altgraph/layout/ILayout.kt b/src/main/kotlin/com/jaytux/altgraph/layout/ILayout.kt index 3b2db32..0254ec9 100644 --- a/src/main/kotlin/com/jaytux/altgraph/layout/ILayout.kt +++ b/src/main/kotlin/com/jaytux/altgraph/layout/ILayout.kt @@ -2,12 +2,12 @@ package com.jaytux.altgraph.layout import com.jaytux.altgraph.core.IGraph -interface ILayout, S : ISize> { +interface ILayout { fun graph(): IGraph fun setGraph(graph: IGraph) fun compute() - fun location(vertex: V): P - fun freezeAt(vertex: V, point: P) + fun location(vertex: V): GPoint + fun freezeAt(vertex: V, point: GPoint) fun unfreeze(vertex: V) - fun boundingBox(): S + fun boundingBox(): GSize } \ No newline at end of file diff --git a/src/main/kotlin/com/jaytux/altgraph/layout/PseudoForestLayout.kt b/src/main/kotlin/com/jaytux/altgraph/layout/PseudoForestLayout.kt index 648d2c4..b36a90a 100644 --- a/src/main/kotlin/com/jaytux/altgraph/layout/PseudoForestLayout.kt +++ b/src/main/kotlin/com/jaytux/altgraph/layout/PseudoForestLayout.kt @@ -8,37 +8,36 @@ import kotlin.concurrent.atomics.AtomicBoolean import kotlin.concurrent.atomics.ExperimentalAtomicApi import kotlin.math.max -class PseudoForestLayout, S : ISize>( +class PseudoForestLayout( graph: IGraph, var horizontalMargin: Float, var disjoinXMargin: Float, var interLayer: Float, - val pointZero: P, val sizeZero: S, - vertexSize: (V) -> VertexSize -) : ILayout { - data class VertexSize

, S : ISize>(val vertex: S, val labelOffset: P, val labelSize: S) { - fun fullSize(): S { // TODO: check the math here - val minX = 0 + labelOffset.x() - val minY = 0 + labelOffset.y() - val maxX = vertex.width() + labelOffset.x() + labelSize.width() - val maxY = vertex.height() + labelOffset.y() + labelSize.height() - return vertex.copy( + vertexSize: (V) -> VertexSize +) : ILayout { + data class VertexSize(val vertex: GSize, val labelOffset: GPoint, val labelSize: GSize) { + fun fullSize(): GSize { // TODO: check the math here + val minX = 0 + labelOffset.x + val minY = 0 + labelOffset.y + val maxX = vertex.width + labelOffset.x + labelSize.width + val maxY = vertex.height + labelOffset.y + labelSize.height + return GSize( width = maxX - minX, height = maxY - minY ) } - fun vCenterInBox(): P { // TODO: check the math here - return labelOffset.copy( - x = labelOffset.x() + vertex.width() / 2, - y = labelOffset.y() + vertex.height() / 2 + fun vCenterInBox(): GPoint { // TODO: check the math here + return GPoint( + x = labelOffset.x + vertex.width / 2, + y = labelOffset.y + vertex.height / 2 ) } } private var _graph: IGraph = graph - private val _positions = mutableMapOf() - private var _vertexSize: (V) -> VertexSize = vertexSize - private var _boundingBox: S? = null + private val _positions = mutableMapOf() + private var _vertexSize: (V) -> VertexSize = vertexSize + private var _boundingBox: GSize? = null @OptIn(ExperimentalAtomicApi::class) private var _lock: AtomicBoolean = AtomicBoolean(false) @@ -61,7 +60,7 @@ class PseudoForestLayout, S : ISize>( override fun graph(): IGraph = _graph override fun setGraph(graph: IGraph) { locked { _graph = graph } } - fun setVertexSize(vertexSize: (V) -> VertexSize) { locked { _vertexSize = vertexSize } } + fun setVertexSize(vertexSize: (V) -> VertexSize) { locked { _vertexSize = vertexSize } } // Either a vertex, or a dummy node to break up multi-layer-spanning edges private data class Connector( @@ -120,7 +119,7 @@ class PseudoForestLayout, S : ISize>( // Assign a layer to each vertex by traversing depth-first and ignoring back-edges. val roots = _graph.roots() if (roots.isEmpty()) { // Only reachable nodes matter. - _boundingBox = sizeZero + _boundingBox = GSize(0.0f, 0.0f) return } val layers = mutableMapOf>>() @@ -162,10 +161,10 @@ class PseudoForestLayout, S : ISize>( layers.forEach { (vertex, pair) -> val (layer, _) = pair val size = vertexSizes[vertex]!! - layerHeights[layer] = max(layerHeights[layer], size.first.height()) + layerHeights[layer] = max(layerHeights[layer], size.first.height) if(layer == 0) { // Take into account vertex bounding box offset - val delta = size.second.y() + val delta = size.second.y if(delta < minOffset) minOffset = delta if(delta > maxOffset) maxOffset = delta } @@ -234,7 +233,7 @@ class PseudoForestLayout, S : ISize>( layered[i].forEach { v -> v.x.fold({ node -> - val w = vertexSizes[node]!!.first.width() + val w = vertexSizes[node]!!.first.width layerWidths[i] += w + horizontalMargin }) { /* do nothing */ } } @@ -251,8 +250,8 @@ class PseudoForestLayout, S : ISize>( layer.forEach { v -> v.x.fold({ node -> val offset = vertexSizes[node]!!.second - _positions[node] = pointZero.copy(x = currentX + offset.x(), y = layerY[idx] + offset.y()) - currentX += vertexSizes[node]!!.first.width() + horizontalMargin + _positions[node] = GPoint(x = currentX + offset.x, y = layerY[idx] + offset.y) + currentX += vertexSizes[node]!!.first.width + horizontalMargin }) { /* do nothing */ } } } @@ -263,11 +262,11 @@ class PseudoForestLayout, S : ISize>( // Compute the bounding box // min x and y are 0 - _boundingBox = sizeZero.copy(currentXZero - disjoinXMargin, layerY.last() + layerHeights.last() / 2 + offset) + _boundingBox = GSize(currentXZero - disjoinXMargin, layerY.last() + layerHeights.last() / 2 + offset) } } - override fun location(vertex: V): P { + override fun location(vertex: V): GPoint { if(vertex !in _graph.vertices()) throw GraphException.vertexNotFound(vertex) return _positions[vertex] ?: run { compute() @@ -275,8 +274,8 @@ class PseudoForestLayout, S : ISize>( } } - override fun freezeAt(vertex: V, point: P) = throw UnsupportedOperationException("PseudoForestLayout does not allow freezing vertices.") + override fun freezeAt(vertex: V, point: GPoint) = throw UnsupportedOperationException("PseudoForestLayout does not allow freezing vertices.") override fun unfreeze(vertex: V) { /* no-op: cannot freeze vertices */ } - override fun boundingBox(): S = _boundingBox ?: run { compute(); _boundingBox!! } + override fun boundingBox(): GSize = _boundingBox ?: run { compute(); _boundingBox!! } } \ No newline at end of file diff --git a/src/main/kotlin/com/jaytux/altgraph/swing/GeometryWrappers.kt b/src/main/kotlin/com/jaytux/altgraph/swing/GeometryWrappers.kt index 2e23085..e90967e 100644 --- a/src/main/kotlin/com/jaytux/altgraph/swing/GeometryWrappers.kt +++ b/src/main/kotlin/com/jaytux/altgraph/swing/GeometryWrappers.kt @@ -1,29 +1,12 @@ package com.jaytux.altgraph.swing -import com.jaytux.altgraph.layout.IPoint -import com.jaytux.altgraph.layout.ISize +import com.jaytux.altgraph.layout.GPoint +import com.jaytux.altgraph.layout.GSize import java.awt.Dimension import java.awt.Point -class GSize(val size: Dimension) : ISize { - override fun width(): Float = size.width.toFloat() - override fun height(): Float = size.height.toFloat() - override fun copy(width: Float, height: Float): GSize = - GSize(Dimension(width.toInt(), height.toInt())) +fun GSize.swing() = Dimension(width.toInt(), height.toInt()) +fun GPoint.swing() = Point(x.toInt(), y.toInt()) - override fun hashCode(): Int = size.hashCode() - override fun equals(other: Any?): Boolean = other is GSize && size == other.size - override fun toString(): String = "$size" -} - -class GPoint(val point: Point) : IPoint { - override fun x(): Float = point.x.toFloat() - override fun y(): Float = point.y.toFloat() - override fun copy(x: Float, y: Float): GPoint = - GPoint(Point(x.toInt(), y.toInt())) - - override fun hashCode(): Int = point.hashCode() - override fun equals(other: Any?): Boolean = - other is GPoint && point == other.point - override fun toString(): String = "$point" -} \ No newline at end of file +fun Dimension.graph() = GSize(width.toFloat(), height.toFloat()) +fun Point.graph() = GPoint(x.toFloat(), y.toFloat()) \ No newline at end of file