summaryrefslogtreecommitdiff
path: root/gerberex/dxf_path.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerberex/dxf_path.py')
-rw-r--r--gerberex/dxf_path.py412
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