diff options
Diffstat (limited to 'gerberex/dxf_path.py')
-rw-r--r-- | gerberex/dxf_path.py | 412 |
1 files changed, 0 insertions, 412 deletions
diff --git a/gerberex/dxf_path.py b/gerberex/dxf_path.py deleted file mode 100644 index 960b054..0000000 --- a/gerberex/dxf_path.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com> - -from gerber.utils import inch, metric, write_gerber_value -from gerber.cam import FileSettings -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): - return self.statements[0].start - - @property - def end(self): - return self.statements[-1].end - - @property - def is_closed(self): - if len(self.statements) == 1: - return self.statements[0].is_closed - else: - return is_equal_point(self.start, self.end, self.error_range) - - def is_equal_to(self, target, error_range=0): - if not isinstance(target, DxfPath): - return False - if len(self.statements) != len(target.statements): - return False - if is_equal_point(self.start, target.start, error_range) and \ - is_equal_point(self.end, target.end, error_range): - for i in range(0, len(self.statements)): - if not self.statements[i].is_equal_to(target.statements[i], error_range): - return False - return True - elif is_equal_point(self.start, target.end, error_range) and \ - is_equal_point(self.end, target.start, error_range): - for i in range(0, len(self.statements)): - if not self.statements[i].is_equal_to(target.statements[-1 - i], error_range): - return False - return True - return False - - def contain(self, target, error_range=0): - for statement in self.statements: - if statement.is_equal_to(target, error_range): - return True - else: - return False - - def to_inch(self): - self.error_range = inch(self.error_range) - for statement in self.statements: - statement.to_inch() - - def to_metric(self): - self.error_range = metric(self.error_range) - for statement in self.statements: - statement.to_metric() - - def offset(self, offset_x, offset_y): - for statement in self.statements: - statement.offset(offset_x, offset_y) - - def rotate(self, angle, center=(0, 0)): - for statement in self.statements: - statement.rotate(angle, center) - - def reverse(self): - rlist = [] - for statement in reversed(self.statements): - statement.reverse() - rlist.append(statement) - self.statements = rlist - - def merge(self, element, error_range=0): - if self.is_closed or element.is_closed: - return False - if not error_range: - error_range = self.error_range - if is_equal_point(self.end, element.start, error_range): - return self._append_at_end(element, error_range) - elif is_equal_point(self.end, element.end, error_range): - element.reverse() - return self._append_at_end(element, error_range) - elif is_equal_point(self.start, element.end, error_range): - return self._insert_on_top(element, error_range) - elif is_equal_point(self.start, element.start, error_range): - element.reverse() - return self._insert_on_top(element, error_range) - else: - return False - - def _append_at_end(self, element, error_range=0): - if isinstance(element, DxfPath): - if self.is_equal_to(element, error_range): - return False - for i in range(0, min(len(self.statements), len(element.statements))): - if not self.statements[-1 - i].is_equal_to(element.statements[i]): - break - for j in range(0, min(len(self.statements), len(element.statements))): - if not self.statements[j].is_equal_to(element.statements[-1 - j]): - break - if i + j >= len(element.statements): - return False - mergee = list(element.statements) - if i > 0: - del mergee[0:i] - del self.statements[-i] - 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 - - def _insert_on_top(self, element, error_range=0): - if isinstance(element, DxfPath): - if self.is_equal_to(element, error_range): - return False - for i in range(0, min(len(self.statements), len(element.statements))): - if not self.statements[-1 - i].is_equal_to(element.statements[i]): - break - for j in range(0, min(len(self.statements), len(element.statements))): - if not self.statements[j].is_equal_to(element.statements[-1 - j]): - break - if i + j >= len(element.statements): - return False - mergee = list(element.statements) - if i > 0: - del mergee[0:i] - del self.statements[-i] - if j > 0: - del mergee[-j] - del self.statements[0:j] - self.statements[0:0] = 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.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: - x0, y0 = self.statements[0].start - gerber = 'G01*\nX{0}Y{1}D02*\nG75*'.format( - write_gerber_value(x0, settings.format, - settings.zero_suppression), - write_gerber_value(y0, settings.format, - settings.zero_suppression), - ) - - for statement in self.statements: - x0, y0 = statement.start - x1, y1 = statement.end - if isinstance(statement, DxfArcStatement): - xc, yc = statement.center - gerber += '\nG{0}*\nX{1}Y{2}I{3}J{4}D01*'.format( - '03' if statement.end_angle > statement.start_angle else '02', - write_gerber_value(x1, settings.format, - settings.zero_suppression), - write_gerber_value(y1, settings.format, - settings.zero_suppression), - write_gerber_value(xc - x0, settings.format, - settings.zero_suppression), - write_gerber_value(yc - y0, settings.format, - settings.zero_suppression) - ) - else: - gerber += '\nG01*\nX{0}Y{1}D01*'.format( - write_gerber_value(x1, settings.format, - settings.zero_suppression), - write_gerber_value(y1, settings.format, - settings.zero_suppression), - ) - else: - def ploter(x, y): - return 'X{0}Y{1}D03*\n'.format( - write_gerber_value(x, settings.format, - settings.zero_suppression), - write_gerber_value(y, settings.format, - settings.zero_suppression), - ) - gerber = self._plot_dots(pitch, width, ploter) - - return gerber - - def to_excellon(self, settings=FileSettings(), pitch=0, width=0): - from gerberex.dxf import DxfArcStatement - if pitch == 0: - x0, y0 = self.statements[0].start - excellon = 'G00{0}\nM15\n'.format( - CoordinateStmtEx(x=x0, y=y0).to_excellon(settings)) - - for statement in self.statements: - x0, y0 = statement.start - x1, y1 = statement.end - if isinstance(statement, DxfArcStatement): - i = statement.center[0] - x0 - j = statement.center[1] - y0 - excellon += '{0}{1}\n'.format( - 'G03' if statement.end_angle > statement.start_angle else 'G02', - CoordinateStmtEx(x=x1, y=y1, i=i, j=j).to_excellon(settings)) - else: - excellon += 'G01{0}\n'.format( - CoordinateStmtEx(x=x1, y=y1).to_excellon(settings)) - - excellon += 'M16\nG05\n' - else: - def ploter(x, y): - return CoordinateStmtEx(x=x, y=y).to_excellon(settings) + '\n' - excellon = self._plot_dots(pitch, width, ploter) - - return excellon - - def _plot_dots(self, pitch, width, ploter): - out = '' - offset = 0 - for idx in range(0, len(self.statements)): - statement = self.statements[idx] - if offset < 0: - offset += pitch - for dot, offset in statement.dots(pitch, width, offset): - if dot is None: - break - if offset > 0 and (statement.is_closed or idx != len(self.statements) - 1): - break - #if idx == len(self.statements) - 1 and statement.is_closed and offset > -pitch: - # break - 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 - - paths = [] - for statement in filter(lambda s: isinstance(s, DxfPolylineStatement), statements): - units = [unit for unit in statement.disassemble()] - paths.append(DxfPath(units, error_range)) - - unique_statements = [] - redundant = 0 - for statement in filter(lambda s: not isinstance(s, DxfPolylineStatement), statements): - for path in paths: - if path.contain(statement): - redundant += 1 - break - else: - for target in unique_statements: - if statement.is_equal_to(target, error_range): - redundant += 1 - break - else: - unique_statements.append(statement) - - paths.extend([DxfPath([s], error_range) for s in unique_statements]) - - prev_paths_num = 0 - while prev_paths_num != len(paths): - working = [] - for i in range(len(paths)): - mergee = paths[i] - for j in range(i + 1, len(paths)): - target = paths[j] - if target.merge(mergee, error_range): - break - else: - working.append(mergee) - prev_paths_num = len(paths) - paths = working - - 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 |