1 package org.opentrafficsim.base.geometry;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6
7 import org.djutils.draw.line.Polygon2d;
8 import org.djutils.draw.point.Point2d;
9 import org.djutils.exceptions.Throw;
10
11
12
13
14
15
16
17
18
19 public class BoundingBoxRounded implements OtsBounds2d
20 {
21
22
23 private final static int POLYGON_STEPS = 128;
24
25
26 private final double dx;
27
28
29 private final double dy;
30
31
32 private Polygon2d polygon;
33
34
35 private final double r;
36
37
38 private final double maxX;
39
40
41 private final double maxY;
42
43
44
45
46
47
48
49 public BoundingBoxRounded(final double dx, final double dy, final double r)
50 {
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 this.dx = Math.abs(dx) / 2.0;
67 this.dy = Math.abs(dy) / 2.0;
68 Throw.when(r >= this.dx + this.dy + Math.sqrt(2.0 * this.dx * this.dy), IllegalArgumentException.class,
69 "Radius makes rounded rectangle non-existent.");
70 Throw.when(r < 0.0, IllegalArgumentException.class, "Radius must be positive.");
71 this.r = r;
72 this.maxX = this.dx - signedDistance(new Point2d(this.dx, 0.0));
73 this.maxY = this.dy - signedDistance(new Point2d(0.0, this.dy));
74 }
75
76
77 @Override
78 public double getMinX()
79 {
80 return -this.maxX;
81 }
82
83
84 @Override
85 public double getMaxX()
86 {
87 return this.maxX;
88 }
89
90
91 @Override
92 public double getMinY()
93 {
94 return -this.maxY;
95 }
96
97
98 @Override
99 public double getMaxY()
100 {
101 return this.maxY;
102 }
103
104
105 @Override
106 public boolean contains(final Point2d point) throws NullPointerException
107 {
108 return signedDistance(point) < 0.0;
109 }
110
111
112 @Override
113 public boolean covers(final Point2d point) throws NullPointerException
114 {
115 return signedDistance(point) <= 0.0;
116 }
117
118
119
120
121
122 @Override
123 public double signedDistance(final Point2d point)
124 {
125 double qx = Math.abs(point.x) - this.dx + this.r;
126 double qy = Math.abs(point.y) - this.dy + this.r;
127 return Math.hypot(Math.max(qx, 0.0), Math.max(qy, 0.0)) + Math.min(Math.max(qx, qy), 0.0) - this.r;
128 }
129
130
131 @Override
132 public Polygon2d asPolygon()
133 {
134 if (this.polygon == null)
135 {
136
137 int n = POLYGON_STEPS / 4;
138 List<Point2d> pq = new ArrayList<>();
139 for (int i = 0; i <= n; i++)
140 {
141 double ang = (0.5 * Math.PI * i) / n;
142 double x = this.dx + Math.cos(ang) * this.r - this.r;
143 double y = this.dy + Math.sin(ang) * this.r - this.r;
144 if (x >= 0.0 && y >= 0.0)
145 {
146 pq.add(new Point2d(x, y));
147 }
148 }
149
150 List<Point2d> pqReversed = new ArrayList<>(pq);
151 Collections.reverse(pqReversed);
152 List<Point2d> points = new ArrayList<>(pq);
153 pqReversed.forEach((p) -> points.add(new Point2d(-p.x, p.y)));
154 pq.forEach((p) -> points.add(p.neg()));
155 pqReversed.forEach((p) -> points.add(new Point2d(p.x, -p.y)));
156 this.polygon = new Polygon2d(true, points);
157 }
158 return this.polygon;
159 }
160
161 }