ContinuousVolume

class ContinuousVolume<A : AgentLike> @JvmOverloads constructor(val context: AgentModel.Context<A>, val xRange: ClosedRange<Double>, val yRange: ClosedRange<Double>, val zRange: ClosedRange<Double>, val torus: Boolean = false, val cellSize: Double = run { val xs = xRange.endInclusive - xRange.start val ys = yRange.endInclusive - yRange.start val zs = zRange.endInclusive - zRange.start val smallest = minOf(xs, ys, zs) if (smallest > 0.0) smallest / Defaults.cellSizeDivisor else 1.0 }, val name: String = "continuousVolume") : Projection<A> (source)

A 3D Euclidean projection — the volumetric analog of ContinuousProjection. Each agent in the context has an optional Point3D position; positions are set explicitly via placeAt / moveTo from user code.

Spatial queries use an internal uniform 3D spatial hash: the volume is partitioned into cubic cells of cellSize units, agents are bucketed by the cell their position falls into, and within scans only the cells overlapping the query sphere rather than every agent. nearest uses progressive radius doubling on top of within. Insertion / move / remove are O(1) amortized.

The default cellSize is min(xSize, ySize, zSize) / cellSizeDivisor, where the divisor defaults to 10 (settable on Defaults). For typical UAV-airspace use cases — large horizontal extent, sparse vertical density — the §13.6 Q3 resolution is to start with the direct 3D bucket map and benchmark on a real model before considering an octree. Sparse-layer scenarios still work; they just allocate a Triple-keyed map entry per occupied layer (cheap in absolute terms — Triple of Ints is ~32 bytes plus map overhead).

Boundaries: positions are not enforced to be inside xRange / yRange / zRange — the ranges describe the projection's logical domain but placeAt / moveTo accept any coordinates. When torus is true, distances wrap on all three axes and bucket coordinates wrap accordingly. (A "ground+ceiling" model — wrap horizontally only, hard boundary vertically — should use torus = false and handle the vertical clamp in the user's motion loop.)

Parameters

context

the context whose agents this projection positions

xRange

logical x bounds

yRange

logical y bounds

zRange

logical z bounds

torus

if true, distances wrap at all three boundaries

cellSize

cubic-cell side length. Defaults to min(xSize, ySize, zSize) / cellSizeDivisor, or 1.0 for degenerate ranges. Must be positive.

name

display name

Constructors

Link copied to clipboard
constructor(context: AgentModel.Context<A>, xRange: ClosedRange<Double>, yRange: ClosedRange<Double>, zRange: ClosedRange<Double>, torus: Boolean = false, cellSize: Double = run { val xs = xRange.endInclusive - xRange.start val ys = yRange.endInclusive - yRange.start val zs = zRange.endInclusive - zRange.start val smallest = minOf(xs, ys, zs) if (smallest > 0.0) smallest / Defaults.cellSizeDivisor else 1.0 }, name: String = "continuousVolume")

Types

Link copied to clipboard
object Defaults

Mutable global defaults for ContinuousVolume internals, mirroring ContinuousProjection.Defaults.

Properties

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Maximum occupancy of any single bucket. Diagnostic for cellSize tuning.

Link copied to clipboard
open override val name: String
Link copied to clipboard

Number of non-empty buckets. Diagnostic for cellSize tuning.

Link copied to clipboard
val size: Int

Number of agents currently placed in this projection.

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Functions

Link copied to clipboard
fun delta(from: Point3D, to: Point3D): Point3D

Signed shortest-direction delta from from to to under this projection's geometry. For non-torus this is to - from. For a torus, each component picks whichever wrap (positive or negative) is shorter on its axis.

Link copied to clipboard
fun distance(a: A, b: A): Double

Distance between two agents. Returns Double.NaN if either agent has no assigned position. Euclidean, or wrapped Euclidean if torus is true.

Distance between two points under this projection's metric (Euclidean, with torus wrap if enabled).

Link copied to clipboard
fun moveTo(agent: A, point: Point3D)

fun moveTo(agent: A, x: Double, y: Double, z: Double)

Update agent's position. Identical to placeAt.

Link copied to clipboard
fun nearest(center: Point3D, k: Int): List<A>

The k nearest agents to center, ordered by distance. Implemented as progressive radius doubling on top of within: start with one cell, grow by Defaults.nearestRadiusGrowthFactor until enough candidates are found, then trim.

Link copied to clipboard
fun neighborsOf(agent: A, radius: Double): List<A>

All agents within radius of agent's position (excluding the agent itself), ordered by distance.

Link copied to clipboard
open override fun onAgentLeft(agent: A)

Called by the AgentModel.Context when agent leaves. Default no-op — projections that track per-agent state (positions, edges) typically override to drop their bookkeeping for the departing agent.

Link copied to clipboard
fun placeAt(agent: A, point: Point3D)

fun placeAt(agent: A, x: Double, y: Double, z: Double)

Place agent at the given coordinates. Idempotent — equivalent to moveTo.

Link copied to clipboard
fun positionOf(agent: A): Point3D?

Return the position of agent, or null if the agent has no assigned position (never placed, or has left the context).

Link copied to clipboard
fun within(center: Point3D, radius: Double): List<A>

All agents whose position is within radius of center, ordered by distance. Includes any agent exactly at center. Uses the spatial hash: only buckets overlapping the query sphere are scanned.