Skip to content

GraphConnectivityExample.kt

src/jvmMain/kotlin/afield/examples/stdlib/GraphConnectivityExample.kt · 327 lines

package and imports — 60 lines, from line 1
package afield.examples.stdlib

import afield.ai.learning.*
import afield.ai.retrieval.*
import afield.animation.interpolation.*
import afield.animation.keyframes.*
import afield.animation.motion.*
import afield.app.commands.*
import afield.app.diagnostics.*
import afield.audio.analysis.*
import afield.audio.synth.*
import afield.data.collections.*
import afield.data.formats.*
import afield.data.graph.*
import afield.data.sorting.*
import afield.document.context.*
import afield.document.graph.*
import afield.document.properties.*
import afield.document.workspace.*
import afield.geometry.mesh.*
import afield.geometry.path.*
import afield.geometry.spatial.*
import afield.graphics.color.*
import afield.graphics.interaction.*
import afield.graphics.render.*
import afield.graphics.shader.*
import afield.graphics.text.*
import afield.graphics.vulkan.buffers.*
import afield.io.osc.*
import afield.io.process.*
import afield.lang.lambda.*
import afield.lang.python.*
import afield.lang.remotejs.*
import afield.lang.stacksl.*
import afield.math.autodiff.*
import afield.math.control.*
import afield.math.estimation.*
import afield.math.functions.*
import afield.math.linalg.*
import afield.math.optimization.*
import afield.math.statistics.*
import afield.math.units.*
import afield.media.image.*
import afield.media.video.*
import afield.runtime.execution.*
import afield.runtime.languages.*
import afield.runtime.lifecycle.*
import afield.simulation.agents.*
import afield.simulation.automata.*
import afield.simulation.particles.*
import afield.study.build.*
import afield.study.evidence.*
import afield.study.search.*
import afield.study.source.*
import afield.study.ui.*
import afield.ui.controls.*
import afield.ui.output.*
import afield.ui.visualization.*
import kotlin.math.abs
import kotlin.random.Random

object GraphConnectivityExample

Example demonstrating the graph connectivity library usage.

This example shows how to use various graph algorithms on 2D and 3D point sets.

object GraphConnectivityExample {

fun main(args: Array<String>)

body — 258 lines, from line 69
    @JvmStatic
    fun main(args: Array<String>) {
        println("=".repeat(60))
        println("Graph Connectivity Library Examples")
        println("=".repeat(60))

        example1_BasicKNN()
        example2_MinimumSpanningTree()
        example3_GabrielGraph()
        example4_ComparingGraphs()
        example5_3DPointCloud()
        example6_BetaSkeleton()

        println("\n" + "=".repeat(60))
        println("All examples completed!")
        println("=".repeat(60))
    }

    private fun example1_BasicKNN() {
        println("\n--- Example 1: K-Nearest Neighbors Graph ---")

        val points = listOf(
            vec(0.0, 0.0),
            vec(1.0, 0.0),
            vec(0.5, 0.5),
            vec(1.0, 1.0),
            vec(0.0, 1.0)
        )

        val knnGraph = vec2KnnGraph(points, k = 2)

        println("Points: ${knnGraph.pointCount}")
        println("Edges: ${knnGraph.edges.size}")

        for (i in 0 until knnGraph.pointCount) {
            val neighbors = knnGraph.neighbors(i)
            println("Point $i neighbors: $neighbors (degree: ${knnGraph.degree(i)})")
        }
    }

    private fun example2_MinimumSpanningTree() {
        println("\n--- Example 2: Minimum Spanning Tree ---")

        val rnd = Random(42)
        val points = List(10) {
            vec(rnd.nextDouble(-5.0, 5.0), rnd.nextDouble(-5.0, 5.0))
        }

        val mst = vec2MinimumSpanningTree(points)

        println("Points: ${mst.pointCount}")
        println("MST edges: ${mst.edges.size} (should be ${points.size - 1})")

        val totalWeight = mst.edges.sumOf { it.weight }
        println("Total MST weight: ${"%.3f".format(totalWeight)}")

        // Verify MST is connected
        val totalDegree = (0 until mst.pointCount).sumOf { mst.degree(it) }
        println("Total degree: $totalDegree (should be ${2 * (points.size - 1)})")
    }

