2525import static com .google .common .base .Preconditions .checkArgument ;
2626import static com .google .common .base .Preconditions .checkNotNull ;
2727import static com .google .common .collect .Collections2 .transform ;
28- import static com . google . common . collect . Sets . union ;
28+ import static java . util . Objects . requireNonNull ;
2929
30+ import java .util .NoSuchElementException ;
3031import java .util .Set ;
32+ import java .util .function .BiFunction ;
3133import java .util .function .Function ;
3234
35+ import com .google .common .collect .Iterables ;
3336import com .google .common .collect .Sets ;
3437
3538/**
@@ -48,20 +51,59 @@ private Shapes() {
4851
4952 /**
5053 * Creates a shape, containing all the positions that are contained in both given shapes. This only makes sense, if
51- * the dimensions of the two sets are the same. If they are not, then an {@link IllegalArgumentException} is thrown.
54+ * the dimensions of the two shapes are the same. If they are not, then an {@link IllegalArgumentException} is
55+ * thrown.
5256 *
5357 * @param left the first shape from which to take positions
5458 * @param right the second shape from which to take positions
5559 * @return a shape containing all positions, which are contained in both given shapes
5660 */
5761 public static Shape intersection (Shape left , Shape right ) {
58- checkLeftRightNotNull (left , right );
59- if (!left .hasSameDimensionsAs (right )) {
60- throw new IllegalArgumentException ("The two shapes do not have the same dimension, "
61- + "therefore the intersection of coordinates cannot be determined. Left dimensions: "
62- + left .dimensionSet () + "; Right dimensions: " + right .dimensionSet ());
63- }
64- return Shape .of (Sets .intersection (left .positionSet (), right .positionSet ()));
62+ return combineLeftRightBy (left , right , Sets ::intersection );
63+ }
64+
65+ /**
66+ * Creates a shape, containing all the positions that are either contained in the left or the right shape.This only
67+ * makes sense, if the dimensions of the two shapes are the same. If they are not, then an
68+ * {@link IllegalArgumentException} is thrown.
69+ *
70+ * @param left the first shape from which to take positions
71+ * @param right the second shape from which to take positions
72+ * @return a shape containing all positions, which are contained in at least one of the two shapes
73+ */
74+ public static Shape union (Shape left , Shape right ) {
75+ return combineLeftRightBy (left , right , Sets ::union );
76+ }
77+
78+ /**
79+ * Creates a shape, containing all the positions that are contained at least in one of the given shapes. This only
80+ * makes sense, if the dimensions of the shapes are the same. If they are not, then an
81+ * {@link IllegalArgumentException} is thrown. Further, it is required that at least one element is contained in the
82+ * iterable.
83+ *
84+ * @param shapes the shapes for which the union shall be found
85+ * @return a shape which represents the union of all the shapes
86+ * @throws IllegalArgumentException if the shapes are not of the same dimension
87+ * @throws NoSuchElementException in case the iterable is empty
88+ * @throws NullPointerException if the given iterable is {@code null}
89+ */
90+ public static final Shape union (Iterable <Shape > shapes ) {
91+ return combineBy (shapes , Shapes ::union );
92+ }
93+
94+ /**
95+ * Creates a shape, containing the positions which are contained in each of the given shapes. This only makes sense,
96+ * if the dimensions of the shapes are the same. If they are not, then an {@link IllegalArgumentException} is
97+ * thrown. Further, it is required that at least one element is contained in the iterable.
98+ *
99+ * @param shapes the shapes for which the intersection shall be found
100+ * @return a shape which represents the intersection of all the shapes
101+ * @throws IllegalArgumentException if the shapes are not of the same dimension
102+ * @throws NoSuchElementException in case the iterable is empty
103+ * @throws NullPointerException if the given iterable is {@code null}
104+ */
105+ public static final Shape intersection (Iterable <Shape > shapes ) {
106+ return combineBy (shapes , Shapes ::intersection );
65107 }
66108
67109 /**
@@ -89,7 +131,8 @@ public static Set<Class<?>> dimensionalIntersection(Shape left, Shape right) {
89131 public static Shape dimensionStripped (Shape shape , Set <? extends Class <?>> dimensionsToStrip ) {
90132 checkNotNull (shape , "shape must not be null" );
91133 checkNotNull (dimensionsToStrip , "dimensions must not be null" );
92- return Shape .of (Positions .unique (transform (shape .positionSet (), toGuavaFunction (Positions .stripping (dimensionsToStrip )))));
134+ return Shape .of (Positions
135+ .unique (transform (shape .positionSet (), toGuavaFunction (Positions .stripping (dimensionsToStrip )))));
93136 }
94137
95138 /**
@@ -107,7 +150,7 @@ public static Shape dimensionStripped(Shape shape, Set<? extends Class<?>> dimen
107150 public static Shape outerProduct (Shape left , Shape right ) {
108151 checkArgument (dimensionalIntersection (left , right ).isEmpty (), "The two shapes have "
109152 + "overlapping dimensions. The outer product is not foreseen to be used in this situation." );
110- Shape .Builder builder = Shape .builder (union (left .dimensionSet (), right .dimensionSet ()));
153+ Shape .Builder builder = Shape .builder (Sets . union (left .dimensionSet (), right .dimensionSet ()));
111154 for (Position leftPosition : left .positionSet ()) {
112155 for (Position rightPosition : right .positionSet ()) {
113156 builder .add (Positions .union (leftPosition , rightPosition ));
@@ -137,4 +180,36 @@ public R apply(T input) {
137180 }
138181 };
139182 }
183+
184+ private static void checkLeftRightSameDimensions (Shape left , Shape right ) {
185+ if (!left .hasSameDimensionsAs (right )) {
186+ throw new IllegalArgumentException ("The two shapes do not have the same dimension, "
187+ + "therefore combining of positions does not make sense. Left dimensions: " + left .dimensionSet ()
188+ + "; Right dimensions: " + right .dimensionSet ());
189+ }
190+ }
191+
192+ private static Shape combineLeftRightBy (Shape left , Shape right ,
193+ BiFunction <Set <Position >, Set <Position >, Set <Position >> combiner ) {
194+ checkLeftRightNotNull (left , right );
195+ checkLeftRightSameDimensions (left , right );
196+ return Shape .of (combiner .apply (left .positionSet (), right .positionSet ()));
197+ }
198+
199+ private static Shape combineBy (Iterable <Shape > shapes , BiFunction <Shape , Shape , Shape > combiner ) {
200+ requireNonNull (shapes , "shapes must not be null" );
201+ if (Iterables .isEmpty (shapes )) {
202+ throw new NoSuchElementException ("At least one shape is required." );
203+ }
204+ Shape resultingShape = null ;
205+ for (Shape shape : shapes ) {
206+ if (shape == null ) {
207+ resultingShape = shape ;
208+ } else {
209+ resultingShape = combiner .apply (resultingShape , shape );
210+ }
211+ }
212+ return resultingShape ;
213+ }
214+
140215}
0 commit comments