|
1 | 1 | /**
|
2 | 2 | * Extrude.java
|
3 | 3 | *
|
4 |
| - * Copyright 2014-2014 Michael Hoffer <[email protected]>. All rights |
| 4 | + * Copyright 2014-2017 Michael Hoffer <[email protected]>. All rights |
5 | 5 | * reserved.
|
6 | 6 | *
|
7 | 7 | * Redistribution and use in source and binary forms, with or without
|
|
33 | 33 | */
|
34 | 34 | package eu.mihosoft.jcsg;
|
35 | 35 |
|
| 36 | +import eu.mihosoft.vvecmath.Transform; |
36 | 37 | import eu.mihosoft.vvecmath.Vector3d;
|
37 | 38 | import eu.mihosoft.jcsg.ext.org.poly2tri.PolygonUtil;
|
38 | 39 | import java.util.ArrayList;
|
@@ -82,6 +83,105 @@ public static CSG points(Vector3d dir, List<Vector3d> points) {
|
82 | 83 |
|
83 | 84 | return extrude(dir, Polygon.fromPoints(toCCW(newList)));
|
84 | 85 | }
|
| 86 | + |
| 87 | + /** |
| 88 | + * Extrudes the specified path (convex or concave polygon without holes or |
| 89 | + * intersections, specified in CCW) into the specified direction. |
| 90 | + * |
| 91 | + * @param dir direction |
| 92 | + * @param points path (convex or concave polygon without holes or |
| 93 | + * intersections) |
| 94 | + * |
| 95 | + * @return a list containing the extruded polygon |
| 96 | + */ |
| 97 | + public static List<Polygon> points(Vector3d dir, boolean top, boolean bottom, Vector3d... points) { |
| 98 | + |
| 99 | + return extrude(dir, Polygon.fromPoints(toCCW(Arrays.asList(points))), top, bottom); |
| 100 | + } |
| 101 | + |
| 102 | + /** |
| 103 | + * Extrudes the specified path (convex or concave polygon without holes or |
| 104 | + * intersections, specified in CCW) into the specified direction. |
| 105 | + * |
| 106 | + * @param dir direction |
| 107 | + * @param points1 path (convex or concave polygon without holes or |
| 108 | + * intersections) |
| 109 | + * @param points1 path (convex or concave polygon without holes or |
| 110 | + * intersections) |
| 111 | + * |
| 112 | + * @return a list containing the extruded polygon |
| 113 | + */ |
| 114 | + public static List<Polygon> points(Vector3d dir, boolean top, boolean bottom, List<Vector3d> points1) { |
| 115 | + |
| 116 | + List<Vector3d> newList1 = new ArrayList<>(points1); |
| 117 | + |
| 118 | + return extrude(dir, Polygon.fromPoints(toCCW(newList1)), top, bottom); |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * Combines two polygons into one CSG object. Polygons p1 and p2 are treated as top and |
| 123 | + * bottom of a tube segment with p1 and p2 as the profile. <b>Note:</b> both polygons must have the |
| 124 | + * same number of vertices. This method does not guarantee intersection-free CSGs. It is in the |
| 125 | + * responsibility of the caller to ensure that the orientation of p1 and p2 allow for |
| 126 | + * intersection-free combination of both. |
| 127 | + * |
| 128 | + * @param p1 first polygon |
| 129 | + * @param p2 second polygon |
| 130 | + * @return List of polygons |
| 131 | + */ |
| 132 | + public static CSG combine(Polygon p1, Polygon p2) { |
| 133 | + return CSG.fromPolygons(combine(p1,p2,true,true)); |
| 134 | + } |
| 135 | + |
| 136 | + /** |
| 137 | + * Combines two polygons into one CSG object. Polygons p1 and p2 are treated as top and |
| 138 | + * bottom of a tube segment with p1 and p2 as the profile. <b>Note:</b> both polygons must have the |
| 139 | + * same number of vertices. This method does not guarantee intersection-free CSGs. It is in the |
| 140 | + * responsibility of the caller to ensure that the orientation of p1 and p2 allow for |
| 141 | + * intersection-free combination of both. |
| 142 | + * |
| 143 | + * @param p1 first polygon |
| 144 | + * @param p2 second polygon |
| 145 | + * @param bottom defines whether to close the bottom of the tube |
| 146 | + * @param top defines whether to close the top of the tube |
| 147 | + * @return List of polygons |
| 148 | + */ |
| 149 | + public static List<Polygon> combine(Polygon p1, Polygon p2, boolean bottom, boolean top) { |
| 150 | + List<Polygon> newPolygons = new ArrayList<>(); |
| 151 | + |
| 152 | + if (p1.vertices.size() != p2.vertices.size()) { |
| 153 | + throw new RuntimeException("Polygons must have the same number of vertices"); |
| 154 | + } |
| 155 | + |
| 156 | + int numVertices = p1.vertices.size(); |
| 157 | + |
| 158 | + if (bottom) { |
| 159 | + newPolygons.add(p1.flipped()); |
| 160 | + } |
| 161 | + |
| 162 | + for (int i = 0; i < numVertices; i++) { |
| 163 | + |
| 164 | + int nexti = (i + 1) % numVertices; |
| 165 | + |
| 166 | + Vector3d bottomV1 = p1.vertices.get(i).pos; |
| 167 | + Vector3d topV1 = p2.vertices.get(i).pos; |
| 168 | + Vector3d bottomV2 = p1.vertices.get(nexti).pos; |
| 169 | + Vector3d topV2 = p2.vertices.get(nexti).pos; |
| 170 | + |
| 171 | + List<Vector3d> pPoints; |
| 172 | + |
| 173 | + pPoints = Arrays.asList(bottomV2, topV2, topV1); |
| 174 | + newPolygons.add(Polygon.fromPoints(pPoints, p1.getStorage())); |
| 175 | + pPoints = Arrays.asList(bottomV2, topV1, bottomV1); |
| 176 | + newPolygons.add(Polygon.fromPoints(pPoints, p1.getStorage())); |
| 177 | + } |
| 178 | + |
| 179 | + if (top) { |
| 180 | + newPolygons.add(p2); |
| 181 | + } |
| 182 | + |
| 183 | + return newPolygons; |
| 184 | + } |
85 | 185 |
|
86 | 186 | private static CSG extrude(Vector3d dir, Polygon polygon1) {
|
87 | 187 | List<Polygon> newPolygons = new ArrayList<>();
|
@@ -118,6 +218,78 @@ private static CSG extrude(Vector3d dir, Polygon polygon1) {
|
118 | 218 |
|
119 | 219 | }
|
120 | 220 |
|
| 221 | + |
| 222 | + private static List<Polygon> extrude(Vector3d dir, Polygon polygon1, boolean top, boolean bottom) { |
| 223 | + List<Polygon> newPolygons = new ArrayList<>(); |
| 224 | + |
| 225 | + |
| 226 | + if (bottom) { |
| 227 | + newPolygons.addAll(PolygonUtil.concaveToConvex(polygon1)); |
| 228 | + } |
| 229 | + |
| 230 | + Polygon polygon2 = polygon1.translated(dir); |
| 231 | + |
| 232 | + Transform rot = Transform.unity(); |
| 233 | + |
| 234 | + Vector3d a = polygon2.plane.getNormal().normalized(); |
| 235 | + Vector3d b = dir.normalized(); |
| 236 | + |
| 237 | + Vector3d c = a.crossed(b); |
| 238 | + |
| 239 | + double l = c.magnitude(); // sine of angle |
| 240 | + |
| 241 | + if (l > 1e-9) { |
| 242 | + |
| 243 | + Vector3d axis = c.times(1.0 / l); |
| 244 | + double angle = a.angle(b); |
| 245 | + |
| 246 | + double sx = 0; |
| 247 | + double sy = 0; |
| 248 | + double sz = 0; |
| 249 | + |
| 250 | + int n = polygon2.vertices.size(); |
| 251 | + |
| 252 | + for (Vertex v : polygon2.vertices) { |
| 253 | + sx += v.pos.x(); |
| 254 | + sy += v.pos.y(); |
| 255 | + sz += v.pos.z(); |
| 256 | + } |
| 257 | + |
| 258 | + Vector3d center = Vector3d.xyz(sx / n, sy / n, sz / n); |
| 259 | + |
| 260 | + rot = rot.rot(center, axis, angle * Math.PI / 180.0); |
| 261 | + |
| 262 | + for (Vertex v : polygon2.vertices) { |
| 263 | + v.pos = rot.transform(v.pos); |
| 264 | + } |
| 265 | + } |
| 266 | + |
| 267 | + int numvertices = polygon1.vertices.size(); |
| 268 | + for (int i = 0; i < numvertices; i++) { |
| 269 | + |
| 270 | + int nexti = (i + 1) % numvertices; |
| 271 | + |
| 272 | + Vector3d bottomV1 = polygon1.vertices.get(i).pos; |
| 273 | + Vector3d topV1 = polygon2.vertices.get(i).pos; |
| 274 | + Vector3d bottomV2 = polygon1.vertices.get(nexti).pos; |
| 275 | + Vector3d topV2 = polygon2.vertices.get(nexti).pos; |
| 276 | + |
| 277 | + List<Vector3d> pPoints = Arrays.asList(bottomV2, topV2, topV1, bottomV1); |
| 278 | + |
| 279 | + newPolygons.add(Polygon.fromPoints(pPoints, polygon1.getStorage())); |
| 280 | + } |
| 281 | + |
| 282 | + polygon2 = polygon2.flipped(); |
| 283 | + List<Polygon> topPolygons = PolygonUtil.concaveToConvex(polygon2); |
| 284 | + if (top) { |
| 285 | + newPolygons.addAll(topPolygons); |
| 286 | + } |
| 287 | + |
| 288 | + return newPolygons; |
| 289 | + |
| 290 | + } |
| 291 | + |
| 292 | + |
121 | 293 | static List<Vector3d> toCCW(List<Vector3d> points) {
|
122 | 294 |
|
123 | 295 | List<Vector3d> result = new ArrayList<>(points);
|
|
0 commit comments