1 package eu.mihosoft.vrl.v3d;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
8 import javafx.scene.image.WritableImage;
9 import java.util.HashMap;
10 import eu.mihosoft.vrl.v3d.CSG;
11 import javafx.application.Platform;
13 import javafx.geometry.Insets;
14 import javafx.scene.SnapshotParameters;
15 import javafx.scene.layout.AnchorPane;
16 import javafx.scene.layout.Background;
17 import javafx.scene.layout.BackgroundFill;
18 import javafx.scene.layout.CornerRadii;
19 import javafx.scene.layout.Pane;
20 import javafx.scene.paint.Color;
21 import javafx.scene.shape.MeshView;
22 import javafx.scene.transform.Scale;
23 import javafx.stage.Stage;
24 import javafx.scene.image.PixelReader;
28 private static class DefaultSliceImp
implements ISlice {
29 double sizeinPixelSpace = 1500;
30 HashMap<WritableImage, PixelReader> readers =
new HashMap<>();
32 ArrayList<int[]> usedPixels =
new ArrayList<>();
36 Object[] toPixMap(
CSG slicePart) {
51 double size = sizeinPixelSpace * (mySize / 200) * (polys.size() / 300);
58 double xPix = size * (ratioOrentation ? 1.0 : ratio);
59 double yPix = size * (!ratioOrentation ? 1.0 : ratio);
60 double xOffset = slicePart.
getMinX();
61 double yOffset = slicePart.
getMinY();
62 double scaleX = slicePart.
getTotalX() / xPix;
63 double scaleY = slicePart.
getTotalY() / yPix;
68 double imageOffset = 180.0;
69 double imageOffsetMotion = imageOffset * scaleX / 2;
70 int imgx = (int) (xPix + imageOffset);
71 int imgy = (int) (yPix + imageOffset);
72 WritableImage obj_img =
new WritableImage(imgx, imgy);
76 MeshView sliceMesh = slicePart.
getMesh();
77 sliceMesh.getTransforms()
78 .add(javafx.scene.transform.Transform.translate(imageOffsetMotion, imageOffsetMotion));
79 AnchorPane anchor =
new AnchorPane(sliceMesh);
80 AnchorPane.setBottomAnchor(sliceMesh, (
double) 0);
81 AnchorPane.setTopAnchor(sliceMesh, (
double) 0);
82 AnchorPane.setLeftAnchor(sliceMesh, (
double) 0);
83 AnchorPane.setRightAnchor(sliceMesh, (
double) 0);
84 Pane snapshotGroup =
new Pane(anchor);
85 snapshotGroup.prefHeight((
double) (yPix + imageOffset));
86 snapshotGroup.prefWidth((
double) (xPix + imageOffset));
88 .setBackground(
new Background(
new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
90 SnapshotParameters snapshotParameters =
new SnapshotParameters();
91 snapshotParameters.setTransform(
new Scale(1 / scaleX, 1 / scaleY));
92 snapshotParameters.setDepthBuffer(
true);
93 snapshotParameters.setFill(Color.TRANSPARENT);
95 Runnable r =
new Runnable() {
98 snapshotGroup.snapshot(snapshotParameters, obj_img);
102 Platform.runLater(r);
103 while (done ==
false) {
106 }
catch (InterruptedException e) {
114 return new Object[] { obj_img, scaleX, xOffset - imageOffsetMotion, scaleY, yOffset - imageOffsetMotion,
115 imageOffsetMotion, imageOffset };
117 int[] toPixels(
double absX,
double absY,
double xOff,
double yOff,
double scaleX,
double scaleY) {
118 return new int[] { (int) ((absX - xOff) / scaleX), (
int) ((absY - yOff) / scaleY) };
121 boolean pixelBlack(
double absX,
double absY, WritableImage obj_img) {
122 if (readers.get(obj_img) ==
null) {
123 readers.put(obj_img, obj_img.getPixelReader());
125 PixelReader pixelReader = readers.get(obj_img);
126 return pixelReader.getColor((
int) absX, (
int) absY).getOpacity() != 0;
129 boolean pixelEdge(
double absX,
double absY, WritableImage obj_img) {
130 for (
int i = -1; i < 2; i++) {
131 int x = (int) (absX + i);
132 for (
int j = -1; j < 2; j++) {
133 int y = (int) (absY + j);
135 if (!pixelBlack(x, y, obj_img)) {
138 }
catch (Throwable t) {
158 public List<Polygon>
slice(
CSG incoming,
Transform slicePlane,
double normalInsetDistance) {
159 if (Thread.interrupted()) {
162 long startTime = System.currentTimeMillis();
164 List<Polygon> rawPolygons =
new ArrayList<>();
177 CSG slicePart = finalPart
186 Object[] parts = toPixMap(slicePart);
187 WritableImage obj_img = (WritableImage) parts[0];
188 double scaleX = (double) parts[1];
189 double xOffset = (double) parts[2];
190 double scaleY = (double) parts[3];
191 double yOffset = (double) parts[4];
193 ArrayList<Vector3d> points =
new ArrayList<>();
194 for (
Polygon p : rawPolygons) {
195 for (
Vertex v : p.vertices) {
200 ArrayList<Polygon> polys =
new ArrayList<>();
201 ArrayList<int[]> pixelVersionOfPoints =
new ArrayList<>();
203 int[] pix = toPixels(it.x, it.y, xOffset, yOffset, scaleX, scaleY);
204 if (pixelEdge(pix[0], pix[1], obj_img)) {
205 pixelVersionOfPoints.add(pix);
209 ArrayList<int[]> pixelVersionOfPointsFiltered =
new ArrayList<>();
210 for (
int[] d : pixelVersionOfPoints) {
211 boolean testIt =
false;
212 for (
int[] x : pixelVersionOfPointsFiltered) {
213 if (withinAPix(x, d)) {
219 pixelVersionOfPointsFiltered.add(d);
222 pixelVersionOfPoints = pixelVersionOfPointsFiltered;
224 int[] pixStart = pixelVersionOfPoints.get(0);
225 pixelVersionOfPoints.remove(0);
226 int[] nextPoint = pixStart;
227 ArrayList<int[]> listOfPointsForThisPoly =
new ArrayList<>();
228 listOfPointsForThisPoly.add(pixStart);
231 int lastSearchIndex = 0;
232 while ((pixelVersionOfPoints.size() > 0 || listOfPointsForThisPoly.size() > 0) && !Thread.interrupted()) {
234 Object[] results = searchNext(nextPoint, obj_img, lastSearchIndex);
236 if (results ==
null) {
237 listOfPointsForThisPoly.clear();
238 if (pixelVersionOfPoints.size() > 0) {
239 pixStart = pixelVersionOfPoints.remove(0);
240 nextPoint = pixStart;
241 listOfPointsForThisPoly.clear();
242 listOfPointsForThisPoly.add(nextPoint);
248 nextPoint = (
int[]) results[0];
249 lastSearchIndex = (int) results[1];
252 ArrayList<int[]> toRemove =
new ArrayList<>();
253 for (
int[] it : pixelVersionOfPoints) {
254 if (withinAPix(nextPoint, it)) {
259 if (toRemove.size() > 0) {
261 for (
int[] d : toRemove) {
263 pixelVersionOfPoints.remove(d);
264 listOfPointsForThisPoly.add(d);
268 if (listOfPointsForThisPoly.size() > 2) {
269 if (withinAPix(nextPoint, pixStart)) {
272 List<Vector3d> p =
new ArrayList<>();
273 for (
int[] it : listOfPointsForThisPoly) {
274 p.add(
new Vector3d((it[0] * scaleX) + xOffset, (it[1] * scaleY) + yOffset, 0));
279 listOfPointsForThisPoly.clear();
280 if (pixelVersionOfPoints.size() > 0) {
281 pixStart = pixelVersionOfPoints.remove(0);
282 nextPoint = pixStart;
283 listOfPointsForThisPoly.add(nextPoint);
291 if (listOfPointsForThisPoly.size() > 0) {
294 List<Vector3d> p =
new ArrayList<>();
295 for (
int[] it : listOfPointsForThisPoly) {
296 p.add(
new Vector3d((it[0] * scaleX) + xOffset, (it[1] * scaleY) + yOffset, 0));
309 "Slice took: " + (((
double) (System.currentTimeMillis() - startTime)) / 1000.0) +
" seconds");
313 Object[] searchNext(
int[] pixStart, WritableImage obj_img,
int lastSearchIndex) {
316 Object[] ret = searchNextDepth(pixStart, obj_img, index, lastSearchIndex);
318 while (ret ==
null && index < 10 && !Thread.interrupted()) {
320 ret = searchNextDepth(pixStart, obj_img, index, 0);
326 Object[] searchNextDepth(
int[] pixStart, WritableImage obj_img,
double searchSize,
int lastSearchIndex) {
327 ArrayList<int[]> locations =
new ArrayList<>();
328 double inc = Math.toDegrees(Math.atan2(1, searchSize));
329 if (searchSize > 2) {
330 for (
double i = 0; i < 360 + inc; i += inc) {
331 int x = (int) Math.round(Math.cos(Math.toRadians(i)) * searchSize);
332 int y = (int) Math.round(Math.sin(Math.toRadians(i)) * searchSize);
333 locations.add(
new int[] { pixStart[0] + x, pixStart[1] + y });
338 for (
int i = (
int) -searchSize; i < searchSize + 1; i++) {
339 locations.add(
new int[] { (int) (pixStart[0] + searchSize), pixStart[1] + i });
342 for (
int i = (
int) (searchSize - 1); i > -searchSize - 1; i--) {
343 locations.add(
new int[] { pixStart[0] + i, (int) (pixStart[1] + searchSize) });
345 for (
int i = (
int) (searchSize - 1); i > -searchSize - 1; i--) {
346 locations.add(
new int[] { (int) (pixStart[0] - searchSize), pixStart[1] + i });
348 for (
int i = (
int) (-searchSize + 1); i < searchSize + 1; i++) {
349 locations.add(
new int[] { pixStart[0] + i, (int) (pixStart[1] - searchSize) });
355 int searchArraySize = locations.size();
356 if (lastSearchIndex >= searchArraySize) {
359 int end = lastSearchIndex - 1;
361 end = searchArraySize - 1;
363 for (
int i = lastSearchIndex; i != end
364 && !Thread.interrupted(); i = (i + 1 >= searchArraySize ? 0 : i + 1)) {
367 int counterCW = i - 1;
369 counterCW = searchArraySize - 1;
370 int[] ccw = locations.get(counterCW);
371 int[]
self = locations.get(i);
372 boolean w = !pixelBlack(
self[0],
self[1], obj_img);
373 boolean b = pixelBlack(ccw[0], ccw[1], obj_img);
375 boolean useMe =
true;
376 for (
int[] it : usedPixels) {
377 if (it[0] ==
self[0] && it[1] ==
self[1]) {
382 if (w && b && useMe) {
383 usedPixels.add(
self);
385 return new Object[] {
self, i };
404 boolean withinAPix(
int[] incoming,
int[] out) {
406 for (
int i = -pixSize; i < pixSize + 1; i++) {
407 int x = incoming[0] + i;
408 for (
int j = -pixSize; j < pixSize + 1; j++) {
409 int y = incoming[1] + j;
410 if (x == out[0] && y == out[1]) {
449 final double SLICE_UPPER_BOUND = 0.001, SLICE_LOWER_BOUND = -0.001;
453 return vertex.
getZ() < SLICE_UPPER_BOUND && vertex.
getZ() > SLICE_LOWER_BOUND;
456 public static List<Polygon>
slice(
CSG incoming,
Transform slicePlane,
double normalInsetDistance) {
458 if(DefaultSliceImp.class.isInstance(
sliceEngine)) {
461 return sanatize(
new DefaultSliceImp().
slice(incoming, slicePlane, normalInsetDistance));
462 }
catch(IllegalStateException e) {
465 return sanatize(
new DefaultSliceImp().
slice(incoming, slicePlane, normalInsetDistance));
469 }
catch(Throwable e) {
475 for (
int i = 0; i <
slice.size(); i++) {
481 ArrayList<Vector3d> result =
new ArrayList<Vector3d>(points);
482 Collections.reverse(result);
493 public static List<Polygon>
slice(
CSG incoming,
double normalInsetDistance) {
CSG transformed(Transform transform)
List< Polygon > getPolygons()
CSG toolOffset(Number sn)
static void setNumFacesInOffset(int numFacesInOffset)
static boolean isCCW(Polygon polygon)
final List< Vertex > vertices
List< Vector3d > getPoints()
static Polygon fromPoints(List< Vector3d > points, PropertyStorage shared)
static ISlice sliceEngine
static List< Polygon > sanatize(List< Polygon > slice)
static void setSliceEngine(ISlice sliceEngine)
static List< Polygon > slice(CSG incoming, Transform slicePlane, double normalInsetDistance)
static ISlice getSliceEngine()
static List< Polygon > slice(CSG incoming)
static void setNumFacesInOffset(int numFacesInOffset)
static boolean isVertexAtZero(Vertex vertex)
static void setMaxRes(int mr)
static boolean isPolygonAtZero(Polygon polygon)
static List< Polygon > slice(CSG incoming, double normalInsetDistance)