2Basic geometric data structures for layout processing.
6from typing
import List, Tuple, Optional, Union
17 """2D point with coordinates and utility methods."""
25 x_str = str(int(self.
x))
if self.
x == int(self.
x)
else str(self.
x)
26 y_str = str(int(self.
y))
if self.
y == int(self.
y)
else str(self.
y)
27 return f
"Point({x_str}, {y_str})"
30 if not isinstance(other, Point):
32 return abs(self.
x - other.x) < 1e-10
and abs(self.
y - other.y) < 1e-10
35 return hash((round(self.
x, 10), round(self.
y, 10)))
38 """Calculate Euclidean distance to another point."""
39 return math.sqrt((self.
x - other.x)**2 + (self.
y - other.y)**2)
42 """Calculate distance from this point to a line segment."""
44 line_vec =
Point(line_end.x - line_start.x, line_end.y - line_start.y)
45 line_length_sq = line_vec.x**2 + line_vec.y**2
47 if line_length_sq < 1e-10:
51 point_vec =
Point(self.
x - line_start.x, self.
y - line_start.y)
54 t = max(0, min(1, (point_vec.x * line_vec.x + point_vec.y * line_vec.y) / line_length_sq))
57 closest =
Point(line_start.x + t * line_vec.x, line_start.y + t * line_vec.y)
62 """Convert to numpy array (if numpy is available)."""
64 return np.array([self.
x, self.
y])
66 return [self.
x, self.
y]
70 """Axis-aligned rectangle for bounding boxes and simple components."""
72 def __init__(self, x: float, y: float, width: float, height: float):
79 return f
"Rectangle({self.x}, {self.y}, {self.width}, {self.height})"
90 def top(self) -> float:
102 """Check if point is inside rectangle."""
103 return (self.
left <= point.x <= self.
right and
107 """Check if this rectangle intersects with another."""
108 return not (self.
right < other.left
or other.right < self.
left or
112 """Convert rectangle to polygon."""
123 """Polygon class supporting both convex and concave polygons."""
126 if len(vertices) < 3:
127 raise ValueError(
"Polygon must have at least 3 vertices")
131 return f
"Polygon({len(self.vertices)} vertices)"
134 def edges(self) -> List[Tuple[Point, Point]]:
135 """Get all edges as (start, end) point pairs."""
143 """Calculate axis-aligned bounding box."""
145 raise ValueError(
"Cannot calculate bounding box of empty polygon")
147 min_x = min(v.x
for v
in self.
vertices)
148 max_x = max(v.x
for v
in self.
vertices)
149 min_y = min(v.y
for v
in self.
vertices)
150 max_y = max(v.y
for v
in self.
vertices)
152 return Rectangle(min_x, min_y, max_x - min_x, max_y - min_y)
155 """Calculate polygon area using shoelace formula."""
165 return abs(area) / 2.0
168 """Check if polygon is convex."""
172 def cross_product(o: Point, a: Point, b: Point) -> float:
173 return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
188 elif (cp > 0) != sign:
194 """Check if point is inside polygon using ray casting."""
195 x, y = point.x, point.y
210 for i
in range(1, n + 1):
212 if y > min(p1y, p2y):
213 if y <= max(p1y, p2y):
214 if x <= max(p1x, p2x):
216 xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
217 if p1x == p2x
or x <= xinters:
223 def _point_on_edge(self, point: Point, edge_start: Point, edge_end: Point) -> bool:
224 """Check if a point lies on an edge."""
226 distance = point.distance_to_line(edge_start, edge_end)
227 return distance < 1e-10
230 """Find vertices with sharp angles or boundary angles.
232 For traditional sharp angle detection (small thresholds < 60°):
233 finds acute angles smaller than threshold.
234 For boundary angle detection (large thresholds ≥ 60°):
235 finds angles larger than threshold.
237 def calculate_interior_angle(prev_pt: Point, curr_pt: Point, next_pt: Point) -> float:
238 """Calculate the interior angle at curr_pt."""
240 v1 =
Point(prev_pt.x - curr_pt.x, prev_pt.y - curr_pt.y)
241 v2 =
Point(next_pt.x - curr_pt.x, next_pt.y - curr_pt.y)
244 angle1 = math.atan2(v1.y, v1.x)
245 angle2 = math.atan2(v2.y, v2.x)
248 angle_diff = angle2 - angle1
251 while angle_diff < 0:
252 angle_diff += 2 * math.pi
253 while angle_diff > 2 * math.pi:
254 angle_diff -= 2 * math.pi
257 interior_angle = math.degrees(angle_diff)
261 if interior_angle > 180:
262 interior_angle = 360 - interior_angle
264 return interior_angle
270 prev_vertex = self.
vertices[(i - 1) % n]
272 next_vertex = self.
vertices[(i + 1) % n]
274 interior_angle = calculate_interior_angle(prev_vertex, curr_vertex, next_vertex)
281 is_traditional_sharp = interior_angle < threshold_degrees * 0.8
282 is_boundary_angle = threshold_degrees < interior_angle < threshold_degrees + tolerance
284 if is_traditional_sharp
or is_boundary_angle:
285 sharp_angles.append(i)
2D point with high-precision coordinates and utility methods
double distance_to_line(const Point &line_start, const Point &line_end) const
Calculate distance from this point to a line segment.
double distance_to(const Point &other) const
Calculate Euclidean distance to another point.
__init__(self, float x, float y)
Polygon class supporting both convex and concave polygons.
std::vector< std::pair< Point, Point > > edges() const
Get polygon edges as pairs of consecutive vertices.
Rectangle bounding_box() const
Calculate axis-aligned bounding box.
std::vector< size_t > get_sharp_angles(double threshold_degrees=30.0) const
Find vertices with sharp angles.
std::vector< Point > vertices
Polygon vertices in order.
bool _point_on_edge(self, Point point, Point edge_start, Point edge_end)
bool is_convex() const
Check if polygon is convex.
__init__(self, List[Point] vertices)
bool contains_point(const Point &point) const
Check if point is inside polygon (ray casting algorithm)
double area() const
Calculate polygon area using shoelace formula.
Axis-aligned rectangle for bounding boxes and simple EDA components.
double top() const
Get top edge Y coordinate.
double left() const
Get left edge X coordinate.
double y
Y coordinate of bottom-left corner.
Polygon to_polygon() const
Convert rectangle to polygon representation.
Point center() const
Get center point of rectangle.
__init__(self, float x, float y, float width, float height)
double height
Height of rectangle.
double right() const
Get right edge X coordinate.
bool intersects(const Rectangle &other) const
Check if this rectangle intersects with another rectangle.
bool contains_point(const Point &point) const
Check if point is inside rectangle (inclusive of boundary)
double x
X coordinate of bottom-left corner.
double width
Width of rectangle.