def _offset_loops_to_polygons(offset_loops): model = pycam.Geometry.Model.ContourModel() before = None for n_loop, loop in enumerate(offset_loops): lines = [] _log.info("loop #%d has %d lines/arcs", n_loop, len(loop)) for n_segment, item in enumerate(loop): _log.info("%d -> %s", n_segment, item) point, radius = item[:2] point = (point.x, point.y, 0.0) if before is not None: if radius == -1: lines.append(Line(before, point)) _log.info("%d line %s to %s", n_segment, before, point) else: _log.info("%d arc %s to %s r=%f", n_segment, before, point, radius) center, clock_wise = item[2:4] center = (center.x, center.y, 0.0) direction_before = (before[0] - center[0], before[1] - center[1], 0.0) direction_end = (point[0] - center[0], point[1] - center[1], 0.0) angles = [ 180.0 * get_angle_pi((1.0, 0.0, 0.0), (0, 0.0, 0.0), direction, (0.0, 0.0, 1.0), pi_factor=True) for direction in (direction_before, direction_end) ] if clock_wise: angles.reverse() points = get_points_of_arc(center, radius, angles[0], angles[1]) last_p = before for p in points: lines.append(Line(last_p, p)) last_p = p before = point for line in lines: if line.len > epsilon: model.append(line) return model.get_polygons()
def get_spiral_layer(minx, maxx, miny, maxy, z, line_distance, step_width, grid_direction, start_position, rounded_corners, reverse): current_location = _get_position(minx, maxx, miny, maxy, z, start_position) if line_distance > 0: line_steps_x = math.ceil((float(maxx - minx) / line_distance)) line_steps_y = math.ceil((float(maxy - miny) / line_distance)) line_distance_x = (maxx - minx) / line_steps_x line_distance_y = (maxy - miny) / line_steps_y lines = get_spiral_layer_lines(minx, maxx, miny, maxy, z, line_distance_x, line_distance_y, grid_direction, start_position, current_location) if reverse: lines.reverse() # turn the lines into steps if rounded_corners: rounded_lines = [] previous = None for index, (start, end) in enumerate(lines): radius = 0.5 * min(line_distance_x, line_distance_y) edge_vector = psub(end, start) # TODO: ellipse would be better than arc offset = pmul(pnormalized(edge_vector), radius) if previous: start = padd(start, offset) center = padd(previous, offset) up_vector = pnormalized( pcross(psub(previous, center), psub(start, center))) north = padd(center, (1.0, 0.0, 0.0, 'v')) angle_start = get_angle_pi( north, center, previous, up_vector, pi_factor=True) * 180.0 angle_end = get_angle_pi( north, center, start, up_vector, pi_factor=True) * 180.0 # TODO: remove these exceptions based on up_vector.z (get_points_of_arc does # not respect the plane, yet) if up_vector[2] < 0: angle_start, angle_end = -angle_end, -angle_start arc_points = get_points_of_arc(center, radius, angle_start, angle_end) if up_vector[2] < 0: arc_points.reverse() for arc_index in range(len(arc_points) - 1): p1_coord = arc_points[arc_index] p2_coord = arc_points[arc_index + 1] p1 = (p1_coord[0], p1_coord[1], z) p2 = (p2_coord[0], p2_coord[1], z) rounded_lines.append((p1, p2)) if index != len(lines) - 1: end = psub(end, offset) previous = end rounded_lines.append((start, end)) lines = rounded_lines for start, end in lines: points = [] if step_width is None: points.append(start) points.append(end) else: line = Line(start, end) if isiterable(step_width): steps = step_width else: steps = floatrange(0.0, line.len, inc=step_width) for step in steps: next_point = padd(line.p1, pmul(line.dir, step)) points.append(next_point) if reverse: points.reverse() yield points
def parse_arc(self, circle=False): start_line = self.line_number # the z-level defaults to zero (for 2D models) center = [None, None, 0] color = None radius = None if circle: angle_start = 0 angle_end = 360 else: angle_start = None angle_end = None key, value = self._read_key_value() while (key is not None) and (key != self.KEYS["MARKER"]): if key == self.KEYS["P1_X"]: center[0] = value elif key == self.KEYS["P1_Y"]: center[1] = value elif key == self.KEYS["P1_Z"]: center[2] = value elif key == self.KEYS["RADIUS"]: radius = value elif key == self.KEYS["ANGLE_START"]: angle_start = value elif key == self.KEYS["ANGLE_END"]: angle_end = value elif key == self.KEYS["COLOR"]: color = value else: pass key, value = self._read_key_value() end_line = self.line_number # The last lines were not used - they are just the marker for the next item. if key is not None: self._push_on_stack(key, value) if (None in center) or (None in (radius, angle_start, angle_end)): log.warn( "DXFImporter: Incomplete ARC definition between line %d and %d", start_line, end_line) else: if self._color_as_height and (color is not None): # use the color code as the z coordinate center[2] = float(color) / 255 center = tuple(center) xy_point_coords = get_points_of_arc(center, radius, angle_start, angle_end) # Somehow the order of points seems to be the opposite of what is # expected. xy_point_coords.reverse() if len(xy_point_coords) > 1: for index in range(len(xy_point_coords) - 1): p1 = xy_point_coords[index] p1 = (p1[0], p1[1], center[2]) p2 = xy_point_coords[index + 1] p2 = (p2[0], p2[1], center[2]) if p1 != p2: self.lines.append(Line(p1, p2)) else: log.warn( "DXFImporter: Ignoring tiny ARC (between input line %d and %d): %s / %s " "(%s - %s)", start_line, end_line, center, radius, angle_start, angle_end)
def __init__(self, stream, callback=None): self.letters = {} self.meta = {} self.callback = callback feeder = _LineFeeder(stream.readlines()) while not feeder.is_empty(): line = feeder.consume() if not line: # ignore pass elif line.startswith("#"): # comment or meta data content = line[1:].split(":", 1) if len(content) == 2: key = content[0].lower().strip() value = content[1].strip() if key in self.META_KEYWORDS: try: if key != "encoding": self.meta[key] = float(value) else: self.meta[key] = value except ValueError: raise _CXFParseError( "Invalid meta information in line %d" % feeder.get_index()) elif key in self.META_KEYWORDS_MULTI: if key in self.meta: self.meta[key].append(value) else: self.meta[key] = [value] else: # unknown -> ignore pass elif line.startswith("["): # Update the GUI from time to time. # This is useful for the big unicode font. if self.callback and (len(self.letters) % 50 == 0): self.callback() if (len(line) >= 3) and (line[2] == "]"): # single character for encoding in ("utf-8", "iso8859-1", "iso8859-15"): try: character = line[1].decode(encoding) break except UnicodeDecodeError: pass else: raise _CXFParseError( "Failed to decode character at line %d" % feeder.get_index()) elif (len(line) >= 6) and (line[5] == "]"): # unicode character (e.g. "[1ae4]") try: character = chr(int(line[1:5], 16)) except ValueError: raise _CXFParseError( "Failed to parse unicode character at line %d" % feeder.get_index()) elif (len(line) > 3) and (line.find("]") > 2): # read UTF8 (qcad 1 compatibility) end_bracket = line.find("] ") text = line[1:end_bracket] character = text.decode("utf-8", errors="ignore")[0] else: # unknown format raise _CXFParseError( "Failed to parse character at line %d" % feeder.get_index()) # parse the following lines up to the next empty line char_definition = [] while not feeder.is_empty() and (len(feeder.get()) > 0): line = feeder.consume() # split the line after the first whitespace type_def, coord_string = line.split(None, 1) coords = [ float(value) for value in coord_string.split(",") ] if (type_def == "L") and (len(coords) == 4): # line p1 = (coords[0], coords[1], 0) p2 = (coords[2], coords[3], 0) char_definition.append(Line(p1, p2)) elif (type_def in ("A", "AR")) and (len(coords) == 5): # arc previous = None center = (coords[0], coords[1], 0) radius = coords[2] start_angle, end_angle = coords[3], coords[4] if type_def == "AR": # reverse the arc start_angle, end_angle = end_angle, start_angle for p in get_points_of_arc(center, radius, start_angle, end_angle): current = (p[0], p[1], 0) if previous is not None: char_definition.append(Line(previous, current)) previous = current else: raise _CXFParseError( "Failed to read item coordinates in line %d" % feeder.get_index()) self.letters[character] = char_definition else: # unknown line format raise _CXFParseError( "Failed to parse unknown content in line %d" % feeder.get_index())