Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

253
LINES

< > BotCompany Repo | #1032229 // PtTree - tree structure for quick finding of points in any rectangle [probably OK but super slow to make]

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (4623L/25K).

1  
// TODO: find splitPoint faster (just access the median element!)
2  
// TODO: really slow making the initial tree (5s for ~250 points)
3  
4  
sclass PtTree extends AbstractSet<Pt> {
5  
  int size;
6  
  Node root = new Leaf(null);
7  
  
8  
  // config
9  
  
10  
  int maxPointsPerNode = 4;
11  
    
12  
  abstract class Node {
13  
    Node parent;
14  
    
15  
    abstract bool add(Pt p);
16  
    abstract void collectPointsIn(Rect r, L<Pt> out);
17  
    
18  
    abstract void replaceChild(Node from, Node to);
19  
    
20  
    void addAll(Iterable<Pt> l) {
21  
      fOr (Pt p : l) add(p);
22  
    }
23  
  }
24  
  
25  
  class Leaf extends Node {
26  
    Cl<Pt> points;
27  
    
28  
    *(Node *parent) {}
29  
    
30  
    bool add(Pt p) {
31  
      if (main contains(points, p)) false;
32  
      if (points == null) points = new L;
33  
      points.add(p);
34  
      ++size;
35  
      possiblySplit();
36  
      true;
37  
    }
38  
    
39  
    void possiblySplit {
40  
      if (l(points) > maxPointsPerNode)
41  
        split();
42  
    }
43  
    
44  
    void collectPointsIn(Rect r, L<Pt> out) {
45  
      fOr (p : points)
46  
        if (containsPt(r, p))
47  
          out.add(p);
48  
    }
49  
    
50  
    record noeq ProposedSplit(int dimension, int splitPoint, int count) {
51  
      int error() { ret abs(count-half(l(points))); }
52  
    }
53  
    
54  
    Split split() {
55  
      int n = l(points), half = n/2;
56  
      
57  
      ProposedSplit xSplit = checkSplit(0);
58  
      ProposedSplit ySplit = checkSplit(1);
59  
      
60  
      var best = xSplit.error() < ySplit.error() ? xSplit : ySplit;
61  
62  
      new Split split;
63  
      split.dimension = (byte) best.dimension;
64  
      split.splitPoint = best.splitPoint;
65  
      Leaf a = new Leaf(split);
66  
      IPred<Pt> pred = p -> ptCoord(split.dimension, p) >= split.splitPoint;
67  
      a.addAll(antiFilter(pred, points));
68  
      split.a = a;
69  
      Leaf b = new Leaf(split);
70  
      b.addAll(filter(pred, points));
71  
      split.b = b;
72  
      
73  
      a.possiblySplit();
74  
      b.possiblySplit();
75  
      
76  
      ret replaceMeWith(split);
77  
    }
78  
    
79  
    <A extends Node> A replaceMeWith(A node) {
80  
      node.parent = parent;
81  
      if (parent != null) parent.replaceChild(this, node);
82  
      else if (root == this) root = node;
83  
      ret node;
84  
    }
85  
86  
    ProposedSplit checkSplit(int dimension) {
87  
      L<Pt> lx = sortedBy(points, p -> ptCoord(dimension, p));
88  
      new ProposedSplit ps;
89  
      ps.dimension = dimension;
90  
      
91  
      int n = l(points), half = n/2;
92  
      int i = 0;
93  
      int splitPoint = Int.MIN_VALUE;
94  
      while true {
95  
        int lastSplitPoint = splitPoint;
96  
        splitPoint = ptCoord(lx.get(i++), dimension)+1;
97  
        int lastI = i;
98  
        while (i < n && ptCoord(lx.get(i), dimension) < splitPoint) ++i;
99  
        if (i >= half) {
100  
          if (abs(lastI-half) < abs(i-half)) {
101  
            ps.splitPoint = lastSplitPoint;
102  
            ps.count = lastI;
103  
          } else {
104  
            ps.splitPoint = splitPoint;
105  
            ps.count = i;
106  
          }
107  
          ret ps;
108  
        }
109  
      }
110  
    }
111  
    
112  
    void replaceChild(Node from, Node to) { unimplemented(); } // doesn't need
113  
  }
114  
  
115  
  static final int minInf = Int.MIN_VALUE/4;
116  
  static final int inf = Int.MAX_VALUE/4;
117  
  
118  
  class Split extends Node {
119  
    byte dimension; // 0 for X, 1 for Y
120  
    int splitPoint;
121  
    Node a, b;
122  
    
123  
    bool add(Pt p) {
124  
      ret (ptCoord(p, dimension) >= splitPoint ? b : a).add(p);
125  
    }
126  
    
127  
    Rect aRect() {
128  
      ret rectFromPoints(minInf, minInf,
129  
        dimension == 0 ? splitPoint : inf,
130  
        dimension == 1 ? splitPoint : inf);
131  
    }
132  
    
133  
    Rect bRect() {
134  
      ret rectFromPoints(
135  
        dimension == 0 ? splitPoint : minInf,
136  
        dimension == 1 ? splitPoint : minInf,
137  
        inf, inf);
138  
    }
139  
    
140  
    void collectPointsIn(Rect r, L<Pt> out) {
141  
      Rect rA = intersectRect(r, aRect());
142  
      if (nempty(rA))
143  
        a.collectPointsIn(rA, out);
144  
        
145  
      Rect rB = intersectRect(r, bRect());
146  
      if (nempty(rB))
147  
        b.collectPointsIn(rB, out);
148  
    }
149  
    
150  
    void replaceChild(Node from, Node to) {
151  
      if (a == from) a = to;
152  
      if (b == from) b = to;
153  
    } 
154  
  }
155  
  
156  
  public bool add(Pt p) {
157  
    ret root.add(p);
158  
  }
159  
  
160  
  L<Pt> pointsIn aka pointsInRect(Rect r) {
161  
    new L<Pt> out;
162  
    root.collectPointsIn(r, out);
163  
    ret out;
164  
  }
165  
  
166  
  public bool contains(O p) {
167  
    ret p instanceof Pt && contains(p/Pt);
168  
  }
169  
  
170  
  bool contains(Pt p) {
171  
    ret nempty(pointsIn(rect(p.x, p.y, 1, 1)));
172  
  }
173  
  
174  
  // use this to make a PtTree!
175  
  // It's actually makes a balanced tree.
176  
  // It is assumed that points contains no duplicates
177  
  static PtTree fromPointSet(Iterable<Pt> points) {
178  
    new PtTree tree;
179  
    Leaf root = cast tree.root;
180  
    root.points = cloneList(points);
181  
    tree.size = l(root.points);
182  
    root.possiblySplit();
183  
    ret tree;
184  
  }
185  
  
186  
  public int size() { ret size; }
187  
  
188  
  public Iterator<Pt> iterator() {
189  
    /* What we actually want is a coroutine yielding the values.
190  
    // Like this, if we had this syntax:
191  
    
192  
    ret coroutine {
193  
      void iterate(Node node) {
194  
        if (node cast Leaf)
195  
          returnAll(node.points);
196  
        else {
197  
          cast node to Split;
198  
          iterate(node.a);
199  
          iterate(node.b);
200  
        }
201  
        
202  
        run { iterate(root); }
203  
      }
204  
    };
205  
    
206  
    // So how do we turn this into an iterator?
207  
    // Maybe through VStack.Computable.
208  
    */
209  
    
210  
    new Var<Iterator<Pt>> out;
211  
    new VStack stack;
212  
    stack.push(new co_iterate(root, out));
213  
    
214  
    ret iff_endOnNull(new IF0<Pt> {
215  
      bool done;
216  
      
217  
      public Pt get() {
218  
        while ping (true) {
219  
          // something in the pipe? return it
220  
          if (out.has())
221  
            if (out->hasNext())
222  
              ret out->next();
223  
            else
224  
              out.clear(); // and clean the pipe afterwards, boy!
225  
              
226  
          if (done) null; // end of the line
227  
          // nothing in the pipe, gotta walk the tree some more
228  
          if (!step(stack))
229  
            set done; // prepare to get off the train
230  
        }
231  
      }
232  
    });
233  
  }
234  
  
235  
  record noeq co_iterate(Node node, Var<Iterator<Pt>> out) extends VStackComputableWithStep {
236  
    void step(VStack stack) {
237  
      if (step == 0) {
238  
        if (node cast Leaf) {
239  
          out.set(main iterator(node.points));
240  
          stack.return();
241  
        } else {
242  
          cast node to Split;
243  
          stack.call(new co_iterate(node.a, out));
244  
          ret with step = 1;
245  
        }
246  
      } else {
247  
        cast node to Split;
248  
        stack.replace(new co_iterate(node.b, out));
249  
      }
250  
    }
251  
  }
252  
253  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx

No comments. add comment

Snippet ID: #1032229
Snippet name: PtTree - tree structure for quick finding of points in any rectangle [probably OK but super slow to make]
Eternal ID of this version: #1032229/29
Text MD5: fbd207613c282e9244d96da4ca3cd560
Transpilation MD5: d89e72e91ab65f5db75e707df0555b61
Author: stefan
Category: javax / 2d space
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-08-22 19:11:42
Source code size: 6597 bytes / 253 lines
Pitched / IR pitched: No / No
Views / Downloads: 148 / 325
Version history: 28 change(s)
Referenced in: [show references]