    private fun example3_GabrielGraph() {
        println("\n--- Example 3: Gabriel Graph (Square) ---")

        val square = listOf(
            vec(0.0, 0.0),
            vec(1.0, 0.0),
            vec(1.0, 1.0),
            vec(0.0, 1.0)
        )

        val gabriel = vec2GabrielGraph(square)

        println("Points: ${gabriel.pointCount}")
        println("Gabriel edges: ${gabriel.edges.size}")

        println("\nEdges:")
        for (edge in gabriel.edges) {
            println("  ${edge.i} -- ${edge.j}: weight = ${"%.3f".format(edge.weight)}")
        }
    }

    private fun example4_ComparingGraphs() {
        println("\n--- Example 4: Comparing Different Graphs ---")

        val rnd = Random(123)
        val points = List(20) {
            vec(rnd.nextDouble(0.0, 10.0), rnd.nextDouble(0.0, 10.0))
        }

        val complete = vec2CompleteGraph(points)
        val gabriel = vec2GabrielGraph(points)
        val rng = vec2RelativeNeighborhoodGraph(points)
        val mst = vec2MinimumSpanningTree(points)
        val knn5 = vec2KnnGraph(points, k = 5)

        println("Graph sparsity comparison (20 points):")
        println("  Complete Graph:      ${complete.edges.size} edges")
        println("  Gabriel Graph:       ${gabriel.edges.size} edges")
        println("  RNG:                 ${rng.edges.size} edges")
        println("  MST:                 ${mst.edges.size} edges")
        println("  5-NN Graph:          ${knn5.edges.size} edges")

        println("\nAverage degree:")
        fun avgDegree(graph: PointGraph) =
            (0 until graph.pointCount).map { graph.degree(it) }.average()

        println("  Complete Graph:      ${"%.2f".format(avgDegree(complete))}")
        println("  Gabriel Graph:       ${"%.2f".format(avgDegree(gabriel))}")
        println("  RNG:                 ${"%.2f".format(avgDegree(rng))}")
        println("  MST:                 ${"%.2f".format(avgDegree(mst))}")
        println("  5-NN Graph:          ${"%.2f".format(avgDegree(knn5))}")
    }

    private fun example5_3DPointCloud() {
        println("\n--- Example 5: 3D Point Cloud Connectivity ---")

        val rnd = Random(456)
        val cloud = List(15) {
            vec(
                rnd.nextDouble(-3.0, 3.0),
                rnd.nextDouble(-3.0, 3.0),
                rnd.nextDouble(-3.0, 3.0)
            )
        }

        val knn3d = vec3KnnGraph(cloud, k = 4)
        val gabriel3d = vec3GabrielGraph(cloud)
        val mst3d = vec3MinimumSpanningTree(cloud)

        println("3D point cloud (${cloud.size} points):")
        println("  4-NN Graph:          ${knn3d.edges.size} edges")
        println("  Gabriel Graph:       ${gabriel3d.edges.size} edges")
        println("  MST:                 ${mst3d.edges.size} edges")

        // Analyze MST structure
        val leafNodes = (0 until mst3d.pointCount).count { mst3d.degree(it) == 1 }
        val internalNodes = (0 until mst3d.pointCount).count { mst3d.degree(it) > 2 }
        println("\nMST structure:")
        println("  Leaf nodes:          $leafNodes")
        println("  Internal nodes (>2): $internalNodes")
    }

