# ########################################################## # FlatCAM: 2D Post-processing for Manufacturing # # File Author: Marius Adrian Stanciu (c) # # Date: 3/10/2019 # # MIT Licence # # ########################################################## from shapely.geometry import LineString from shapely.affinity import rotate from ezdxf.math.vector import Vector as ezdxf_vector from appParsers.ParseFont import * from appParsers.ParseDXF_Spline import * import logging log = logging.getLogger('base2') def distance(pt1, pt2): return math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) def dxfpoint2shapely(point): geo = Point(point.dxf.location).buffer(0.01) return geo def dxfline2shapely(line): try: start = (line.dxf.start[0], line.dxf.start[1]) stop = (line.dxf.end[0], line.dxf.end[1]) except Exception as e: log.debug(str(e)) return None geo = LineString([start, stop]) return geo def dxfcircle2shapely(circle, n_points=100): ocs = circle.ocs() # if the extrusion attribute is not (0, 0, 1) then we have to change the coordinate system from OCS to WCS if circle.dxf.extrusion != (0, 0, 1): center_pt = ocs.to_wcs(circle.dxf.center) else: center_pt = circle.dxf.center radius = circle.dxf.radius geo = Point(center_pt).buffer(radius, int(n_points / 4)) return geo def dxfarc2shapely(arc, n_points=100): # ocs = arc.ocs() # # if the extrusion attribute is not (0, 0, 1) then we have to change the coordinate system from OCS to WCS # if arc.dxf.extrusion != (0, 0, 1): # arc_center = ocs.to_wcs(arc.dxf.center) # start_angle = math.radians(arc.dxf.start_angle) + math.pi # end_angle = math.radians(arc.dxf.end_angle) + math.pi # dir = 'CW' # else: # arc_center = arc.dxf.center # start_angle = math.radians(arc.dxf.start_angle) # end_angle = math.radians(arc.dxf.end_angle) # dir = 'CCW' # # center_x = arc_center[0] # center_y = arc_center[1] # radius = arc.dxf.radius # # point_list = [] # # if start_angle > end_angle: # start_angle += 2 * math.pi # # line_seg = int((n_points * (end_angle - start_angle)) / math.pi) # step_angle = (end_angle - start_angle) / float(line_seg) # # angle = start_angle # for step in range(line_seg + 1): # if dir == 'CCW': # x = center_x + radius * math.cos(angle) # y = center_y + radius * math.sin(angle) # else: # x = center_x + radius * math.cos(-angle) # y = center_y + radius * math.sin(-angle) # point_list.append((x, y)) # angle += step_angle # # # log.debug("X = %.4f, Y = %.4f, Radius = %.4f, start_angle = %.1f, stop_angle = %.1f, step_angle = %.4f, dir=%s" % # (center_x, center_y, radius, start_angle, end_angle, step_angle, dir)) # # geo = LineString(point_list) # return geo ocs = arc.ocs() # if the extrusion attribute is not (0, 0, 1) then we have to change the coordinate system from OCS to WCS if arc.dxf.extrusion != (0, 0, 1): arc_center = ocs.to_wcs(arc.dxf.center) start_angle = arc.dxf.start_angle + 180 end_angle = arc.dxf.end_angle + 180 direction = 'CW' else: arc_center = arc.dxf.center start_angle = arc.dxf.start_angle end_angle = arc.dxf.end_angle direction = 'CCW' center_x = arc_center[0] center_y = arc_center[1] radius = arc.dxf.radius point_list = [] if start_angle > end_angle: start_angle = start_angle - 360 angle = start_angle step_angle = float(abs(end_angle - start_angle) / n_points) while angle <= end_angle: if direction == 'CCW': x = center_x + radius * math.cos(math.radians(angle)) y = center_y + radius * math.sin(math.radians(angle)) else: x = center_x + radius * math.cos(math.radians(-angle)) y = center_y + radius * math.sin(math.radians(-angle)) point_list.append((x, y)) angle += abs(step_angle) # in case the number of segments do not cover everything until the end of the arc if angle != end_angle: if direction == 'CCW': x = center_x + radius * math.cos(math.radians(end_angle)) y = center_y + radius * math.sin(math.radians(end_angle)) else: x = center_x + radius * math.cos(math.radians(- end_angle)) y = center_y + radius * math.sin(math.radians(- end_angle)) point_list.append((x, y)) # log.debug("X = %.4f, Y = %.4f, Radius = %.4f, start_angle = %.1f, stop_angle = %.1f, step_angle = %.4f" % # (center_x, center_y, radius, start_angle, end_angle, step_angle)) geo = LineString(point_list) return geo def dxfellipse2shapely(ellipse, ellipse_segments=100): # center = ellipse.dxf.center # start_angle = ellipse.dxf.start_param # end_angle = ellipse.dxf.end_param ocs = ellipse.ocs() # if the extrusion attribute is not (0, 0, 1) then we have to change the coordinate system from OCS to WCS if ellipse.dxf.extrusion != (0, 0, 1): center = ocs.to_wcs(ellipse.dxf.center) start_angle = ocs.to_wcs(ellipse.dxf.start_param) end_angle = ocs.to_wcs(ellipse.dxf.end_param) direction = 'CW' else: center = ellipse.dxf.center start_angle = ellipse.dxf.start_param end_angle = ellipse.dxf.end_param direction = 'CCW' # print("Dir = %s" % dir) major_axis = ellipse.dxf.major_axis ratio = ellipse.dxf.ratio points_list = [] major_axis = Vector(list(major_axis)) major_x = major_axis[0] major_y = major_axis[1] if start_angle >= end_angle: end_angle += 2.0 * math.pi line_seg = int((ellipse_segments * (end_angle - start_angle)) / math.pi) step_angle = abs(end_angle - start_angle) / float(line_seg) angle = start_angle for step in range(line_seg + 1): if direction == 'CW': major_dim = normalize_2(major_axis) minor_dim = normalize_2(Vector([ratio * k for k in major_axis])) vx = (major_dim[0] + major_dim[1]) * math.cos(angle) vy = (minor_dim[0] - minor_dim[1]) * math.sin(angle) x = center[0] + major_x * vx - major_y * vy y = center[1] + major_y * vx + major_x * vy angle += step_angle else: major_dim = normalize_2(major_axis) minor_dim = (Vector([ratio * k for k in major_dim])) vx = (major_dim[0] + major_dim[1]) * math.cos(angle) vy = (minor_dim[0] + minor_dim[1]) * math.sin(angle) x = center[0] + major_x * vx + major_y * vy y = center[1] + major_y * vx + major_x * vy angle += step_angle points_list.append((x, y)) geo = LineString(points_list) return geo def dxfpolyline2shapely(polyline): final_pts = [] pts = polyline.points() for i in pts: final_pts.append((i[0], i[1])) if polyline.is_closed: final_pts.append(final_pts[0]) geo = LineString(final_pts) return geo def dxflwpolyline2shapely(lwpolyline): final_pts = [] for point in lwpolyline: x, y, _, _, _ = point final_pts.append((x, y)) if lwpolyline.closed: final_pts.append(final_pts[0]) geo = LineString(final_pts) return geo def dxfsolid2shapely(solid): iterator = 0 corner_list = [] try: corner_list.append(solid[iterator]) iterator += 1 except Exception: return Polygon(corner_list) def dxfspline2shapely(spline): # for old version of ezdxf # with spline.edit_data() as spline_data: # ctrl_points = spline_data.control_points # try: # # required if using old version of ezdxf # knot_values = spline_data.knot_values # except AttributeError: # knot_values = spline_data.knots ctrl_points = spline.control_points knot_values = spline.knots is_closed = spline.closed degree = spline.dxf.degree x_list, y_list, _ = spline2Polyline(ctrl_points, degree=degree, closed=is_closed, segments=20, knots=knot_values) points_list = zip(x_list, y_list) geo = LineString(points_list) return geo def dxftrace2shapely(trace): iterator = 0 corner_list = [] try: corner_list.append(trace[iterator]) iterator += 1 except Exception: return Polygon(corner_list) def getdxfgeo(dxf_object): msp = dxf_object.modelspace() geos = get_geo(dxf_object, msp) # geo_block = get_geo_from_block(dxf_object) return geos def get_geo_from_insert(dxf_object, insert): geo_block_transformed = [] phi = insert.dxf.rotation tr = insert.dxf.insert sx = insert.dxf.xscale sy = insert.dxf.yscale r_count = insert.dxf.row_count r_spacing = insert.dxf.row_spacing c_count = insert.dxf.column_count c_spacing = insert.dxf.column_spacing # print(phi, tr) # identify the block given the 'INSERT' type entity name block = dxf_object.blocks[insert.dxf.name] block_coords = (block.block.dxf.base_point[0], block.block.dxf.base_point[1]) # get a list of geometries found in the block geo_block = get_geo(dxf_object, block) # iterate over the geometries found and apply any transformation found in the 'INSERT' entity attributes for geo in geo_block: # get the bounds of the geometry # minx, miny, maxx, maxy = geo.bounds if tr[0] != 0 or tr[1] != 0: geo = translate(geo, (tr[0] - block_coords[0]), (tr[1] - block_coords[1])) # support for array block insertions if r_count > 1: for r in range(r_count): geo_block_transformed.append(translate(geo, (tr[0] + (r * r_spacing) - block_coords[0]), 0)) if c_count > 1: for c in range(c_count): geo_block_transformed.append(translate(geo, 0, (tr[1] + (c * c_spacing) - block_coords[1]))) if sx != 1 or sy != 1: geo = scale(geo, sx, sy) if phi != 0: if isinstance(tr, str) and tr.lower() == 'c': tr = 'center' elif isinstance(tr, ezdxf_vector): tr = list(tr) geo = rotate(geo, phi, origin=tr) geo_block_transformed.append(geo) return geo_block_transformed def get_geo(dxf_object, container): # store shapely geometry here geo = [] for dxf_entity in container: g = [] # print("Entity", dxf_entity.dxftype()) if dxf_entity.dxftype() == 'POINT': g = dxfpoint2shapely(dxf_entity,) elif dxf_entity.dxftype() == 'LINE': g = dxfline2shapely(dxf_entity,) elif dxf_entity.dxftype() == 'CIRCLE': g = dxfcircle2shapely(dxf_entity) elif dxf_entity.dxftype() == 'ARC': g = dxfarc2shapely(dxf_entity) elif dxf_entity.dxftype() == 'ELLIPSE': g = dxfellipse2shapely(dxf_entity) elif dxf_entity.dxftype() == 'LWPOLYLINE': g = dxflwpolyline2shapely(dxf_entity) elif dxf_entity.dxftype() == 'POLYLINE': g = dxfpolyline2shapely(dxf_entity) elif dxf_entity.dxftype() == 'SOLID': g = dxfsolid2shapely(dxf_entity) elif dxf_entity.dxftype() == 'TRACE': g = dxftrace2shapely(dxf_entity) elif dxf_entity.dxftype() == 'SPLINE': g = dxfspline2shapely(dxf_entity) elif dxf_entity.dxftype() == 'INSERT': g = get_geo_from_insert(dxf_object, dxf_entity) else: log.debug(" %s is not supported yet." % dxf_entity.dxftype()) if g is not None: if type(g) == list: for subg in g: geo.append(subg) else: geo.append(g) return geo def getdxftext(exf_object, object_type, units=None): pass # def get_geo_from_block(dxf_object): # geo_block_transformed = [] # # msp = dxf_object.modelspace() # # iterate through all 'INSERT' entities found in modelspace msp # for insert in msp.query('INSERT'): # phi = insert.dxf.rotation # tr = insert.dxf.insert # sx = insert.dxf.xscale # sy = insert.dxf.yscale # r_count = insert.dxf.row_count # r_spacing = insert.dxf.row_spacing # c_count = insert.dxf.column_count # c_spacing = insert.dxf.column_spacing # # # print(phi, tr) # # # identify the block given the 'INSERT' type entity name # print(insert.dxf.name) # block = dxf_object.blocks[insert.dxf.name] # block_coords = (block.block.dxf.base_point[0], block.block.dxf.base_point[1]) # # # get a list of geometries found in the block # # store shapely geometry here # geo_block = [] # # for dxf_entity in block: # g = [] # # print("Entity", dxf_entity.dxftype()) # if dxf_entity.dxftype() == 'POINT': # g = dxfpoint2shapely(dxf_entity, ) # elif dxf_entity.dxftype() == 'LINE': # g = dxfline2shapely(dxf_entity, ) # elif dxf_entity.dxftype() == 'CIRCLE': # g = dxfcircle2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'ARC': # g = dxfarc2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'ELLIPSE': # g = dxfellipse2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'LWPOLYLINE': # g = dxflwpolyline2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'POLYLINE': # g = dxfpolyline2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'SOLID': # g = dxfsolid2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'TRACE': # g = dxftrace2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'SPLINE': # g = dxfspline2shapely(dxf_entity) # elif dxf_entity.dxftype() == 'INSERT': # log.debug("Not supported yet.") # else: # log.debug("Not supported yet.") # # if g is not None: # if type(g) == list: # for subg in g: # geo_block.append(subg) # else: # geo_block.append(g) # # # iterate over the geometries found and apply any transformation found in the 'INSERT' entity attributes # for geo in geo_block: # if tr[0] != 0 or tr[1] != 0: # geo = translate(geo, (tr[0] - block_coords[0]), (tr[1] - block_coords[1])) # # # support for array block insertions # if r_count > 1: # for r in range(r_count): # geo_block_transformed.append(translate(geo, (tr[0] + (r * r_spacing) - block_coords[0]), 0)) # # if c_count > 1: # for c in range(c_count): # geo_block_transformed.append(translate(geo, 0, (tr[1] + (c * c_spacing) - block_coords[1]))) # # if sx != 1 or sy != 1: # geo = scale(geo, sx, sy) # if phi != 0: # geo = rotate(geo, phi, origin=tr) # # geo_block_transformed.append(geo) # return geo_block_transformed