Commit cfd8c5b0 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

Merge branch 'dev/MeshIntegration' into 'master'

Dev/mesh integration

See merge request !117
parents d4db6865 cd1d4906
Pipeline #214030 passed with stages
in 136 minutes and 36 seconds
......@@ -7,6 +7,7 @@ import org.vadere.gui.components.model.DefaultSimulationConfig;
import org.vadere.gui.components.utils.Messages;
import org.vadere.meshing.mesh.impl.PSLG;
import org.vadere.meshing.utils.io.poly.PSLGGenerator;
import org.vadere.simulator.utils.pslg.PSLGConverter;
import org.vadere.state.scenario.Obstacle;
import org.vadere.util.config.VadereConfig;
import org.vadere.util.geometry.shapes.VPolygon;
......@@ -58,30 +59,10 @@ public class ActionGeneratePoly extends AbstractAction {
outputFile = fileChooser.getSelectedFile().toString().endsWith(".poly") ? fileChooser.getSelectedFile()
: new File(fileChooser.getSelectedFile().toString() + ".poly");
List<Obstacle> boundingObstacles = model.getTopography().getBoundaryObstacles();
PSLGConverter pslgConverter = new PSLGConverter();
PSLG pslg = pslgConverter.toPSLG(model.getTopography());
String polyString = PSLGGenerator.toPSLG(pslg.getSegmentBound(), pslg.getHoles());
Rectangle2D.Double boundWithBorder = model.getTopography().getBounds();
double boundWidth = model.getTopography().getBoundingBoxWidth();
VRectangle bound = new VRectangle(boundWithBorder.x + boundWidth, boundWithBorder.y + boundWidth, boundWithBorder.width - 2*boundWidth, boundWithBorder.height - 2*boundWidth);
List<Obstacle> obstacles = new ArrayList<>(model.getTopography().getObstacles());
obstacles.removeAll(model.getTopography().getBoundaryObstacles());
List<VPolygon> obsShapes = obstacles.stream()
.map(obs -> obs.getShape())
.map(shape -> new VPolygon(shape))
.collect(Collectors.toList());
// this computes the union of intersecting obstacles.
obsShapes = PSLG.constructHoles(obsShapes);
// this will help to construct a valid non-rectangular bound.
List<VPolygon> polygons = PSLG.constructBound(new VPolygon(bound), obsShapes);
String polyString = PSLGGenerator.toPSLG(
polygons.get(0),
polygons.size() > 1 ? polygons.subList(1, polygons.size()) : Collections.emptyList());
try {
outputFile.createNewFile();
Writer out = new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8");
......
......@@ -8,6 +8,8 @@ import org.vadere.state.scenario.Agent;
import org.vadere.state.scenario.MeasurementArea;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.scenario.Stairs;
import org.vadere.util.config.VadereConfig;
import org.vadere.util.geometry.shapes.VShape;
import org.vadere.util.geometry.shapes.Vector2D;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VLine;
......@@ -39,6 +41,8 @@ public abstract class DefaultRenderer {
private IDefaultModel defaultModel;
private BufferedImage logo;
private static final double rotNeg90 = - Math.PI /2;
private boolean renderNodes = VadereConfig.getConfig().getBoolean("Gui.showNodes");
private double nodeRadius = VadereConfig.getConfig().getDouble("Gui.node.radius");
/**
* <p>Default constructor.</p>
......@@ -146,10 +150,17 @@ public abstract class DefaultRenderer {
protected void renderScenarioElement(final Iterable<? extends ScenarioElement> elements, final Graphics2D g,
final Color color) {
final Color tmpColor = g.getColor();
g.setColor(color);
for (ScenarioElement element : elements) {
fill(element.getShape(), g);
VShape shape = element.getShape();
g.setColor(color);
fill(shape, g);
if(renderNodes) {
for(VPoint node : shape.getPath()) {
g.setColor(Color.RED);
g.fill(new VCircle(node, nodeRadius));
}
}
}
g.setColor(tmpColor);
......@@ -243,6 +254,12 @@ public abstract class DefaultRenderer {
final Color tmpColor = graphics.getColor();
graphics.setColor(color);
fill(element.getShape(), graphics);
if(renderNodes) {
for(VPoint node : element.getShape().getPath()) {
graphics.setColor(Color.RED);
graphics.fill(new VCircle(node, nodeRadius));
}
}
graphics.setColor(tmpColor);
}
......
......@@ -89,7 +89,7 @@ public class RecordTriangulationMovie {
while (nSteps < 300) {
nSteps++;
if(!meshImprover.initializationFinished()) {
if(!meshImprover.isInitialized()) {
addPictures(recorder, meshRenderer, 10, (int)bbound.getWidth()*1000, (int)bbound.getHeight()*1000);
}
else if(finished) {
......@@ -106,7 +106,7 @@ public class RecordTriangulationMovie {
e.printStackTrace();
}*/
overAllTime.resume();
meshImprover.step();
meshImprover.improve();
overAllTime.suspend();
distmeshPanel.repaint();
}
......@@ -128,5 +128,4 @@ public class RecordTriangulationMovie {
}
}
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
package org.vadere.meshing.examples;
import org.jetbrains.annotations.NotNull;
import org.vadere.meshing.mesh.gen.MeshPanel;
import org.vadere.meshing.mesh.impl.PMeshPanel;
import org.vadere.meshing.mesh.impl.PSLG;
import org.vadere.meshing.mesh.triangulation.DistanceFunctionApproxBF;
import org.vadere.meshing.mesh.triangulation.EdgeLengthFunctionApprox;
import org.vadere.meshing.mesh.triangulation.improver.eikmesh.impl.PEikMesh;
import org.vadere.meshing.mesh.triangulation.triangulator.gen.GenRuppertsTriangulator;
import org.vadere.meshing.mesh.triangulation.triangulator.impl.PContrainedDelaunayTriangulator;
import org.vadere.meshing.mesh.triangulation.triangulator.impl.PDelaunayTriangulator;
import org.vadere.meshing.mesh.triangulation.triangulator.impl.PRuppertsTriangulator;
import org.vadere.meshing.utils.io.poly.PSLGGenerator;
import org.vadere.meshing.utils.io.tex.TexGraphGenerator;
import org.vadere.util.math.IDistanceFunction;
......@@ -18,35 +11,37 @@ import org.vadere.util.math.IDistanceFunction;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.Collectors;
public class BackgroundMeshExamples {
private static final Color lightBlue = new Color(0.8584083044982699f, 0.9134486735870818f, 0.9645674740484429f);
public static void main(String ... args) throws IOException, InterruptedException {
//localFeatureSize("/poly/kaiserslautern.poly");
localFeatureSize("/poly/kaiserslautern_1.poly");
//localFeatureSize("/poly/kaiserslautern_large.poly");
//localFeatureSize("/poly/room.poly");
//localFeatureSize("/poly/corner.poly");
//localFeatureSize("/poly/narrowCorridor.poly");
//localFeatureSize("/poly/bridge.poly");
distance("/poly/mf_small_very_simple.poly");
//distance("/poly/mf_small_very_simple.poly");
//distance("/poly/mf_small_very_simple.poly");
//distance("/poly/mf_small_very_simple.poly");
}
public static void localFeatureSize(@NotNull final String fileName) throws IOException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream(fileName);
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
EdgeLengthFunctionApprox edgeLengthFunctionApprox = new EdgeLengthFunctionApprox(pslg);
edgeLengthFunctionApprox.smooth(0.2);
edgeLengthFunctionApprox.printPython();
//System.out.println(TexGraphGenerator.toTikz(edgeLengthFunctionApprox.getMesh(), f-> lightBlue, 1.0f));
System.out.println(TexGraphGenerator.toTikz(edgeLengthFunctionApprox.getMesh(), f-> lightBlue, 1.0f));
}
public static void distance(@NotNull final String fileName) throws IOException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream(fileName);
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
DistanceFunctionApproxBF distFunctionApprox = new DistanceFunctionApproxBF(pslg, IDistanceFunction.create(pslg.getSegmentBound(), pslg.getHoles()));
distFunctionApprox.printPython();
}
......
......@@ -7,6 +7,7 @@ import org.vadere.meshing.mesh.triangulation.IEdgeLengthFunction;
import org.vadere.meshing.mesh.triangulation.improver.eikmesh.gen.GenEikMesh;
import org.vadere.meshing.mesh.triangulation.improver.eikmesh.impl.PEikMesh;
import org.vadere.meshing.mesh.triangulation.triangulator.impl.PDelaunayTriangulator;
import org.vadere.meshing.utils.color.Colors;
import org.vadere.meshing.utils.io.movie.MovRecorder;
import org.vadere.meshing.utils.io.poly.MeshPolyReader;
import org.vadere.meshing.utils.io.poly.MeshPolyWriter;
......@@ -23,7 +24,10 @@ import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import org.vadere.util.math.IDistanceFunction;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
......@@ -31,17 +35,20 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
/**
* Shows a very basic example how {@link GenEikMesh} can be used
* to mesh a simple geometry.
*/
public class EikMeshExamples {
private static final Color lightBlue = new Color(0.8584083044982699f, 0.9134486735870818f, 0.9645674740484429f);
public static void main(String... args) throws InterruptedException, IOException {
squareHole2();
//delaunayTriangulation();
//distanceFuncCombination();
uniformMeshDiscFunction(0.15);
//uniformMeshDiscFunction(0.15);
//uniformMeshRingFunction(0.05);
//combineDistanceFunctions();
//edgeLengthFunction();
......@@ -49,6 +56,42 @@ public class EikMeshExamples {
userDefinedPoints();*/
}
public static void squareHole() throws InterruptedException {
IDistanceFunction distanceFunction = p -> Math.max(Math.abs(p.getX()-0.5), Math.abs(p.getY()-0.5)) - 0.5;
var improver = new PEikMesh(distanceFunction, p -> 0.1, 0.01, new VRectangle(-2, -2, 4, 4));
var panel = new PMeshPanel(improver.getMesh(), 500, 500);
panel.display("A square mesh");
panel.repaint();
improver.initialize();
for(int i = 0; i < 1000; i++) {
Thread.sleep(50);
improver.improve();
panel.repaint();
}
}
public static void squareHole2() throws InterruptedException {
VRectangle rect = new VRectangle(0, 0, 1, 1);
IDistanceFunction distanceFunction = p -> rect.distance(p);
var improver = new PEikMesh(distanceFunction, p -> 0.1, 0.01, new VRectangle(-2, -2, 4, 4), Arrays.asList(rect));
var panel = new PMeshPanel(improver.getMesh(), 500, 500);
panel.display("A square mesh");
panel.repaint();
improver.initialize();
for(int i = 0; i < 1000; i++) {
Thread.sleep(50);
improver.improve();
panel.repaint();
}
}
public static void delaunayTriangulation() throws InterruptedException {
Random random = new Random(0);
int width = 10;
......@@ -78,7 +121,7 @@ public class EikMeshExamples {
panel.repaint();
for(int i = 0; i < 1000; i++) {
Thread.sleep(5000);
Thread.sleep(50);
improver.improve();
panel.repaint();
}
......@@ -174,8 +217,7 @@ public class EikMeshExamples {
d,
p -> edgeLength + 0.3 * Math.abs(d.apply(p)),
edgeLength,
GeometryUtils.boundRelative(boundary.getPath()),
Arrays.asList(rect)
boundary
);
// generate the mesh
......@@ -186,14 +228,14 @@ public class EikMeshExamples {
// (optional) define the gui to display the mesh
PMeshPanel meshPanel = new PMeshPanel(meshImprover.getMesh(), 1000, 800);
var recorder = new MovRecorder<>(meshImprover, meshPanel.getMeshRenderer(), 1024, 800, meshImprover.getMesh().getBound());
recorder.record();
//var recorder = new MovRecorder<>(meshImprover, meshPanel.getMeshRenderer(), 1024, 800, meshImprover.getMesh().getBound());
//recorder.record();
meshPanel.display("Geometry defined by shapes");
//meshImprover.initialize();
//meshPanel.repaint();
meshImprover.initialize();
meshPanel.repaint();
/*while (!meshImprover.isFinished()) {
while (!meshImprover.isFinished()) {
meshImprover.improve();
try {
......@@ -202,13 +244,13 @@ public class EikMeshExamples {
e.printStackTrace();
}
meshPanel.repaint();
}*/
}
meshImprover.setDistanceFunc(d_b);
meshImprover.setEdgeLenFunction(p -> edgeLength + 0.3 * Math.abs(d_b.apply(p)));
recorder.record();
//recorder.record();
/*while (!meshImprover.isFinished()) {
while (!meshImprover.isFinished()) {
meshImprover.improve();
try {
......@@ -217,18 +259,18 @@ public class EikMeshExamples {
e.printStackTrace();
}
meshPanel.repaint();
}*/
}
meshImprover.setDistanceFunc(d_r);
meshImprover.setEdgeLenFunction(p -> edgeLength + 0.3 * Math.abs(d_r.apply(p)));
recorder.record();
//recorder.record();
IDistanceFunction d_c = IDistanceFunction.createDisc(0, 0, 0.5);
meshImprover.setDistanceFunc(d_c);
meshImprover.setEdgeLenFunction(p -> edgeLength /*+ 0.3 * Math.abs(d_c.apply(p))*/);
recorder.record();
/*while (!meshImprover.isFinished()) {
//recorder.record();
while (!meshImprover.isFinished()) {
meshImprover.improve();
try {
......@@ -237,13 +279,22 @@ public class EikMeshExamples {
e.printStackTrace();
}
meshPanel.repaint();
}*/
}
recorder.finish();
//recorder.finish();
Function<PVertex, Color> vertexColorFunction = v -> {
if(meshImprover.getMesh().isAtBoundary(v)){
return Colors.BLUE;
} else if(meshImprover.isFixPoint(v)) {
return Colors.RED;
} else {
return Color.BLACK;
}
};
//System.out.println(TexGraphGenerator.toTikz(meshImprover.getMesh()));
//write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, null, vertexColorFunction,1.0f, true)), "mesh.tex");
}
/**
......@@ -532,4 +583,29 @@ public class EikMeshExamples {
// display the mesh
//meshPanel.display("User defined Points");
}
private static void write(final String string, final String filename) throws IOException {
File outputFile = new File("./"+filename);
try(FileWriter fileWriter = new FileWriter(outputFile)) {
fileWriter.write(string);
}
}
private static String toTexDocument(final String tikz) {
return "\\documentclass[usenames,dvipsnames]{standalone}\n" +
"\\usepackage[utf8]{inputenc}\n" +
"\\usepackage{amsmath}\n" +
"\\usepackage{amsfonts}\n" +
"\\usepackage{amssymb}\n" +
"\\usepackage{calc}\n" +
"\\usepackage{graphicx}\n" +
"\\usepackage{tikz}\n" +
"\\usepackage{xcolor}\n" +
"\n" +
"%\\clip (-0.200000,-0.100000) rectangle (1.2,0.8);\n" +
"\\begin{document}"+
tikz
+
"\\end{document}";
}
}
......@@ -21,7 +21,6 @@ import org.vadere.util.math.IDistanceFunction;
import java.awt.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
......@@ -62,39 +61,59 @@ public class EikMeshPlots {
//cornerLFS();
//uniformRing(0.3);
randomDelaunay();
}
public static void randomDelaunay() throws IOException {
public static void randomDelaunay() throws IOException, InterruptedException {
ArrayList<EikMeshPoint> points = new ArrayList<>();
Random random = new Random(0);
for(int i = 0; i < 1000; i++) {
Random random = new Random(1);
for (int i = 0; i < 100; i++) {
points.add(new EikMeshPoint(random.nextDouble() * 10, random.nextDouble() * 10));
}
PDelaunayTriangulator dt = new PDelaunayTriangulator(points);
dt.generate();
write(toTexDocument(TexGraphGenerator.toTikz(dt.getMesh(), f-> lightBlue, 1.0f)), "eikmesh_random_before");
write(toTexDocument(TexGraphGenerator.toTikz(dt.getMesh(), f -> lightBlue, 1.0f)), "eikmesh_random_before");
VPolygon bound = dt.getMesh().toPolygon(dt.getMesh().getBorder());
var meshImprover = new PEikMesh(
p -> 1.0 + Math.abs(bound.distance(p)),
p -> 2.0,
dt.getTriangulation()
);
// generate the mesh
meshImprover.generate();
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, 1.0f)), "eikmesh_random_after");
// display the mesh
PMeshPanel meshPanel = new PMeshPanel(dt.getMesh(), 1000, 1000);
meshPanel.display("Random Delaunay triangulation");
while (!meshImprover.isFinished()) {
meshImprover.improve();
Thread.sleep(10);
meshPanel.repaint();
}
meshImprover.finish();
meshPanel.repaint();
var meshImprover2 = new PEikMesh(
p -> 0.05 + 0.2 * Math.sqrt(p.getY() * p.getY() / 20.0 + p.getX() * p.getX() / 20.0),
dt.getTriangulation(),
true
);
while (!meshImprover2.isFinished()) {
meshImprover2.improve();
Thread.sleep(1);
meshPanel.repaint();
System.out.println("imp");
}
System.out.println("end");
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f -> lightBlue, 1.0f)), "eikmesh_random_after");
}
public static void kaiserslautern() throws IOException, InterruptedException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream("/poly/kaiserslautern.poly");
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
Collection<VPolygon> holes = pslg.getHoles();
VPolygon segmentBound = pslg.getSegmentBound();
......@@ -114,7 +133,7 @@ public class EikMeshPlots {
meshImprover.generate();
var meshPanel = new PMeshPanel(meshImprover.getMesh(), 1000, 800);
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, 1.0f)), "eikmesh_kaiserslautern_"+ Double.toString(h0).replace('.', '_'));
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f -> lightBlue, 1.0f)), "eikmesh_kaiserslautern_" + Double.toString(h0).replace('.', '_'));
// display the mesh
meshPanel.display("Combined distance functions " + h0);
......@@ -122,7 +141,7 @@ public class EikMeshPlots {
public static void bridge() throws IOException, InterruptedException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream("/poly/bridge.poly");
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
Collection<VPolygon> holes = pslg.getHoles();
VPolygon segmentBound = pslg.getSegmentBound();
IDistanceFunction distanceFunction = IDistanceFunction.create(segmentBound, holes);
......@@ -146,7 +165,7 @@ public class EikMeshPlots {
}
//meshImprover.generate();
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, 1.0f)), "eikmesh_kaiserslautern_"+ Double.toString(h0).replace('.', '_'));
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f -> lightBlue, 1.0f)), "eikmesh_kaiserslautern_" + Double.toString(h0).replace('.', '_'));
// display the mesh
meshPanel.display("Combined distance functions " + h0);
......@@ -154,7 +173,7 @@ public class EikMeshPlots {
public static void roomLFS() throws IOException, InterruptedException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream("/poly/room.poly");
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
EdgeLengthFunctionApprox edgeLengthFunctionApprox = new EdgeLengthFunctionApprox(pslg, p -> 2.0);
edgeLengthFunctionApprox.printPython();
......@@ -182,7 +201,7 @@ public class EikMeshPlots {
}
//meshImprover.generate();
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, 1.0f)), "eikmesh_kaiserslautern_"+ Double.toString(h0).replace('.', '_'));
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f -> lightBlue, 1.0f)), "eikmesh_kaiserslautern_" + Double.toString(h0).replace('.', '_'));
// display the mesh
meshPanel.display("Combined distance functions " + h0);
......@@ -190,7 +209,7 @@ public class EikMeshPlots {
public static void cornerLFS() throws IOException, InterruptedException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream("/poly/corner.poly");
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
EdgeLengthFunctionApprox edgeLengthFunctionApprox = new EdgeLengthFunctionApprox(pslg, p -> 1.0);
edgeLengthFunctionApprox.printPython();
......@@ -217,7 +236,7 @@ public class EikMeshPlots {
}
//meshImprover.generate();
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, 1.0f)), "eikmesh_kaiserslautern_"+ Double.toString(h0).replace('.', '_'));
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f -> lightBlue, 1.0f)), "eikmesh_kaiserslautern_" + Double.toString(h0).replace('.', '_'));
// display the mesh
meshPanel.display("Combined distance functions " + h0);
......@@ -225,11 +244,11 @@ public class EikMeshPlots {
public static void eikMeshA(double h0) throws IOException, InterruptedException {
final InputStream inputStream = MeshExamples.class.getResourceAsStream("/poly/a.poly");
PSLG pslg = PSLGGenerator.toPSLGtoVShapes(inputStream);
PSLG pslg = PSLGGenerator.toPSLG(inputStream);
PEikMesh meshImprover = new PEikMesh(pslg.getSegmentBound(), h0, pslg.getHoles());
meshImprover.generate();
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f-> lightBlue, 10.0f)), "eikmesh_a_"+ Double.toString(h0).replace('.', '_'));
write(toTexDocument(TexGraphGenerator.toTikz(meshImprover.getMesh(), f -> lightBlue, 10.0f)), "eikmesh_a_" + Double.toString(h0).replace('.', '_'));
// display the mesh
var meshPanel = new PMeshPanel(meshImprover.getMesh(), 1000, 1000);
......@@ -239,13 +258,13 @@ public class EikMeshPlots {
public static void distanceFuncCombination(double h0) throws IOException {
// define your holes
VRectangle rect = new VRectangle(-0.5, -0.5, 1, 1);
VRectangle boundary = new VRectangle(-1.5,-0.7,3,1.4);
VRectangle boundary = new VRectangle(-1.5, -0.7, 3, 1.4);
IDistanceFunction d1_c = IDistanceFunction.createDisc(-0.5, 0, 0.5);
IDistanceFunction d2_c = IDistanceFunction.createDisc(0.5, 0, 0.5);
IDistanceFunction d_r = IDistanceFunction.create(rect);
IDistanceFunction d_b = IDistanceFunction.create(boundary);
IDistanceFunction d_union = IDistanceFunction.union(IDistanceFunction.union(d1_c, d_r), d2_c);
IDistanceFunction d = IDistanceFunction.substract(d_b,d_union);
IDistanceFunction d = IDistanceFunction.substract(d_b, d_union);