    private fun example6_BetaSkeleton() {
        println("\n--- Example 6: Beta-Skeleton Progression ---")

        val points = listOf(
            vec(0.0, 0.0),
            vec(1.0, 0.0),
            vec(0.5, 0.8),
            vec(1.5, 0.5)
        )

        println("Beta-skeleton edge counts for 4 points:")

        val betaValues = listOf(0.5, 1.0, 1.5, 2.0, 3.0)
        for (beta in betaValues) {
            val skeleton = vec2BetaSkeleton(points, beta = beta)
            println("  beta = ${"%.1f".format(beta)}: ${skeleton.edges.size} edges")
        }

        println("\nNote:")
        println("  beta = 1.0 ≈ Gabriel graph")
        println("  beta = 2.0 ≈ RNG")
        println("  Higher beta → sparser graph")
    }
}

private fun example7_VoronoiAndDelaunay() {
    println("\n--- Example 7: Voronoi Diagrams and Full Delaunay ---")

    val rnd = Random(789)
    val points = List(8) {
        vec(rnd.nextDouble(0.0, 5.0), rnd.nextDouble(0.0, 5.0))
    }

    // Compute Voronoi diagram
    val voronoi = vec2VoronoiDiagram(points)
    println("Voronoi diagram with ${voronoi.cells.size} cells")

    // Compute full Delaunay triangulation
    val delaunay = vec2DelaunayGraph(points)
    val triangles = vec2DelaunayTriangles(points)

    println("Delaunay triangulation:")
    println("  Edges: ${delaunay.edges.size}")
    println("  Triangles: ${triangles.size}")

    // Convex hull
    val hull = vec2ConvexHull(points)
    println("  Hull vertices: ${hull.hullPoints.size}")
}

private fun example8_CustomDistanceMetric() {
    println("\n--- Example 8: Custom Distance Metrics ---")

    val points = listOf(
        vec(0.0, 0.0),
        vec(1.0, 0.0),
        vec(0.0, 1.0),
        vec(1.0, 1.0)
    )

    // Standard Euclidean distance
    val euclideanKnn = vec2KnnGraph(points, k = 2)
    println("Euclidean KNN edges: ${euclideanKnn.edges.size}")

    // Manhattan distance
    val manhattanMetric: (Int, Int, List<*>) -> Double = { i, j, pts ->
        @Suppress("UNCHECKED_CAST")
        val p = pts as List<Vec2>
        abs(p[i].x - p[j].x).toDouble() + abs(p[i].y - p[j].y).toDouble()
    }

    val manhattanOptions = GraphConnectivityOptions(customDistanceMetric = manhattanMetric)
    val manhattanKnn = vec2KnnGraph(points, k = 2, manhattanOptions)

    println("Manhattan KNN edges: ${manhattanKnn.edges.size}")

    println("\nEuclidean vs Manhattan edge weights:")
    for ((eucEdge, manhEdge) in euclideanKnn.edges.zip(manhattanKnn.edges)) {
        println(
            "  Edge (${eucEdge.i},${eucEdge.j}): Euclidean=${"%.3f".format(eucEdge.weight)}, Manhattan=${
                "%.3f".format(
                    manhEdge.weight
                )
            }"
        )
    }
}

private fun example9_ConvexHullOperations() {
    println("\n--- Example 9: Convex Hull Operations ---")

    val points = listOf(
        vec(0.0, 0.0),
        vec(2.0, 0.0),
        vec(2.0, 2.0),
        vec(0.0, 2.0),
        vec(1.0, 1.0),  // Interior
        vec(0.8, 0.9)   // Interior
    )

    val hull = vec2ConvexHull(points)
    println("Convex hull has ${hull.hullPoints.size} vertices")
    println("Hull indices: ${hull.hullIndices}")

    val hullGraph = vec2ConvexHullGraph(points)
    println("Hull boundary edges: ${hullGraph.edges.size}")

    val projection = vec2HullProjectionGraph(points)
    println("Interior-to-hull connections: ${projection.edges.size}")

    // Show which interior points connect to which hull points
    for (edge in projection.edges) {
        println("  Interior point ${edge.i} → Hull point ${edge.j} (distance: ${"%.3f".format(edge.weight)})")
    }
}