Skip to content

Commit b6b33e2

Browse files
committed
Generalize interface to allow aribtrary operations for all offsets in grid.
This addresses the conversation in #53
1 parent 7a0557c commit b6b33e2

File tree

2 files changed

+214
-17
lines changed

2 files changed

+214
-17
lines changed

src/main/java/net/imglib2/algorithm/util/Grids.java

Lines changed: 159 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@
3737
import java.util.ArrayList;
3838
import java.util.Arrays;
3939
import java.util.List;
40+
import java.util.function.Consumer;
4041
import java.util.function.Function;
42+
import java.util.stream.IntStream;
4143

4244
import net.imglib2.FinalInterval;
4345
import net.imglib2.Interval;
46+
import net.imglib2.Localizable;
47+
import net.imglib2.Positionable;
4448
import net.imglib2.util.Intervals;
4549
import net.imglib2.util.Pair;
4650
import net.imglib2.util.ValuePair;
@@ -52,6 +56,158 @@
5256
*/
5357
public class Grids
5458
{
59+
60+
/**
61+
*
62+
* Helper interface for moving by a specified distance along a specified
63+
* dimension.
64+
*
65+
*/
66+
public static interface MoveForDimension
67+
{
68+
/**
69+
*
70+
* @param by
71+
* Distance to move.
72+
* @param dimension
73+
* Dimension along which to move.
74+
*/
75+
public void move( long by, int dimension );
76+
}
77+
78+
/**
79+
*
80+
* Helper interface to set position of specified dimension.
81+
*
82+
*/
83+
public static interface SetForDimension
84+
{
85+
/**
86+
*
87+
* @param to
88+
* Set to this value.
89+
* @param dimension
90+
* Affected dimension.
91+
*/
92+
public void set( long to, int dimension );
93+
}
94+
95+
/**
96+
*
97+
* Helper interface to get current value of specified dimension
98+
*
99+
*/
100+
public static interface GetForDimension
101+
{
102+
/**
103+
*
104+
* @param dimension
105+
* @return Current value at specified dimension.
106+
*/
107+
public long get( int dimension );
108+
}
109+
110+
/**
111+
* Execute {@code runAtOffset} for each offset of a grid defined by
112+
* {@code min}, {@code max}, and {@code blockSize}. The offset object
113+
* {@link p} must be provided by the caller.
114+
*
115+
* @param min
116+
* @param max
117+
* @param blockSize
118+
* @param p
119+
* @param runAtOffset
120+
*/
121+
public static < P extends Positionable & Localizable > void forEachOffset(
122+
final long[] min,
123+
final long[] max,
124+
final int[] blockSize,
125+
final P p,
126+
final Runnable runAtOffset )
127+
{
128+
129+
assert p.numDimensions() == min.length: "Dimensionality mismatch!";
130+
131+
forEachOffset( min, max, blockSize, ( to, d ) -> p.setPosition( to, d ), d -> p.getLongPosition( d ), ( by, d ) -> p.move( by, d ), runAtOffset );
132+
}
133+
134+
/**
135+
*
136+
* Execute {@code runAtOffset} for each offset of a grid defined by
137+
* {@code min}, {@code max}, and {@code blockSize}.
138+
*
139+
* @param min
140+
* @param max
141+
* @param blockSize
142+
* @param runAtOffset
143+
*/
144+
public static void forEachOffset(
145+
final long[] min,
146+
final long[] max,
147+
final int[] blockSize,
148+
final Consumer< long[] > runAtOffset )
149+
{
150+
final long[] offset = new long[ min.length ];
151+
forEachOffset( min, max, blockSize, ( to, d ) -> offset[ d ] = to, ( d ) -> offset[ d ], ( by, d ) -> offset[ d ] += by, () -> runAtOffset.accept( offset ) );
152+
}
153+
154+
/**
155+
* Execute a {@link Runnable} for each offset of a grid defined by
156+
* {@code min}, {@code max}, and {@code blockSize}.
157+
*
158+
* This method is agonstic of the object that represents the current offset.
159+
* Instead, the caller provides {@code setOffsetForDimension},
160+
* {@code getOffsetForDimension}, and {@code moveForDimension} to move the
161+
* offset object in the correct positions. Consequently,
162+
* {@code runAtEachOffset} needs to be a stateful object that is aware of
163+
* the current position.
164+
*
165+
* See {@link Grids#forEachOffset(long[], long[], int[], Consumer)} and
166+
* {@link Grids#forEachOffset(long[], long[], int[], Positionable, Runnable)}
167+
* for example/convenience implementations for {@code long[]} and
168+
* {@code Positionable & Lozalizable} offset objects.
169+
*
170+
* @param min
171+
* @param max
172+
* @param blockSize
173+
* @param setOffsetForDimension
174+
* @param getOffsetForDimension
175+
* @param moveForDimension
176+
* @param runAtEachOffset
177+
*/
178+
public static void forEachOffset(
179+
final long[] min,
180+
final long[] max,
181+
final int[] blockSize,
182+
final SetForDimension setOffsetForDimension,
183+
final GetForDimension getOffsetForDimension,
184+
final MoveForDimension moveForDimension,
185+
final Runnable runAtEachOffset )
186+
{
187+
188+
assert Arrays.stream( blockSize ).filter( b -> b < 1 ).count() == 0: "Only non-zero blockSize allowed!";
189+
assert min.length == blockSize.length: "Dimensionality mismatch!";
190+
assert max.length == blockSize.length: "Dimensionality mismatch!";
191+
assert IntStream.range( 0, min.length ).filter( d -> max[ d ] < min[ d ] ).count() == 0: "max has to greater or equal than min for all dimensions!";
192+
193+
final int nDim = min.length;
194+
for ( int d = 0; d < nDim; ++d )
195+
setOffsetForDimension.set( min[ d ], d );
196+
197+
for ( int d = 0; d < nDim; )
198+
{
199+
runAtEachOffset.run();
200+
for ( d = 0; d < nDim; ++d )
201+
{
202+
moveForDimension.move( blockSize[ d ], d );
203+
if ( getOffsetForDimension.get( d ) <= max[ d ] )
204+
break;
205+
else
206+
setOffsetForDimension.set( min[ d ], d );
207+
}
208+
}
209+
}
210+
55211
/**
56212
*
57213
* Get all blocks of size {@code blockSize} contained within an interval
@@ -125,7 +281,7 @@ public static List< Interval > collectAllContainedIntervals( final long[] min, f
125281
*/
126282
public static List< long[] > collectAllOffsets( final long[] dimensions, final int[] blockSize )
127283
{
128-
return collectAllOffsets( dimensions, blockSize, block -> block );
284+
return collectAllOffsets( dimensions, blockSize, block -> block.clone() );
129285
}
130286

131287
/**
@@ -157,7 +313,7 @@ public static < T > List< T > collectAllOffsets( final long[] dimensions, final
157313
*/
158314
public static List< long[] > collectAllOffsets( final long[] min, final long[] max, final int[] blockSize )
159315
{
160-
return collectAllOffsets( min, max, blockSize, block -> block );
316+
return collectAllOffsets( min, max, blockSize, block -> block.clone() );
161317
}
162318

163319
/**
@@ -176,21 +332,7 @@ public static List< long[] > collectAllOffsets( final long[] min, final long[] m
176332
public static < T > List< T > collectAllOffsets( final long[] min, final long[] max, final int[] blockSize, final Function< long[], T > func )
177333
{
178334
final List< T > blocks = new ArrayList<>();
179-
final int nDim = min.length;
180-
final long[] offset = min.clone();
181-
for ( int d = 0; d < nDim; )
182-
{
183-
final long[] target = offset.clone();
184-
blocks.add( func.apply( target ) );
185-
for ( d = 0; d < nDim; ++d )
186-
{
187-
offset[ d ] += blockSize[ d ];
188-
if ( offset[ d ] <= max[ d ] )
189-
break;
190-
else
191-
offset[ d ] = min[ d ];
192-
}
193-
}
335+
forEachOffset( min, max, blockSize, offset -> blocks.add( func.apply( offset ) ) );
194336
return blocks;
195337
}
196338

src/test/java/net/imglib2/algorithm/util/GridsTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
package net.imglib2.algorithm.util;
22

33
import java.util.List;
4+
import java.util.stream.IntStream;
45

56
import org.junit.Assert;
67
import org.junit.Test;
78

89
import net.imglib2.FinalInterval;
910
import net.imglib2.Interval;
11+
import net.imglib2.RandomAccess;
12+
import net.imglib2.img.array.ArrayImg;
13+
import net.imglib2.img.array.ArrayImgs;
14+
import net.imglib2.img.basictypeaccess.array.LongArray;
15+
import net.imglib2.type.logic.BitType;
16+
import net.imglib2.type.numeric.integer.UnsignedIntType;
1017
import net.imglib2.util.Intervals;
1118
import net.imglib2.util.Pair;
19+
import net.imglib2.view.IntervalView;
20+
import net.imglib2.view.Views;
1221

1322
public class GridsTest
1423
{
@@ -108,6 +117,28 @@ public void testWithOffset()
108117
}
109118
}
110119

120+
@Test
121+
public void testRandomAccess()
122+
{
123+
final ArrayImg< BitType, LongArray > data = ArrayImgs.bits( 1, 2, 3, 4, 5 );
124+
data.forEach( BitType::setZero );
125+
final IntervalView< BitType > translated = Views.translate( data, 5, 4, 3, 2, 1 );
126+
final RandomAccess< BitType > access = translated.randomAccess();
127+
final UnsignedIntType count = new UnsignedIntType();
128+
Grids.forEachOffset(
129+
Intervals.minAsLongArray( translated ),
130+
Intervals.maxAsLongArray( translated ),
131+
IntStream.generate( () -> 1 ).limit( data.numDimensions() ).toArray(),
132+
access,
133+
() -> {
134+
count.inc();
135+
access.get().setOne();
136+
} );
137+
138+
Assert.assertEquals( Intervals.numElements( data ), count.get() );
139+
data.forEach( v -> Assert.assertTrue( v.get() ) );
140+
}
141+
111142
private static long[] add( final long[] arr, final long[] add )
112143
{
113144
final long[] result = new long[ arr.length ];
@@ -116,4 +147,28 @@ private static long[] add( final long[] arr, final long[] add )
116147
return result;
117148
}
118149

150+
@Test( expected = AssertionError.class )
151+
public void testZeroBlockSizeAssertion()
152+
{
153+
Grids.forEachOffset( new long[] { 0, 0 }, new long[] { 1, 1 }, new int[] { 1, 0 }, arr -> {} );
154+
}
155+
156+
@Test( expected = AssertionError.class )
157+
public void testMinDimensionalityMismatchAssertion()
158+
{
159+
Grids.forEachOffset( new long[] { 0, 0, 0 }, new long[] { 1, 1 }, new int[] { 1, 1 }, arr -> {} );
160+
}
161+
162+
@Test( expected = AssertionError.class )
163+
public void testMaxDimensionalityMismatchAssertion()
164+
{
165+
Grids.forEachOffset( new long[] { 0, 0 }, new long[] { 1, 1, 1 }, new int[] { 1, 1 }, arr -> {} );
166+
}
167+
168+
@Test( expected = AssertionError.class )
169+
public void testMinLargerThanMaxAssertion()
170+
{
171+
Grids.forEachOffset( new long[] { 0, 2 }, new long[] { 1, 1 }, new int[] { 1, 1 }, arr -> {} );
172+
}
173+
119174
}

0 commit comments

Comments
 (0)