aboutsummaryrefslogtreecommitdiff
path: root/gerberex/dxf_path.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerberex/dxf_path.py')
-rw-r--r--gerberex/dxf_path.py125
1 files changed, 124 insertions, 1 deletions
diff --git a/gerberex/dxf_path.py b/gerberex/dxf_path.py
index 1307411..bb620ff 100644
--- a/gerberex/dxf_path.py
+++ b/gerberex/dxf_path.py
@@ -5,13 +5,17 @@
from gerber.utils import inch, metric, write_gerber_value
from gerber.cam import FileSettings
-from gerberex.utility import is_equal_point, is_equal_value
+from gerberex.utility import is_equal_point, is_equal_value, normalize_vec2d, dot_vec2d
from gerberex.excellon import CoordinateStmtEx
class DxfPath(object):
def __init__(self, statements, error_range=0):
self.statements = statements
self.error_range = error_range
+ self.bounding_box = statements[0].bounding_box
+ self.containers = []
+ for statement in statements[1:]:
+ self._merge_bounding_box(statement.bounding_box)
@property
def start(self):
@@ -116,12 +120,15 @@ class DxfPath(object):
if j > 0:
del mergee[-j]
del self.statements[0:j]
+ for statement in mergee:
+ self._merge_bounding_box(statement.bounding_box)
self.statements.extend(mergee)
return True
else:
if self.statements[-1].is_equal_to(element, error_range) or \
self.statements[0].is_equal_to(element, error_range):
return False
+ self._merge_bounding_box(element.bounding_box)
self.statements.appen(element)
return True
@@ -153,6 +160,21 @@ class DxfPath(object):
self.statements.insert(0, element)
return True
+ def _merge_bounding_box(self, box):
+ self.bounding_box = (min(self.bounding_box[0], box[0]),
+ min(self.bounding_box[1], box[1]),
+ max(self.bounding_box[2], box[2]),
+ max(self.bounding_box[3], box[3]))
+
+ def may_be_in_collision(self, path):
+ if self.bounding_box[0] >= path.bounding_box[2] or \
+ self.bounding_box[1] >= path.bounding_box[3] or \
+ self.bounding_box[2] <= path.bounding_box[0] or \
+ self.bounding_box[3] <= path.bounding_box[1]:
+ return False
+ else:
+ return True
+
def to_gerber(self, settings=FileSettings(), pitch=0, width=0):
from gerberex.dxf import DxfArcStatement
if pitch == 0:
@@ -244,7 +266,61 @@ class DxfPath(object):
out += ploter(dot[0], dot[1])
return out
+ def intersections_with_halfline(self, point_from, point_to, error_range=0):
+ def calculator(statement):
+ return statement.intersections_with_halfline(point_from, point_to, error_range)
+ def validator(pt, statement, idx):
+ if is_equal_point(pt, statement.end, error_range) and \
+ not self._judge_cross(point_from, point_to, idx, error_range):
+ return False
+ return True
+ return self._collect_intersections(calculator, validator, error_range)
+
+ def intersections_with_arc(self, center, radius, angle_regions, error_range=0):
+ def calculator(statement):
+ return statement.intersections_with_arc(center, radius, angle_regions, error_range)
+ return self._collect_intersections(calculator, None, error_range)
+ def _collect_intersections(self, calculator, validator, error_range):
+ allpts = []
+ last = allpts
+ for i in range(0, len(self.statements)):
+ statement = self.statements[i]
+ cur = calculator(statement)
+ if cur:
+ for pt in cur:
+ for dest in allpts:
+ if is_equal_point(pt, dest, error_range):
+ break
+ else:
+ if validator is not None and not validator(pt, statement, i):
+ continue
+ allpts.append(pt)
+ last = cur
+ return allpts
+
+ def _judge_cross(self, from_pt, to_pt, index, error_range):
+ standard = normalize_vec2d((to_pt[0] - from_pt[0], to_pt[1] - from_pt[1]))
+ normal = (standard[1], standard[0])
+ def statements():
+ for i in range(index, len(self.statements)):
+ yield self.statements[i]
+ for i in range(0, index):
+ yield self.statements[i]
+ dot_standard = None
+ for statement in statements():
+ tstart = statement.start
+ tend = statement.end
+ target = normalize_vec2d((tend[0] - tstart[0], tend[1] - tstart[1]))
+ dot= dot_vec2d(normal, target)
+ if dot_standard is None:
+ dot_standard = dot
+ continue
+ if is_equal_point(standard, target, error_range):
+ continue
+ return (dot_standard > 0 and dot > 0) or (dot_standard < 0 and dot < 0)
+ raise Exception('inconsistensy is detected while cross judgement between paths')
+
def generate_paths(statements, error_range=0):
from gerberex.dxf import DxfPolylineStatement
@@ -287,3 +363,50 @@ def generate_paths(statements, error_range=0):
closed_path = list(filter(lambda p: p.is_closed, paths))
open_path = list(filter(lambda p: not p.is_closed, paths))
return (closed_path, open_path)
+
+def judge_containment(path1, path2, error_range=0):
+ from gerberex.dxf import DxfArcStatement, DxfLineStatement
+
+ nocontainment = (None, None)
+ if not path1.may_be_in_collision(path2):
+ return nocontainment
+
+ def is_in_line_segment(point_from, point_to, point):
+ dx = point_to[0] - point_from[0]
+ ratio = (point[0] - point_from[0]) / dx if dx != 0 else \
+ (point[1] - point_from[1]) / (point_to[1] - point_from[1])
+ return ratio >= 0 and ratio <= 1
+
+ def contain_in_path(statement, path):
+ if isinstance(statement, DxfLineStatement):
+ segment = (statement.start, statement.end)
+ elif isinstance(statement, DxfArcStatement):
+ if statement.start == statement.end:
+ segment = (statement.start, statement.center)
+ else:
+ segment = (statement.start, statement.end)
+ else:
+ raise Exception('invalid dxf statement type')
+ pts = path.intersections_with_halfline(segment[0], segment[1], error_range)
+ if len(pts) % 2 == 0:
+ return False
+ for pt in pts:
+ if is_in_line_segment(segment[0], segment[1], pt):
+ return False
+ if isinstance(statement, DxfArcStatement):
+ pts = path.intersections_with_arc(
+ statement.center, statement.radius, statement.angle_regions, error_range)
+ if len(pts) > 0:
+ return False
+ return True
+
+ if contain_in_path(path1.statements[0], path2):
+ containment = [path1, path2]
+ elif contain_in_path(path2.statements[0], path1):
+ containment = [path2, path1]
+ else:
+ return nocontainment
+ for i in range(1, len(containment[0].statements)):
+ if not contain_in_path(containment[0].statements[i], containment[1]):
+ return nocontainment
+ return containment