def calc_bbox(self) -> Tuple[svg.Point, svg.Point]: '''Calculate bounding box of self''' self.bbox = ( svg.Point( min(self.points, key=lambda v: v.x).x, min(self.points, key=lambda v: v.y).y), svg.Point( max(self.points, key=lambda v: v.x).x, max(self.points, key=lambda v: v.y).y), )
def process(self, transformer, flip, fill): points = [] for point in self.points: point = transformer.transform_point(point, flip) if (len(points) < 1 or point.x != points[-1].x or point.y != points[-1].y): points.append(point) if (points[0].x != points[-1].x or points[0].y != points[-1].y): #print( "Warning: Closing polygon. start=({}, {}) end=({}, {})".format( #points[ 0 ].x, points[ 0 ].y, #points[ -1 ].x, points[ -1 ].y, #) ) if fill: points.append(svg.Point( points[0].x, points[0].y, )) #else: #print( "Polygon closed: start=({}, {}) end=({}, {})".format( #points[ 0 ].x, points[ 0 ].y, #points[ -1 ].x, points[ -1 ].y, #) ) self.points = points
def _calculate_translation(self): min_point, max_point = self.imported.svg.bbox() if (self.center): # Center the drawing: adjust_x = min_point.x + (max_point.x - min_point.x) / 2.0 adjust_y = min_point.y + (max_point.y - min_point.y) / 2.0 self.translation = svg.Point( 0.0 - adjust_x, 0.0 - adjust_y, ) else: self.translation = svg.Point( 0.0, 0.0, )
def vertical_intersection(p: svg.Point, q: svg.Point, r: float) -> svg.Point: '''This is used for the in-lining algorithm it finds a point on a line p -> q where x = r ''' if p.x == q.x: return min([p, q], key=lambda v: v.y) if r == p.x: return p if r == q.x: return q return svg.Point(r, (p.y - q.y) * (r - q.x) / (p.x - q.x) + q.y)
def points_starting_on_index(self, index): points = self.points if index > 0: # Strip off end point, which is a duplicate of the start point: points = points[:-1] points = points[index:] + points[:index] points.append(svg.Point(points[0].x, points[0].y)) return points
def inline( self, segments ): if len( segments ) < 1: return self.points print( " Inlining {} segments...".format( len( segments ) ) ) all_segments = segments[ : ] + [ self ] insertions = [] # Find the insertion point for each hole: for hole in segments: insertion = self._find_insertion_point( hole, all_segments ) if insertion is not None: insertions.append( insertion ) insertions.sort( key = lambda i: i[ 0 ] ) inlined = [ self.points[ 0 ] ] ip = 1 points = self.points for insertion in insertions: while ip <= insertion[ 0 ]: inlined.append( points[ ip ] ) ip += 1 if ( inlined[ -1 ].x == insertion[ 1 ][ 0 ].x and inlined[ -1 ].y == insertion[ 1 ][ 0 ].y ): inlined += insertion[ 1 ][ 1 : -1 ] else: inlined += insertion[ 1 ] inlined.append( svg.Point( points[ ip - 1 ].x, points[ ip - 1 ].y, ) ) while ip < len( points ): inlined.append( points[ ip ] ) ip += 1 return inlined
def points_starting_on_index(self, index: int) -> List[svg.Point]: ''' Return the list of ordered points starting on the given index, ensuring that the first and last points are the same. ''' points = self.points if index > 0: # Strip off end point, which is a duplicate of the start point: points = points[:-1] points = points[index:] + points[:index] points.append(svg.Point(points[0].x, points[0].y)) return points
def _find_insertion_point(self, hole: 'PolygonSegment', holes: list, other_insertions: list): ''' KiCad will not "pick up the pen" when moving between a polygon outline and holes within it, so we search for a pair of points connecting the outline (self) or other previously inserted points to the hole such that the connecting segment will not cross the visible inner space within any hole. ''' highest_point = max(hole.points, key=lambda v: v.y) vertical_line = LineSegment( highest_point, svg.Point(highest_point.x, self.bbox[1].y + 1)) intersections = { self: self.intersects(vertical_line, False, count_intersections=True, get_points=True) } for _, h, __ in other_insertions: if h.bbox[0].x < highest_point.x and h.bbox[1].x > highest_point.x: intersections[h] = h.intersects(vertical_line, False, count_intersections=True, get_points=True) best = [self, intersections[self][0]] best.append( LineSegment.vertical_intersection(best[1][0], best[1][1], highest_point.x)) for path in intersections: for p, q in intersections[path]: pnt = LineSegment.vertical_intersection(p, q, highest_point.x) if pnt.y < best[2].y: best = [path, (p, q), pnt] if best[2] != best[1][0] and best[2] != best[1][1]: p = best[0].points.index(best[1][0]) q = best[0].points.index(best[1][1]) ip = p if p < q else q best[0]._set_points(best[0].points[:ip + 1] + [best[2]] + best[0].points[ip + 1:]) return (best[2], hole, highest_point)
def transform_point(self, point, flip=False): transformed_point = svg.Point( (point.x + self.translation.x) * self.scale_factor, (point.y + self.translation.y) * self.scale_factor, ) if flip: transformed_point.x *= -1 if self.use_mm: transformed_point.x = round(transformed_point.x, 12) transformed_point.y = round(transformed_point.y, 12) else: transformed_point.x = int(round(transformed_point.x)) transformed_point.y = int(round(transformed_point.y)) return transformed_point
def transform_point( self, point, flip = False ): ''' Transform provided point by this classes scale factor. ''' transformed_point = svg.Point( ( point.x + self.translation.x ) * self.scale_factor, ( point.y + self.translation.y ) * self.scale_factor, ) if flip: transformed_point.x *= -1 if self.use_mm: transformed_point.x = transformed_point.x transformed_point.y = transformed_point.y else: transformed_point.x = int( round( transformed_point.x ) ) transformed_point.y = int( round( transformed_point.y ) ) return transformed_point
def are_distinct(self, polygon): ''' Checks if the supplied polygon either contains or insets our bounding box''' distinct = True smaller = min([self, polygon], key=lambda p: svg.Segment(p.bbox[0], p.bbox[1]).length()) larger = self if smaller == polygon else polygon if (larger.bbox[0].x < smaller.bbox[0].x and larger.bbox[0].y < smaller.bbox[0].y and larger.bbox[1].x > smaller.bbox[1].x and larger.bbox[1].y > smaller.bbox[1].y): distinct = False # Check number of horizontal intersections. If the number is odd then it the smaller polygon # is contained. If the number is even then the polygon is outside of the larger polygon if not distinct: tline = LineSegment( smaller.points[0], svg.Point(larger.bbox[1].x + 1, smaller.points[0].y)) distinct = bool((larger.intersects(tline, False, True) + 1) % 2) return distinct
def _write_wirepad( self ): root = (self.imported.svg.root) pad_string = "" count = 0 pad_template = """ (module Wire_Pads:SolderWirePad_single_0-8mmDrill (layer F.Cu) (tedit 0) (tstamp 5ABD66D0) (at %f %f) (pad %d thru_hole circle (at 0 0) (size 1.99898 1.99898) (drill 0.8001) (layers *.Cu *.Mask)) ) """ items = self.imported.svg.items for item in items: # print item.name if (item.name == "Drill"): # item.transform() for drill in item.items: count = count + 1 # print drill.matrix.vect old_center = drill.center # drill.transform(item.matrix) new_center = drill.center # transx = ((new_center.x - old_center.x) * 2 ) / (96/25.4) # transy = ((new_center.y - old_center.y) * 2 ) / (96/25.4) test = svg.Point(drill.center.x*1.0666794869689005,drill.center.y*1.0666794869689005) new_pad = self.transform_point(test) pad_x = new_pad.x pad_y = new_pad.y pad_string = pad_string + pad_template % (pad_x, pad_y, count) self.output_file.write(pad_string)
def process(self, transformer, flip, fill): ''' Apply all transformations, then remove duplicate consecutive points along the path. ''' points = [] for point in self.points: point = transformer.transform_point(point, flip) if (len(points) < 1 or point.x != points[-1].x or point.y != points[-1].y): points.append(point) if (points[0].x != points[-1].x or points[0].y != points[-1].y): if fill: points.append(svg.Point( points[0].x, points[0].y, )) self.points = points self.calc_bbox()
def renderLabel(self, inString): # t is an svg Text element t = svg.Text() t.set_font(self.fontName) for i,s in enumerate(inString.split('\n')): t.add_text(s, origin=svg.Point(0, 15*i)) # This needs to be called to convert raw text to useable path elements t.convert_to_path() # bounds check padding padding = self.padding for attr in ['left', 'right', 'top', 'bottom']: if getattr(padding,attr) <= 0: setattr(padding, attr, 0.001) bbox = t.bbox() height = bbox[1].y - bbox[0].y height += padding.top + padding.bottom width = bbox[1].x - bbox[0].x textWidth = (width + (self.padding.left + self.padding.right)) fixedWidth = max(0, self.width - (self.padding.left + self.padding.right)) width = max(width, fixedWidth) alignmentOffset = 0 if self.alignment == 'Left' or fixedWidth != width: alignmentOffset = 0 elif self.alignment == 'Center': alignmentOffset = (self.width - textWidth)/2 elif self.alignment == 'Right': alignmentOffset = (self.width - textWidth) # Create outline around text if (self.leftCap != '') & (self.rightCap != ''): pstr = "M {},{} ".format(bbox[0].x - alignmentOffset, bbox[0].y-padding.top) pstr += "h {} ".format(-padding.left) if self.leftCap == 'round': pstr += "a {},{} 0 0 0 0,{} ".format(height/2,height/2,height) if self.leftCap == 'square': pstr += "v {} ".format(height) if self.leftCap == 'fslash': pstr += "l {},{} h {} ".format(-0.3*height, height, 0.3*height) if self.leftCap == 'bslash': pstr += "h {} l {},{} ".format(-0.3*height, 0.3*height, height) if self.leftCap == 'pointer': pstr += "l {},{} l {},{} ".format(height/-3, height/2, height/3, height/2) if self.leftCap == 'flagtail': pstr += "h {} l {},{} l {},{} h {}".format(height/-3, height/3, height/2, height/-3, height/2, height/3) pstr += "h {} ".format(padding.left) pstr += "h {} ".format(width) pstr += "h {} ".format(padding.right) if self.rightCap == 'round': pstr += "a {},{} 0 0 0 0,{} ".format(height/2, height/2,-height) if self.rightCap == 'square': pstr += "v {} ".format(-height) if self.rightCap == 'fslash': pstr += "l {},{} h {} ".format(0.3*height, -height, -0.3*height) if self.rightCap == 'bslash': pstr += "h {} l {},{} ".format(0.3*height, -0.3*height, -height) if self.rightCap == 'pointer': pstr += "l {},{} l {},{} ".format(height/3, height/-2, height/-3, height/-2) if self.rightCap == 'flagtail': pstr += "h {} l {},{} l {},{} h {}".format(height/3, height/-3, height/-2, height/3, height/-2, height/-3) pstr += "h {} ".format(-padding.right) pstr += "h {} z".format(-1*width) p = svg.Path() p.parse(pstr) t.paths.append([p]) return t
def renderLabel(self, inString): # t is an svg Text element t = svg.Text() t.set_font(self.fontName) for i, s in enumerate(inString.split('\n')): t.add_text(s, origin=svg.Point(0, 15 * i)) # This needs to be called to convert raw text to useable path elements t.convert_to_path() bbox = t.bbox() height = bbox[1].y - bbox[0].y buff = t.size / 5 height += buff * 2 width = bbox[1].x - bbox[0].x # Create outline around text if (self.leftCap != '') & (self.rightCap != ''): pstr = "M {},{} ".format(bbox[0].x, bbox[0].y - buff) if self.leftCap == 'round': pstr += "a {},{} 0 0 0 0,{} ".format(height / 2, height / 2, height) if self.leftCap == 'square': pstr += "l {},0 l 0,{} l {},0 ".format(-buff, height, buff) if self.leftCap == 'fslash': pstr += "l {},0 l {},{} l {},0 ".format( -buff, -2 * buff, height, 3 * buff) if self.leftCap == 'bslash': pstr += "l {},0 l {},{} l {},0 ".format( -3 * buff, 2 * buff, height, buff) if self.leftCap == 'pointer': pstr += "l {},{} l {},{} ".format(height / -2, height / 2, height / 2, height / 2) if self.leftCap == 'flagtail': pstr += "l {},0 l {},{} l {},{} l {},0".format( -1 * buff - height / 2, height / 2, height / 2, height / -2, height / 2, buff + height / 2) pstr += "h {} ".format(width) if self.rightCap == 'round': pstr += "a {},{} 0 0 0 0,{} ".format(height / 2, height / 2, -height) if self.rightCap == 'square': pstr += "l {},0 l 0,{} l {},0 ".format(buff, -height, -buff) if self.rightCap == 'fslash': pstr += "l {},0 l {},{} l {},0 ".format( buff, 2 * buff, -height, -3 * buff) if self.rightCap == 'bslash': pstr += "l {},0 l {},{} l {},0 ".format( 3 * buff, -2 * buff, -height, -buff) if self.rightCap == 'pointer': pstr += "l {},{} l {},{} ".format(height / 2, height / -2, height / -2, height / -2) if self.rightCap == 'flagtail': pstr += "l {},0 l {},{} l {},{} l {},0 ".format( 1 * buff + height / 2, height / -2, height / -2, height / 2, height / -2, -buff - height / 2) pstr += "h {} z".format(-1 * width) p = svg.Path() p.parse(pstr) t.paths.append([p]) return t