1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.threed;
18
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.stream.Stream;
23
24 import org.apache.commons.geometry.core.Transform;
25 import org.apache.commons.geometry.core.partitioning.AbstractConvexHyperplaneBoundedRegion;
26 import org.apache.commons.geometry.core.partitioning.Hyperplane;
27 import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
28 import org.apache.commons.geometry.core.partitioning.Split;
29
30
31
32
33 public class ConvexVolume extends AbstractConvexHyperplaneBoundedRegion<Vector3D, PlaneConvexSubset>
34 implements BoundarySource3D {
35
36
37 private static final ConvexVolume FULL = new ConvexVolume(Collections.emptyList());
38
39
40
41
42
43 protected ConvexVolume(final List<PlaneConvexSubset> boundaries) {
44 super(boundaries);
45 }
46
47
48 @Override
49 public Stream<PlaneConvexSubset> boundaryStream() {
50 return getBoundaries().stream();
51 }
52
53
54 @Override
55 public double getSize() {
56 if (isFull()) {
57 return Double.POSITIVE_INFINITY;
58 }
59
60 double volumeSum = 0.0;
61
62 for (final PlaneConvexSubset boundary : getBoundaries()) {
63 if (boundary.isInfinite()) {
64 return Double.POSITIVE_INFINITY;
65 }
66
67 final Plane boundaryPlane = boundary.getPlane();
68 final double boundaryArea = boundary.getSize();
69 final Vector3D boundaryCentroid = boundary.getCentroid();
70
71 volumeSum += boundaryArea * boundaryCentroid.dot(boundaryPlane.getNormal());
72 }
73
74 return volumeSum / 3.0;
75 }
76
77
78 @Override
79 public Vector3D getCentroid() {
80 double volumeSum = 0.0;
81
82 double sumX = 0.0;
83 double sumY = 0.0;
84 double sumZ = 0.0;
85
86 for (final PlaneConvexSubset boundary : getBoundaries()) {
87 if (boundary.isInfinite()) {
88 return null;
89 }
90
91 final Plane boundaryPlane = boundary.getPlane();
92 final double boundaryArea = boundary.getSize();
93 final Vector3D boundaryCentroid = boundary.getCentroid();
94
95 final double scaledVolume = boundaryArea * boundaryCentroid.dot(boundaryPlane.getNormal());
96
97 volumeSum += scaledVolume;
98
99 sumX += scaledVolume * boundaryCentroid.getX();
100 sumY += scaledVolume * boundaryCentroid.getY();
101 sumZ += scaledVolume * boundaryCentroid.getZ();
102 }
103
104 if (volumeSum > 0) {
105 final double size = volumeSum / 3.0;
106
107
108
109
110 final double centroidScale = 1.0 / (4 * size);
111 return Vector3D.of(
112 sumX * centroidScale,
113 sumY * centroidScale,
114 sumZ * centroidScale);
115 }
116
117 return null;
118 }
119
120
121 @Override
122 public Split<ConvexVolume> split(final Hyperplane<Vector3D> splitter) {
123 return splitInternal(splitter, this, PlaneConvexSubset.class, ConvexVolume::new);
124 }
125
126
127 @Override
128 public RegionBSPTree3D toTree() {
129 return RegionBSPTree3D.from(getBoundaries(), true);
130 }
131
132
133 @Override
134 public PlaneConvexSubset trim(final HyperplaneConvexSubset<Vector3D> convexSubset) {
135 return (PlaneConvexSubset) super.trim(convexSubset);
136 }
137
138
139
140
141
142 public ConvexVolume transform(final Transform<Vector3D> transform) {
143 return transformInternal(transform, this, PlaneConvexSubset.class, ConvexVolume::new);
144 }
145
146
147
148
149 public static ConvexVolume full() {
150 return FULL;
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164 public static ConvexVolume fromBounds(final Plane... planes) {
165 return fromBounds(Arrays.asList(planes));
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179 public static ConvexVolume fromBounds(final Iterable<? extends Plane> boundingPlanes) {
180 final List<PlaneConvexSubset> facets = new ConvexRegionBoundaryBuilder<>(PlaneConvexSubset.class)
181 .build(boundingPlanes);
182 return facets.isEmpty() ? full() : new ConvexVolume(facets);
183 }
184 }