1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.util.Iterator;
4 import java.util.function.Function;
5 import java.util.function.Supplier;
6
7 import org.djunits.value.vdouble.scalar.Length;
8 import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedObject;
9
10 /**
11 * Iterable that additionally provides support for PerceptionCollectors. These gather raw data, to only 'perceive' the result.
12 * <p>
13 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15 * </p>
16 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
17 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
18 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
19 * @param <P> perceived object type
20 * @param <U> underlying object type
21 */
22 public interface PerceptionCollectable<P extends PerceivedObject, U> extends PerceptionIterable<P>
23 {
24
25 /**
26 * Collect the underlying objects in to a perceived result. This methodology is loosely based on Stream.collect().
27 * @param collector collector
28 * @param <C> collection result type
29 * @param <I> intermediate type
30 * @return collection result
31 */
32 default <C, I> C collect(final PerceptionCollector<C, ? super U, I> collector)
33 {
34 return collect(collector.getIdentity(), collector.getAccumulator(), collector.getFinalizer());
35 }
36
37 /**
38 * Collect the underlying objects in to a perceived result. This methodology is loosely based on Stream.collect().
39 * @param identity the initial intermediate result value
40 * @param accumulator accumulator
41 * @param finalizer finalizer
42 * @param <C> collection result type
43 * @param <I> intermediate type
44 * @return collection result
45 */
46 <C, I> C collect(Supplier<I> identity, PerceptionAccumulator<? super U, I> accumulator, Function<I, C> finalizer);
47
48 /**
49 * Returns an iterator over the underlying objects.
50 * @return iterator
51 */
52 Iterator<U> underlying();
53
54 /**
55 * Returns an iterator over the underlying objects coupled with the distance.
56 * @return iterator
57 */
58 Iterator<UnderlyingDistance<U>> underlyingWithDistance();
59
60 /**
61 * Combination of an accumulator and a finalizer.
62 * @param <C> collection result type
63 * @param <U> underlying object type
64 * @param <I> intermediate result type
65 */
66 interface PerceptionCollector<C, U, I>
67 {
68 /**
69 * Returns the identity value, the initial intermediate value.
70 * @return identity value, the initial intermediate value
71 */
72 Supplier<I> getIdentity();
73
74 /**
75 * Returns the accumulator.
76 * @return accumulator
77 */
78 PerceptionAccumulator<U, I> getAccumulator();
79
80 /**
81 * Returns the finalizer.
82 * @return finalizer
83 */
84 Function<I, C> getFinalizer();
85 }
86
87 /**
88 * Accumulates an object one at a time in to an accumulating intermediate result.
89 * @param <U> underlying object type
90 * @param <I> intermediate result type
91 */
92 @FunctionalInterface
93 interface PerceptionAccumulator<U, I>
94 {
95 /**
96 * Accumulate the next object to intermediate result.
97 * @param intermediate intermediate result before accumulation of object
98 * @param object next object to include
99 * @param distance distance to the considered object
100 * @return intermediate result after accumulation of object
101 */
102 Intermediate<I> accumulate(Intermediate<I> intermediate, U object, Length distance);
103 }
104
105 /**
106 * Wrapper of intermediate result with info for the iterator algorithm.
107 * @param <I> intermediate result type
108 */
109 class Intermediate<I>
110 {
111 /** Number of underlying objects being iterated. */
112 private int number = 0;
113
114 /** Intermediate object. */
115 private I object;
116
117 /** Whether to stop accumulating. */
118 private boolean stop = false;
119
120 /**
121 * Constructor.
122 * @param object identity value
123 */
124 public Intermediate(final I object)
125 {
126 this.object = object;
127 }
128
129 /**
130 * Get intermediate object.
131 * @return intermediate object
132 */
133 public I getObject()
134 {
135 return this.object;
136 }
137
138 /**
139 * Set intermediate object.
140 * @param object intermediate object
141 */
142 public void setObject(final I object)
143 {
144 this.object = object;
145 }
146
147 /**
148 * Returns the number of the underlying object currently being accumulated, starts at 0 for the first.
149 * @return number of the underlying object currently being accumulated
150 */
151 public int getNumber()
152 {
153 return this.number;
154 }
155
156 /**
157 * Method for the iterator to increase the underlying object number.
158 */
159 public void step()
160 {
161 this.number++;
162 }
163
164 /**
165 * Method for the accumulator to indicate the iterator can stop.
166 */
167 public void stop()
168 {
169 this.stop = true;
170 }
171
172 /**
173 * Method for the iterator to check if it can stop.
174 * @return whether the iterator can stop
175 */
176 public boolean isStop()
177 {
178 return this.stop;
179 }
180 }
181
182 /**
183 * Wrapper for object and its distance.
184 * @param object underlying object
185 * @param distance distance to object
186 * @param <U> underlying object type
187 */
188 record UnderlyingDistance<U>(U object, Length distance) implements Comparable<UnderlyingDistance<U>>
189 {
190 @Override
191 public int compareTo(final UnderlyingDistance<U> o)
192 {
193 int out = this.distance.compareTo(o.distance);
194 if (out != 0)
195 {
196 return out;
197 }
198 if (this.object == null)
199 {
200 if (o.object == null)
201 {
202 return 0;
203 }
204 return -1;
205 }
206 if (o.object == null)
207 {
208 return 1;
209 }
210 return Integer.compare(this.object.hashCode(), o.object.hashCode());
211 }
212 }
213
214 }