You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Co-authored-by: Rafael Schouten <[email protected]>
This PR performs all the necessary actions to add a spatial tree interface and surrounding machinery to GeometryOps. Actually accelerating GeometryOps functions with the spatial trees is left for another PR - we don't tackle that in this one.
This implements, at a high level, functional, non-allocating single-tree and dual tree queries over arbitrary predicates.
## SpatialTreeInterface
Introduces a new SpatialTreeInterface module that provides an interface for spatial trees, built somewhat on top of AbstractTrees.jl.
## LoopStateMachine
Introduces a new `LoopStateMachine` module that provides utilities for returning state from functions running inside loops. This is particularly useful for operations like clipping where state transitions or early termination may be needed.
The module includes:
- `Action` struct for representing different control flow actions
- `@controlflow` macro for processing actions within loops
- Support for `:continue`, `:break`, `:return`, and `:full_return` actions
Comprehensive tests verify all supported actions work correctly.
- Leaf nodes contain references to the geometries they represent as indices (or so we assume here)
10
+
11
+
## Why is this useful?
12
+
13
+
- It allows us to write algorithms that can work with any spatial tree type, without having to know the details of the tree type.
14
+
- for example, dual tree traversal / queries
15
+
- It allows us to flexibly and easily swap out and use different tree types, depending on the problem at hand.
16
+
17
+
This is also a zero cost interface if implemented correctly! Verified implementations exist for "flat" trees like the "Natural Index" from `tg`, and "hierarchical" trees like the `STRtree` from `SortTileRecursiveTree.jl`.
18
+
19
+
## Interface
20
+
21
+
-`isspatialtree(tree)::Bool`
22
+
-`isleaf(node)::Bool` - is the node a leaf node? In this context, a leaf node is a node that does not have other nodes as its children, but stores a list of indices and extents (even if implicit).
23
+
-`getchild(node)` - get the children of a node. This may be materialized if necessary or available, but can also be lazy (like a generator).
24
+
-`getchild(node, i)` - get the `i`-th child of a node.
25
+
-`nchild(node)::Int` - the number of children of a node.
26
+
-`child_indices_extents(node)` - an iterator over the indices and extents of the children of a **leaf** node.
27
+
28
+
These are the only methods that are required to be implemented.
29
+
30
+
Optionally, one may define:
31
+
-`node_extent(node)` - get the extent of a node. This falls back to `GI.extent` but can potentially be overridden if you want to return a different but extent-like object.
32
+
33
+
They enable the generic query functions described below:
34
+
35
+
## Query functions
36
+
37
+
-`do_query(f, predicate, node)` - call `f(i)` for each index `i` in `node` that satisfies `predicate(extent(i))`.
38
+
-`do_dual_query(f, predicate, tree1, tree2)` - call `f(i1, i2)` for each index `i1` in `tree1` and `i2` in `tree2` that satisfies `predicate(extent(i1), extent(i2))`.
39
+
40
+
These are both completely non-allocating, and will only call `f` for indices that satisfy the predicate.
41
+
You can of course build a standard query interface on top of `do_query` if you want - that's simply:
42
+
```julia
43
+
a = Int[]
44
+
do_query(Base.Fix1(push!, a), predicate, node)
45
+
```
46
+
where `predicate` might be `Base.Fix1(Extents.intersects, extent_to_query)`.
0 commit comments