457 lines
15 KiB
Python
457 lines
15 KiB
Python
# ##########################################################
|
|
# 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
|
|
|
|
import logging
|
|
|
|
log = logging.getLogger('base2')
|
|
|
|
from flatcamParsers.ParseFont import *
|
|
from flatcamParsers.ParseDXF_Spline import *
|
|
|
|
|
|
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
|
|
dir = 'CW'
|
|
else:
|
|
arc_center = arc.dxf.center
|
|
start_angle = arc.dxf.start_angle
|
|
end_angle = 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 = start_angle - 360
|
|
angle = start_angle
|
|
|
|
step_angle = float(abs(end_angle - start_angle) / n_points)
|
|
|
|
while angle <= end_angle:
|
|
if dir == '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 dir == '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)
|
|
dir = 'CW'
|
|
else:
|
|
center = ellipse.dxf.center
|
|
start_angle = ellipse.dxf.start_param
|
|
end_angle = ellipse.dxf.end_param
|
|
dir = 'CCW'
|
|
|
|
# print("Dir = %s" % dir)
|
|
major_axis = ellipse.dxf.major_axis
|
|
ratio = ellipse.dxf.ratio
|
|
|
|
points_list = []
|
|
|
|
major_axis = Vector(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 dir == '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):
|
|
with spline.edit_data() as spline_data:
|
|
ctrl_points = spline_data.control_points
|
|
knot_values = spline_data.knot_values
|
|
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:
|
|
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
|