diff --git a/scripts/py/common/__init__.py b/scripts/py/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/scripts/py/common/drawing.py b/scripts/py/common/drawing.py
deleted file mode 100644
index 212c359..0000000
--- a/scripts/py/common/drawing.py
+++ /dev/null
@@ -1,793 +0,0 @@
-#!/bin/env python
-# -*- coding: utf-8; -*-
-#
-# (c) 2016 FABtotum, http://www.fabtotum.com
-#
-# This file is part of FABUI.
-#
-# FABUI is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# FABUI is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with FABUI. If not, see .
-
-__authors__ = "Daniel Kesler"
-__license__ = "GPL - https://opensource.org/licenses/GPL-3.0"
-__version__ = "1.0"
-
-# Import external modules
-import numpy as np
-from loaders import dxfgrabber
-import loaders.librecadfont1 as lff
-
-class Layer2D(object):
- def __init__(self, name, color = 255):
- self.name = name
- self.color = color
- self.primitives = []
-
- def addPrimitive(self, data):
- self.primitives.append(data)
-
-class Drawing2D(object):
- """
- :todo:
- - filled circle
- - filled ellipse
- - filled polyline
- """
-
- # Text attachment point.
- TOP_LEFT = 1
- TOP_CENTER = 2
- TOP_RIGHT = 3
- MIDDLE_LEFT = 4
- MIDDLE_CENTER = 5
- MIDDLE_RIGHT = 6
- BOTTOM_LEFT = 7
- BOTTOM_CENTER = 8
- BOTTOM_RIGHT = 9
-
- # Text alignment
- ALIGN_LEFT = 1
- ALIGN_CENTER = 2
- ALIGN_RIGHT = 3
-
- def __init__(self):
- self.clear()
- self.fonts = {}
- self.add_layer(name='Default', color=7)
-
- def extend_bounds(self, points):
- """
- Extend boundries to fit the points.
- """
- if type(points) is list:
- pass
- elif type(points) is tuple:
- points = [ points ]
- else:
- print 'neither a tuple or a list'
- return
-
- x1 = 1.0e10
- y1 = 1.0e10
- x2 = -1.0e10
- y2 = -1.0e10
-
- for pt in points:
- if pt[0] > x2:
- x2 = pt[0]
-
- if pt[0] < x1:
- x1 = pt[0]
-
- if pt[1] > y2:
- y2 = pt[1]
-
- if pt[1] < y1:
- y1 = pt[1]
-
- if x2 > self.max_x:
- self.max_x = x2
-
- if x1 < self.min_x:
- self.min_x = x1
-
- if y2 > self.max_y:
- self.max_y = y2
-
- if y1 < self.min_y:
- self.min_y = y1
-
- return x1,y1, np.absolute(x2-x1), np.absolute(y2-y1)
-
- def get_font(self, font):
- filename = '/var/lib/fabui/plugins/fab_laser/fonts/lff/{0}.lff'.format(font)
-
- print "loading font", filename
-
- if font not in self.fonts:
- f = lff.readfile(filename)
- if f:
- self.fonts[font] = f
- return f
- else:
- return self.fonts[font]
-
- def add_layer(self, name = None, color = 7):
- """
- Add a new layer.
- """
- idx = len(self.layers)
- if name is None:
- name = 'Layer_{0}'.format(idx)
-
- layer = Layer2D(name, color)
- self.layers.append(layer)
- return idx
-
- def __bulge2arc(self, p1, p2, b):
- center = (0,0)
- start = 0.0
- end = 0.0
- radius = 0.0
- reverse = False
- theta_half = 2*np.arctan(b)
-
- dx = p2[0] - p1[0]
- dy = p2[1] - p1[1]
-
- chord = np.sqrt(dx * dx + dy* dy)
-
- dx_norm = dx / chord
- dy_norm = dy / chord
-
- radius = chord / (2*np.sin(theta_half))
-
- p3 = ( p1[0] + dx*0.5, p1[1] + dy*0.5 )
-
- x = np.cos(theta_half) * radius
-
- center = ( p3[0] - dy_norm*x, p3[1] + dx_norm*x )
-
- if b < 0:
- # CW, flip start end
-
- theta = 2*theta_half
-
- dx = center[0] - p2[0]
- dy = center[1] - p2[1]
-
- a1 = np.arctan2(dy,dx)
-
- A = np.rad2deg( a1 )
- B = A - np.rad2deg( theta )
-
- end = B
- start = A
- reverse = True
- else:
- # CCW
-
- theta = 2*theta_half
-
- dx = center[0] - p1[0]
- dy = center[1] - p1[1]
-
- a1 = np.arctan2(dy,dx)
-
- start = np.rad2deg( a1 ) - 180
- end = start + np.rad2deg( theta )
-
- return center, radius, start, end, reverse
-
- def __translate_points(self, points, tx, ty):
- tpoints = []
- for pt in points:
- x = pt[0] + tx
- y = pt[y] + ty
- tpoints.append( (x,y) )
-
- return tpoints
-
- def add_rect(self, x1, y1, x2, y2, layer = 0, filled = False):
- points = [
- (x1,y1),
- (x2,y1),
- (x2,y2),
- (x1,y2),
- (x1,y1)
- ]
- data = { 'type' : 'rect', 'first': (x1,y1), 'second' : (x2,y2), 'points' : points, 'filled' : filled }
- self.layers[layer].addPrimitive(data)
- return self.extend_bounds(points)
-
- def add_rect2(self, x1, y1, w, h, layer = 0, filled = False):
- points = [
- (x1,y1),
- (x1+w,y1),
- (x1+w,y1+h),
- (x1,y1+h),
- (x1,y1),
- ]
- data = { 'type' : 'rect', 'first': (x1,y1), 'second' : (x1+w,y1+h), 'points' : points, 'filled' : filled }
- self.layers[layer].addPrimitive(data)
- return self.extend_bounds(points)
-
- def add_line(self, start, end, layer = 0):
- data = { 'type' : 'line', 'points': [start, end] }
- self.layers[layer].addPrimitive(data)
-
- return self.extend_bounds( [start, end] )
-
- def add_polyline(self, points, bulges, closed = False, layer = 0, filled = False, dummy = False):
-
- if closed:
- points.append(points[0])
-
- points2 = []
-
- cnt = 0
-
- idx = 0
- has_prev = False
- for pt in points:
- if has_prev:
- b = bulges[idx]
- if b != 0:
- # Construct and arc
- center, radius, start, end, reverse = self.__bulge2arc(p0, pt, b)
- arc = self.__arc(center, radius, start, end, step = 10.0, reverse=reverse)
-
- points2 += arc
- else:
- # Use a straight line
- points2.append(pt)
-
- idx += 1
- else:
- points2.append(pt)
-
- p0 = pt
- has_prev = True
-
- data = { 'type' : 'polyline', 'points' : points2, 'bulges' : bulges, 'closed' : closed, 'filled' : filled }
- if not dummy:
- self.layers[layer].addPrimitive(data)
- return self.extend_bounds(points2)
-
- def add_circle(self, center, radius, layer = 0, filled = False):
- points = self.__circle(center, radius)
- data = { 'type' : 'circle', 'center' : center, 'radius' : radius, 'points' : points, 'filled' : filled }
- self.layers[layer].addPrimitive(data)
- return self.extend_bounds(points)
-
- def add_arc(self, center, radius, start, end, layer = 0):
- points = self.__arc(center, radius, start, end)
- data = { 'type' : 'arc', 'center' : center, 'radius' : radius, 'start' : start, 'end': end, 'points' : points }
- self.layers[layer].addPrimitive(data)
- return self.extend_bounds(points)
-
- def add_spline(self, control_points, knots, degree, layer = 0):
-
- npts = len(control_points)
- k = degree + 1
- #~ p1 = (dxf.header['$SPLINESEGS'] or 8) * npts
- p1 = (8) * npts
-
- points = self.__rbspline(npts, k, p1, control_points, knots)
-
- data = { 'type' : 'spline', 'control_points' : control_points, 'knots' : knots, 'degree' : degree, 'points' : points}
- self.layers[layer].addPrimitive(data)
- return self.extend_bounds(points)
-
- def add_ellipse(self, center, major_axis, ratio, start, end, layer = 0, filled = False):
- points = self.__ellipse(center, major_axis, ratio, start, end)
- data = { 'type' : 'ellipse', 'center' : center, 'major_axis' : major_axis, 'ratio' : ratio, 'start' : start, 'end' : end, 'points' : points, 'filled' : filled}
- self.layers[layer].addPrimitive(data)
- self.extend_bounds(points)
-
- def add_text(self, position, align, width, height, direction, font_name, text_lines, layer = 0):
- # TODO:
- # - alignment
- # - direction
- #~ font_name = 'OpenGostTypeA-Regular'
- #~ font_name = 'iso'
- font = self.get_font(font_name)
-
- if not font:
- print "Font '{0}' not found".format(font_name)
- return
-
- print
- print "direction", direction
- print "height", height
-
- scale = height / 9.0
- print "scale",scale
- wordSpacing = float(font.meta['WordSpacing']) * scale
- letterSpacing = float(font.meta['LetterSpacing']) * scale
- letterHeight = height
-
- print "letterSpacing", letterSpacing
- print "wordSpacing", wordSpacing
-
- print text_lines
-
- off_x = position[0]
- off_y = position[1]
-
- for text in text_lines:
- #~ off_x = position[0]
-
- for c in text:
- if c == ' ':
- off_x += wordSpacing
- else:
- sym = font.getSymbol(c)
- # TODO: load reference character
- if sym.ref:
- #~ print "has ref to '{0}'".format(sym.ref)
- sym_ref = font.getSymbol(sym.ref)
- lines = sym_ref.lines + sym.lines
- else:
- lines = sym.lines
- cnt = 0
-
- max_x = -1e10
- max_y = -1e10
-
- for line in lines:
- eat_one = True
- points = []
- bulges = []
- for pt in line:
- x = pt[0]*scale + off_x
- y = pt[1]*scale + off_y
- p1 = (x, y)
- points.append(p1)
-
- if len(pt) == 3:
- bulges.append(pt[2])
- else:
- if len(points) > 0:
- if eat_one:
- eat_one = False
- pass
- else:
- bulges.append(0.0)
-
- #~ max_x = max(x, max_x)
- #~ max_y = max(y, max_y)
-
- if eat_one == False:
- bulges.append(0.0)
-
- x,y,w,h = self.add_polyline(points, bulges, layer=layer)
- #~ print x,y,w,h
- max_x = max(x+w, max_x)
- max_y = max(y+h,max_y)
-
- #~ print "R",off_x,off_y,max_x,max_y
- #~ self.add_rect(off_x,off_y,max_x,max_y,layer=layer)
-
- print "off_x", off_x, position[0]
-
- off_x = max_x + letterSpacing
- print "+off_x", off_x
-
- off_y -= letterHeight
- off_x = position[0]
-
- def __ellipse_point(self, center, r1, r2, rotM, t):
- x1 = r1 * np.cos( np.radians(t) )
- y1 = r2 * np.sin( np.radians(t) )
- tmp = np.array([x1,y1])
- p1 = center + (tmp * rotM)
- return p1.A1[0], p1.A1[1]
-
- def __ellipse(self, center, axis, ratio, start, end, step = 10.0):
-
- points = []
- x0 = center[0]
- y0 = center[1]
- # Get length of axis vector
- r1 = np.linalg.norm(axis)
- # Get second radius
- r2 = r1 * ratio
-
- # Get axis angle
- if axis[0] == 0:
- a = np.radians(90.0)
- elif axis[1] == 0:
- a = np.radians(0.0)
- else:
- a = np.arctan(axis[1] / axis[0])
-
- # Prepare rotation matrix
- center = np.array([x0,y0])
- c1 = np.cos(a)
- c2 = np.sin(a)
- rotM = np.matrix([
- [c1,c2],
- [-c2,c1]
- ])
- rotMCCW = np.matrix([
- [c1,-c2],
- [c2,c1]
- ])
-
- eye = np.matrix([ [1.0, 0.0], [0.0, 1.0] ])
-
- fix = 0
- if start > end:
- start += 180.0
- if start > 360.0:
- start -= 360.0
- end += 180.0
-
- have_prev = False
-
- tl = np.arange(start, end, step)
-
- for t in tl:
- x2,y2 = self.__ellipse_point(center, r1, r2, rotM, t)
-
- points.append( (x2,y2) )
-
- x1 = x2
- y1 = y2
- have_prev = True
-
- x2,y2 = self.__ellipse_point(center, r1, r2, rotM, end)
- points.append( (x2,y2) )
-
- return points
-
- def __circle(self, center, radius, step = 10.0):
- x0 = center[0]
- y0 = center[1]
- r = radius
- points = []
- start = 0.0
- end = 360.0
- steps = int( 360.0 / step )
-
- have_prev = False
-
- for a in xrange(steps):
- angle = np.deg2rad(start + a*step)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- points.append( (x2,y2) )
-
- x1 = x2
- y1 = y2
- have_prev = True
-
- if (start + (steps-1)*step) != end:
- angle = np.deg2rad(end)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- points.append( (x2,y2) )
-
- return points
-
- def __wrapTo360(self, angle):
- angle = np.fmod(angle,360);
- if angle < 0:
- angle += 360;
- return angle;
-
- def __arc(self, center, radius, start, end, step = 10.0, reverse=False):
- """
- Draw an arc.
-
- :param center: Tuple (x,y) representing the arc center point
- :param radius: Arc radius
- :param start: Arc start angle (degree)
- :param end: Arc end angle (degree)
- :param step: Angle change step
- :param reverse: Reverse arc points
-
- :returns: Arc points
- """
- x0 = center[0]
- y0 = center[1]
- points = []
- r = radius
- angle = self.__wrapTo360(end - start)
-
- steps = int(abs(angle / step))
-
- have_prev = False
-
- start = self.__wrapTo360(start)
- end = self.__wrapTo360(end)
-
- a1 = start
- a2 = end
- sign = 1
-
- if reverse:
- a1 = end
- a2 = start
- sign = -1
-
- for a in xrange(steps):
- angle = np.deg2rad(a1 + sign*a*step)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- points.append( (x2,y2) )
-
- x1 = x2
- y1 = y2
- have_prev = True
-
- if (a1 + sign*(steps-1)*step) != a2:
- angle = np.deg2rad(a2)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- points.append( (x2,y2) )
-
- return points
-
- def __rbasis(self, c, t, npts, x, h):
- """
- Generates rational B-spline basis functions for an open knot vector.
- :note: Source code converted from LibreCad (rs_spline.cpp)
-
- """
- nplusc = npts + c
- temp = np.zeros(nplusc)
-
- # calculate the first order nonrational basis functions n[i]
- for i in xrange(nplusc-1):
- if t >= x[i] and t < x[i+1]:
- temp[i] = 1
-
- # calculate the higher order nonrational basis functions
- for k in xrange(2,c+1):
- for i in xrange(nplusc-k):
- # if the lower order basis function is zero skip the calculation
- if temp[i] != 0:
- temp[i] = ((t-x[i])*temp[i])/(x[i+k-1]-x[i])
-
- # if the lower order basis function is zero skip the calculation
- if temp[i+1] != 0:
- temp[i] += ((x[i+k]-t)*temp[i+1])/(x[i+k]-x[i+1])
-
- # pick up last point
- if t >= x[nplusc-1]:
- temp[npts-1] = 1
-
- # calculate sum for denominator of rational basis functions
- sum = 0.0
- for i in xrange(npts):
- sum += temp[i]*h[i]
-
- r = np.zeros(npts)
- # form rational basis functions and put in r vector
- if sum != 0:
- for i in xrange(npts):
- r[i] = (temp[i]*h[i])/sum
- return r
-
- def __rbspline(self, npts, k, p1, b, knot):
- """
- Generates a rational B-spline curve using a uniform open knot vector.
- :note: Source code converted from LibreCad (rs_spline.cpp)
-
- :param npts: Number of control points
- :param k: Spline degree
- :param b: Control point list
- :param knot: knot list
- """
- p = []
- h = np.ones(npts+1)
- nplusc = npts + k
-
- # generate the open knot vector (we have one already)
- x = knot
-
- # calculate the points on the rational B-spline curve
- t = 0.0
- step = x[nplusc-1] / (p1-1)
-
- vp = np.zeros(shape=(p1,2))
-
- for i in xrange(p1):
- if x[nplusc-1] - t < 5e-6:
- t = x[nplusc-1]
- # generate the basis function for this value of t
- nbasis = self.__rbasis(k, t, npts, x, h)
-
- # generate a point on the curve
- for j in xrange(npts):
- x0 = b[j][0] * nbasis[j]
- y0 = b[j][1] * nbasis[j]
- vp[i] += ( x0, y0 )
-
- t += step
-
- p.append( vp[i] )
-
- return p
-
- def transform(self, sx = 1.0, sy = 1.0, ox = 0.0, oy = 0.0):
- self.max_x *= sx
- self.max_y *= sy
- self.min_x *= sx
- self.min_y *= sy
-
- for l in self.layers:
- for e in l.primitives:
- t = e['type']
-
- if 'points' in e:
- points = []
- for p in e['points']:
- points.append( ( (ox + p[0])*sx, (oy + p[1])*sy) )
- e['points'] = points
-
- if t == 'polyline' or t == 'spline':
- pass
-
- elif t == 'circle' or t == 'arc':
- p = e['center']
- e['center'] = ( (ox + p[0])*sx, (oy + p[1])*sy)
- e['radius'] *= (sx+sy) / 2.0
-
- elif t == 'ellipse':
- p = e['center']
- e['center'] = ( (ox + p[0])*sx, (oy + p[1])*sy)
- m = e['major_axis']
- e['major_axis'] = ( m[0] * sx, m[1] * sy, 0.0)
- # TODO scale other parameters
-
- def scale(self, sx = 1.0, sy = 1.0):
- self.transform(sx, sy)
-
- def width(self):
- return self.max_x - self.min_x
-
- def height(self):
- return self.max_y - self.min_y
-
- def scale_to(self, target_width = 0.0, target_height = 0.0):
- width = self.width()
- height = self.height()
-
- sx = 1.0
- sy = 1.0
-
- if target_width != 0.0:
- sx = target_width / width
- sy = sx
-
- if target_height != 0.0:
- sy = target_height / height
- sx = sy
-
- self.scale(sx, sy)
-
- def normalize(self, margin = 0.1):
- self.transform(1.0, 1.0, -self.min_x+margin, -self.min_y+margin)
-
- width = self.max_x - self.min_x
- height = self.max_y - self.min_y
-
- self.min_x = 0
- self.min_y = 0
- self.max_x = width
- self.max_y = height
-
- def clear(self):
- """
- Clear all values.
- """
- self.layers = []
- self.max_x = 0
- self.max_y = 0
- self.min_x = 0
- self.min_y = 0
-
- def load_from_dxf(self, filename, clear=True):
- dxf = dxfgrabber.readfile(filename)
-
- if clear:
- self.clear()
-
- layer_map = {}
-
- print "- Layers:"
- for l in dxf.layers:
- print " ", l.name, l.color, l.linetype
- color = 255
- layer_map[l.name] = self.add_layer(l.name, color)
-
- #~ print "- Blocks:"
- #~ for b in dxf.blocks:
- #~ print b.name
-
- #~ print "- Objects:"
- for o in dxf.objects:
- print o
-
- print "- Entries:"
- for e in dxf.entities:
- t = e.dxftype
- print "== ", t
- if t == 'LWPOLYLINE' or t == 'POLYLINE':
- is_closed = False
- if e.is_closed:
- is_closed = True
- self.add_polyline(e.points, e.bulge, is_closed, layer_map[e.layer])
-
- elif t == 'LINE':
- self.add_line(e.start, e.end, layer_map[e.layer])
-
- elif t == 'CIRCLE':
- self.add_circle(e.center, e.radius, layer_map[e.layer])
-
- elif t == 'ELLIPSE':
- self.add_ellipse(e.center, e.major_axis, e.ratio, np.rad2deg(e.start_param), np.rad2deg(e.end_param), layer_map[e.layer] )
-
- elif t == 'ARC':
- self.add_arc(e.center, e.radius, e.start_angle, e.end_angle, layer_map[e.layer])
-
- elif t == 'SPLINE':
- self.add_spline(e.control_points, e.knots, e.degree, layer_map[e.layer])
-
- elif t == 'MTEXT':
- ln = len(e.lines())
- lh = e.height
- w = e.rect_width
- x = e.insert[0]
- y = e.insert[1] - lh
- align = self.ALIGN_LEFT
-
- if e.attachment_point == self.TOP_LEFT:
- pass
- elif e.attachment_point == self.TOP_CENTER:
- #~ x -= w * 0.5
- align = self.ALIGN_CENTER
- elif e.attachment_point == self.TOP_RIGHT:
- #~ x -= w
- align = self.ALIGN_RIGHT
- elif e.attachment_point == self.MIDDLE_LEFT:
- y += ln*lh * 0.5
- elif e.attachment_point == self.MIDDLE_CENTER:
- #~ x -= w * 0.5
- y += ln*lh * 0.5
- align = self.ALIGN_CENTER
- elif e.attachment_point == self.MIDDLE_RIGHT:
- #~ x -= w
- y += ln*lh * 0.5
- align = self.ALIGN_RIGHT
- elif e.attachment_point == self.BOTTOM_LEFT:
- y += ln*lh
- pass
- elif e.attachment_point == self.BOTTOM_CENTER:
- #~ x -= w * 0.5
- y += ln*lh
- align = self.ALIGN_CENTER
- elif e.attachment_point == self.BOTTOM_RIGHT:
- #~ x -= w
- y += ln*lh
- align = self.ALIGN_RIGHT
-
- self.add_text((x,y), align, e.rect_width, e.height, e.xdirection, e.font, e.lines(), layer=layer_map[e.layer])
-
diff --git a/scripts/py/img2gcode.py b/scripts/py/img2gcode.py
deleted file mode 100755
index 34f544e..0000000
--- a/scripts/py/img2gcode.py
+++ /dev/null
@@ -1,642 +0,0 @@
-#!/bin/env python
-# -*- coding: utf-8; -*-
-#
-# (c) 2016 FABtotum, http://www.fabtotum.com
-#
-# This file is part of FABUI.
-#
-# FABUI is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# FABUI is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with FABUI. If not, see .
-
-# Import standard python module
-import argparse
-import time
-import gettext
-import json
-import os
-
-# Import external modules
-import numpy as np
-import cv2,cv
-from loaders import dxfgrabber
-
-# Import internal modules
-from common.drawing import Drawing2D
-from output.laser import LaserEngraver
-from output.debug import DebugEngraver
-
-# Set up message catalog access
-tr = gettext.translation('img2gcode', 'locale', fallback=True)
-_ = tr.ugettext
-
-def preprocess_dxf_image(filename):
- output = Drawing2D()
- output.load_from_dxf(filename)
- return output
-
-def preprocess_raster_image(image_file, target_width, target_height, dot_size, levels = 6, invert = False, crop = '', debug=False):
- """
- Convert a raster image to horizontal line list classified into levels by color intensity.
- Only width or height has to be non-zero as the other value is automatically calculated
- based on the image width/height ration.
-
- :param image_file: Raster image filename.
- :param target_width: Target width in mm.
- :param target_height: Target height in mm.
- :param dot_size: Smallest engraver detail (mm).
- :param levels: Number of gray levels.
- :param invert: Invert gray intensity.
- :param crop: Crop image (x,y,w,h) (pixels)
- """
-
- img = cv2.imread(image_file)
-
- #### Resize: BEGIN ####
-
- h, w = img.shape[:2]
-
- # Minimal dots per mm
- min_dpm = 1 #int(1 / dot_size)
-
- dpm = int(1 / dot_size)
-
- # Default scaling factor for Width
- sx = 1.0
- # Default scaling factor for Height
- sy = 1.0
-
- new_w = w
- new_h = h
-
- if target_width:
- # Dots per mm based on given parameters
- dpm_x = target_width / float(w)
-
- # If DPM is less then minimal, scale the image so that pixels
- # are of proper size
- if dpm_x < 1.0:
- max_w = float(target_width)
- new_w = int( w * dpm_x * dpm )
- sx = 1.0 / (dpm_x * dpm)
- print "new_w", new_w
-
- if target_height:
- # Dots per mm based on given parameters
- dpm_y = float(target_height) / float(h)
- ppm_y = float(h) / float(target_height)
-
- # If DPM is less then minimal, scale the image so that pixels
- # are of proper size
- if dpm_y < 1.0:
- max_h = float(target_height)
- new_h = int( h * dpm_y * dpm )
- sy = 1.0 / (dpm_y * dpm)
- print "new_h", new_h
-
- sx = w / float(new_w)
- sy = h / float(new_h)
- scale = max(sx,sy)
- print "scale", scale
-
- w = int(w / scale)
- h = int(h / scale)
-
- if target_width == 0:
- target_width = (w * float(target_height)) / h
-
- if target_height == 0:
- target_height = (h * float(target_width)) / w
-
- if scale > 1:
- work_width = w
- work_height = h
- else:
- work_width = int(target_width / dot_size)
- work_height = int(target_height / dot_size)
-
- print "Target (mm)", target_width, target_height
-
- # Resize image if needed
- if scale != 1:
- print "Resize to {0}x{1}".format(w,h)
- img = cv2.resize(img,(w, h), interpolation = cv2.INTER_CUBIC)
-
- #### Resize: END ####
-
- if crop:
- crop = crop.split(',')
- x1 = int(crop[0])
- x2 = x1 + int(crop[2])
- y1 = int(crop[1])
- y2 = y1 + int(crop[3])
- print "Crop {0} {1} {2} {3}".format(x1, x2, y1, y2)
- img = img[y1:y2, x1:x2]
- #~ if debug:
- #~ cv2.imwrite('cropped.png', img)
-
- # Flip image on the Y axis to compensate for image Y axis and
- # machine Y axis orientation
- img = cv2.flip(img,0)
-
- # ?
- Z = img.reshape((-1,3))
- # Convert to np.float32
- Z = np.float32(Z)
- # Define criteria, number of clusters(K) and apply kmeans()
- criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
- # Define number of levels
- K = levels
- ret,label,center = cv2.kmeans(Z, K, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
-
- shades_of_gray = np.ndarray( (center.shape[0], 1) , dtype=int)
- i = 0
- for c in center:
- x = c[0] * 0.3 + c[1] * 0.59 + c[2] * 0.11
- if invert:
- x = 255 - int(x)
- shades_of_gray[i] = int(x)
- i += 1
-
- # Center is an array of cluster representative colors
- # label is an array of cluster labels for every pixel (label=[0..K-1] )
- h, w = img.shape[:2]
-
- flat_labels = label.flatten()
-
- res = shades_of_gray[flat_labels]
- res2 = res.reshape( (h,w,1) )
- img = res2
-
- # Invert image if requested
- if invert:
- img = 255 - img
-
- # save a preview for internal use
- #~ if debug:
- #~ cv2.imwrite('img_preprocess.png', img)
-
- sorted_geays = shades_of_gray.copy()
- sorted_geays.sort(axis=0)
- mapped = range(shades_of_gray.shape[0])
- rmapped = range(shades_of_gray.shape[0])
-
- for i in xrange(sorted_geays.shape[0]):
- value = sorted_geays[i]
- idx = np.nonzero(shades_of_gray == value)
- j = int(idx[0])
- mapped[i] = j
- rmapped[j] = i
-
- results = {
- 'level' : range(levels),
- 'width' : w,
- 'height' : h,
- 'target_width' : target_width,
- 'target_height' : target_height
- }
-
- for lvl in xrange(levels):
-
- value = int(shades_of_gray[ rmapped[lvl] ])
-
- results['level'][lvl] = {
- 'lines' : [],
- 'value' : value,
- 'percentage' : float(value) / 255.0,
- }
-
- mask = np.zeros((shades_of_gray.shape), dtype=np.int)
- mask[ lvl ] = 255
-
- res = mask[flat_labels]
- res2 = res.reshape( (h,w,1) )
-
- # Invert image if requested
- if invert:
- res2 = 255 - res2
-
- # save a preview for internal use
- #~ if debug:
- #~ cv2.imwrite('img_preprocess_{0}.png'.format(lvl), res2)
-
- for ii in xrange(len(mapped)):
- results['level'][ii]['lines'] = []
-
- results['work_width'] = work_width
- results['work_height'] = work_height
-
- for y in xrange(h):
- for ii in xrange(len(mapped)):
- results['level'][ii]['lines'].append([])
-
- old_lbl = -1
- new_lbl = -1
- start_x = 0
-
- for x in xrange(w):
- idx = y * w + x
- new_lbl = flat_labels[idx]
- if new_lbl != old_lbl:
- if x > start_x:
- lbl = mapped[old_lbl]
-
- x1 = int( float(start_x * work_width) / w )
- x2 = int( float(x * work_width) / w )
-
- results['level'][lbl]['lines'][y].append( (x1, x2) )
- old_lbl = new_lbl
- start_x = x
-
- if x >= start_x:
- lbl = mapped[old_lbl]
- x1 = int( float(start_x * work_width) / w )
- x2 = int( float(x * work_width) / w )+1
- results['level'][lbl]['lines'][y].append( (x1, x2) )
-
- return results
-
-def preprocess_raster_image_bw(image_file, target_width, target_height, dot_size, threshold = 127, invert = False, crop = '', debug=False):
- """
- Convert a raster image to horizontal line list classified into levels by color intensity.
- Only width or height has to be non-zero as the other value is automatically calculated
- based on the image width/height ration.
-
- :param image_file: Raster image filename.
- :param target_width: Target width in mm.
- :param target_height: Target height in mm.
- :param dot_size: Smallest engraver detail (mm).
- :param levels: Number of gray levels.
- :param invert: Invert gray intensity.
- :param crop: Crop image (x,y,w,h) (pixels)
- """
-
- img = cv2.imread(image_file)
-
- #### Resize: BEGIN ####
-
- h, w = img.shape[:2]
-
- # Minimal dots per mm
- min_dpm = 1 #int(1 / dot_size)
-
- dpm = int(1 / dot_size)
-
- # Default scaling factor for Width
- sx = 1.0
- # Default scaling factor for Height
- sy = 1.0
-
- new_w = w
- new_h = h
-
- if target_width:
- # Dots per mm based on given parameters
- dpm_x = target_width / float(w)
-
- # If DPM is less then minimal, scale the image so that pixels
- # are of proper size
- if dpm_x < 1.0:
- max_w = float(target_width)
- new_w = int( w * dpm_x * dpm )
- sx = 1.0 / (dpm_x * dpm)
- print "new_w", new_w
-
- if target_height:
- # Dots per mm based on given parameters
- dpm_y = float(target_height) / float(h)
- ppm_y = float(h) / float(target_height)
-
- # If DPM is less then minimal, scale the image so that pixels
- # are of proper size
- if dpm_y < 1.0:
- max_h = float(target_height)
- new_h = int( h * dpm_y * dpm )
- sy = 1.0 / (dpm_y * dpm)
- print "new_h", new_h
-
- sx = w / float(new_w)
- sy = h / float(new_h)
- scale = max(sx,sy)
- print "scale", scale
-
- w = int(w / scale)
- h = int(h / scale)
-
- if target_width == 0:
- target_width = (w * float(target_height)) / h
-
- if target_height == 0:
- target_height = (h * float(target_width)) / w
-
- if scale > 1:
- work_width = w
- work_height = h
- else:
- work_width = int(target_width / dot_size)
- work_height = int(target_height / dot_size)
-
- print "Target (mm)", target_width, target_height
-
- # Resize image if needed
- if scale != 1:
- print "Resize to {0}x{1}".format(w,h)
- img = cv2.resize(img,(w, h), interpolation = cv2.INTER_CUBIC)
-
- #### Resize: END ####
-
- if crop:
- crop = crop.split(',')
- x1 = int(crop[0])
- x2 = x1 + int(crop[2])
- y1 = int(crop[1])
- y2 = y1 + int(crop[3])
- print "Crop {0} {1} {2} {3}".format(x1, x2, y1, y2)
- img = img[y1:y2, x1:x2]
- if debug:
- cv2.imwrite('cropped.png', img)
-
- # Flip image on the Y axis to compensate for image Y axis and
- # machine Y axis orientation
- img = cv2.flip(img,0)
-
- t = cv2.THRESH_BINARY
- if invert:
- t = cv2.THRESH_BINARY_INV
-
- ACTIVE_LABEL = 1
-
- gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- ret,thresh1 = cv2.threshold(gray_image, threshold, ACTIVE_LABEL, t)
-
- flat_labels = thresh1.flatten()
-
- # Center is an array of cluster representative colors
- # label is an array of cluster labels for every pixel (label=[0..K-1] )
- h, w = img.shape[:2]
-
- # save a preview for internal use
- if debug:
- # Invert image if requested
- img = thresh1 * 255
- cv2.imwrite('img_preprocess.png', img)
-
- results = {
- 'level' : {
- 0 : {
- 'lines' : [],
- 'value' : 255,
- 'percentage' : 1.0,
- }
- },
- 'width' : w,
- 'height' : h,
- 'target_width' : target_width,
- 'target_height' : target_height
- }
-
- results['work_width'] = work_width
- results['work_height'] = work_height
-
- for y in xrange(h):
- results['level'][0]['lines'].append([])
-
- old_lbl = -1
- new_lbl = -1
- start_x = 0
-
- for x in xrange(w):
- idx = y * w + x
- new_lbl = flat_labels[idx]
- if new_lbl != old_lbl:
- if x > start_x:
- lbl = old_lbl
-
- x1 = int( float(start_x * work_width) / w )
- x2 = int( float(x * work_width) / w )
- if lbl == ACTIVE_LABEL:
- results['level'][0]['lines'][y].append( (x1, x2) )
-
- start_x = x
- old_lbl = new_lbl
-
- if x >= start_x:
- lbl = old_lbl
- x1 = int( float(start_x * work_width) / w )
- x2 = int( float(x * work_width) / w )+1
- if lbl == ACTIVE_LABEL:
- results['level'][0]['lines'][y].append( (x1, x2) )
-
- return results
-
-def vectorize_raster(data, preset):
- """
- Draw horizontal lines.
- """
- work_width = data['work_width']
- width = data['width']
- work_height = data['work_height']
- height = data['height']
-
- output = Drawing2D()
-
- reverse = False
- interleave = False
-
- lvl = 0
- for level in data['level']:
- value = data['level'][lvl]['value']
- #~ percent = data['level'][lvl]['percentage']
-
- lyr_idx = output.add_layer('level_{0}'.format(lvl), value)
-
- #~ self.comment(" Level {0}".format(lvl))
- #self.level(value)
-
- old_img_y = -1
- y1 = 0
-
- #~ if 'interleave' in preset['skip']:
- #~ if preset['skip']['interleave']:
- #~ interleave = not interleave
-
- for y in xrange(work_height):
- # Get the pixel row corresponding to the burning y
- # Note: this is needed as a pixel might contain multiple laser lines (fat pixels)
- img_y = int((float(height) * y * 100.0) / float(work_height)) / 100
-
- # Detect the boundry of the pixel rows and create a fill_rect
- # command from it. This way it's easier to handle it as a bulk command.
- if old_img_y != img_y:
-
- #~ self.comment(' Row {0}'.format(y))
-
- old_img_y = img_y
- # Update row end
- y2 = y+1
- did_something = False
-
- # Get all the lines in this row
- lines = data['level'][lvl]['lines'][img_y]
-
- # Change the direction of burning to reduce laser movement
- if reverse:
- lines = reversed(lines)
-
- for line in lines:
- if reverse:
- # Swap x1/x2 if reversed
- x1 = line[1]
- x2 = line[0]
- else:
- x1 = line[0]
- x2 = line[1]
-
- output.add_rect(x1, y1, x2, y2, lyr_idx, filled=True)
- did_something = True
-
- # If any burning happened, reverse the burning direction
- if did_something:
- reverse = not reverse
-
- # Next row starts where this one ended
- y1 = y2
-
- lvl += 1
-
- return output
-
-def main():
- # SETTING EXPECTED ARGUMENTS
- parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
- parser.add_argument("preset_file", help=_("Preset file [json file]"))
- parser.add_argument("image_file", help=_("Image file [raster: jpg, png; vector: dxf]"))
- parser.add_argument("-o", "--output", help=_("Output gcode file."), default='laser.gcode')
- # Image preprocessing
- parser.add_argument("-l", "--levels", help=_("Laser power levels [used for raster, range: 2..10]"), default=6)
- parser.add_argument("-i", "--invert", action='store_true', help=_("Invert color values"), default=False)
- parser.add_argument("-d", "--debug", action='store_true', help=_("Enable debug output"), default=False)
- parser.add_argument("-C", "--crop", help=_("Crop image. Use x,y,w,h'. Example: -c 0,0,100,100"), default='')
- # Vectorization
- parser.add_argument("-W", "--width", help=_("Engraving width"), default=0)
- parser.add_argument("-H", "--height", help=_("Engraving height"), default=0)
- parser.add_argument("-D", "--dot-size", help=_("Engraving dot size [mm]"), default=0.1)
- parser.add_argument("-S", "--shortest-line", help=_("Ignore lines shorter then this value [mm]"), default=0.0)
- # Toolpath
- parser.add_argument("-O", "--optimize", help=_("Optimize toolpath to reduce travel distance [0=off, 1=closest, 2=closes+reverse]"), default=1)
- parser.add_argument("-s", "--statistics", action='store_true', help=_("Output toolpath statistics"), default=False)
-
-
- # GET ARGUMENTS
- args = parser.parse_args()
-
- # INIT VARs
- gcode_file = args.output
- image_file = args.image_file
- preset_file = args.preset_file
- target_width = float(args.width)
- target_height = float(args.height)
- levels = int(args.levels)
- invert = bool(args.invert)
- debug = bool(args.debug)
- crop = args.crop
- dot_size = float(args.dot_size)
- shortest_line = float(args.shortest_line)
- optimize = int(args.optimize)
- statistics = bool(args.statistics)
-
- if levels < 1 or levels > 10:
- print "Level is out of allowed range"
- parser.print_help()
- exit(1)
-
- filename, ext = os.path.splitext(image_file)
- ext = ext.lower()
-
- with open(preset_file) as f:
- preset = json.load(f)
-
- if ext == '.dxf':
- drawing = preprocess_dxf_image(image_file)
- drawing.normalize()
- drawing.scale_to(target_width, target_height)
-
- lsr = LaserEngraver(gcode_file, (1.0, 1.0), preset, statistics=statistics)
- lsr.start()
- lsr.draw(drawing, optimize=optimize)
- lsr.end()
-
- if statistics:
- print lsr.stats
-
- if debug:
- #~ os.system('LC_NUMERIC=C camotics output.gcode')
- work_path = os.path.dirname(gcode_file)
- dbg_file = os.path.join(work_path,'debug.png')
-
- dpm = 1.0 / dot_size
-
- work_width = int(drawing.width() * dpm) + 5
- work_height = int(drawing.height() * dpm) + 5
-
- print "Debug (WxH):", work_width, work_height
-
- color = 0
- #~ if invert:
- #~ color = 255
-
- drawing.transform(dpm, dpm, 0, 0)
-
- dbg = DebugEngraver(dbg_file, work_width, work_height, color, dot_size, True, preset)
- dbg.start()
- dbg.draw(drawing)
- dbg.end()
-
- elif ext == '.jpg' or ext == '.jpeg' or ext == '.png':
-
- if levels > 1:
- result = preprocess_raster_image(image_file, target_width, target_height, dot_size, levels, invert, crop, debug=debug)
- else:
- result = preprocess_raster_image_bw(image_file, target_width, target_height, dot_size, 127, invert, crop, debug=debug)
-
- drawing = vectorize_raster(result, preset)
-
- s = dot_size
-
- lsr = LaserEngraver(gcode_file, (s,s), preset, statistics=statistics)
- lsr.start()
- lsr.draw(drawing, optimize=0)
- lsr.end()
-
- if statistics:
- print lsr.stats
-
- if debug:
- #~ os.system('LC_NUMERIC=C camotics output.gcode')
-
- work_path = os.path.dirname(gcode_file)
- dbg_file = os.path.join(work_path,'debug.png')
-
- color = 0
- #~ if invert:
- #~ color = 0
-
- dbg = DebugEngraver(dbg_file, result['work_width'], result['work_height'], color, dot_size, True, preset)
- dbg.start()
- dbg.draw(drawing)
- dbg.end()
- else:
- print "Unsupported filetype"
- exit(1)
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/py/loaders/__init__.py b/scripts/py/loaders/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/scripts/py/loaders/dxfgrabber/__init__.py b/scripts/py/loaders/dxfgrabber/__init__.py
deleted file mode 100644
index 105bc46..0000000
--- a/scripts/py/loaders/dxfgrabber/__init__.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# dxfgrabber - copyright (C) 2012 by Manfred Moitzi (mozman)
-# Purpose: grab information from DXF drawings - all DXF versions supported
-# Created: 21.07.2012
-# License: MIT License
-
-version = (0, 8, 1)
-VERSION = "%d.%d.%d" % version
-
-__author__ = "mozman "
-__doc__ = """A Python library to grab information from DXF drawings - all DXF versions supported."""
-
-
-# Python27/3x support should be done here
-import sys
-
-PYTHON3 = sys.version_info.major > 2
-
-if PYTHON3:
- tostr = str
-else: # PYTHON27
- tostr = unicode
-
-# end of Python 2/3 adaption
-# if tostr does not work, look at package 'dxfwrite' for escaping unicode chars
-
-from .const import BYBLOCK, BYLAYER
-
-import io
-from .tags import dxfinfo
-from .color import aci_to_true_color
-
-
-def read(stream, options=None):
- if hasattr(stream, 'readline'):
- from .drawing import Drawing
- return Drawing(stream, options)
- else:
- raise AttributeError('stream object requires a readline() method.')
-
-
-def readfile(filename, options=None):
- try: # is it ascii code-page encoded?
- return readfile_as_asc(filename, options)
- except UnicodeDecodeError: # try unicode and ignore errors
- return readfile_as_utf8(filename, options, errors='ignore')
-
-
-def readfile_as_utf8(filename, options=None, errors='strict'):
- return _read_encoded_file(filename, options, encoding='utf-8', errors=errors)
-
-
-def readfile_as_asc(filename, options=None):
- def get_encoding():
- with io.open(filename) as fp:
- info = dxfinfo(fp)
- return info.encoding
-
- return _read_encoded_file(filename, options, encoding=get_encoding())
-
-
-def _read_encoded_file(filename, options=None, encoding='utf-8', errors='strict'):
- from .drawing import Drawing
-
- with io.open(filename, encoding=encoding, errors=errors) as fp:
- dwg = Drawing(fp, options)
- dwg.filename = filename
- return dwg
diff --git a/scripts/py/loaders/dxfgrabber/acdsdata.py b/scripts/py/loaders/dxfgrabber/acdsdata.py
deleted file mode 100644
index 29e70e4..0000000
--- a/scripts/py/loaders/dxfgrabber/acdsdata.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Purpose: acdsdata section manager
-# Created: 05.05.2014
-# Copyright (C) 2014, Manfred Moitzi
-# License: MIT License
-
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from itertools import islice
-from .tags import TagGroups, DXFStructureError, Tags, binary_encoded_data_to_bytes
-
-
-class AcDsDataSection(object):
- name = 'acdsdata'
-
- def __init__(self):
- # Standard_ACIS_Binary (SAB) data store, key = handle of DXF Entity in the ENTITIES section: BODY, 3DSOLID
- # SURFACE, PLANESURFACE, REGION
- self.sab_data = {}
-
- @classmethod
- def from_tags(cls, tags, drawing):
- data_section = cls()
- data_section._build(tags)
- return data_section
-
- def _build(self, tags):
- if len(tags) == 3: # empty entities section
- return
-
- for group in TagGroups(islice(tags, 2, len(tags)-1)):
- data_record = AcDsDataRecord(Tags(group))
- if data_record.dxftype == 'ACDSRECORD':
- asm_data = data_record.get_section('ASM_Data', None)
- if asm_data is not None:
- self.add_asm_data(data_record)
-
- def add_asm_data(self, acdsrecord):
- """ Store SAB data as binary string in the sab_data dict, with handle to owner Entity as key.
- """
- try:
- asm_data = acdsrecord.get_section('ASM_Data')
- entity_id = acdsrecord.get_section('AcDbDs::ID')
- except ValueError:
- return
- else:
- handle = entity_id[2].value
- binary_data_text = (tag.value for tag in asm_data if tag.code == 310)
- binary_data = binary_encoded_data_to_bytes(binary_data_text)
- self.sab_data[handle] = binary_data
-
-
-class Section(Tags):
- @property
- def name(self):
- return self[0].value
-
- @property
- def type(self):
- return self[1].value
-
- @property
- def data(self):
- return self[2:]
-
-
-class AcDsDataRecord(object):
- def __init__(self, tags):
- self.dxftype = tags[0].value
- start_index = 2
- while tags[start_index].code != 2:
- start_index += 1
- self.sections = [Section(tags) for tags in TagGroups(islice(tags, start_index, None), split_code=2)]
-
- def has_section(self, name):
- return self.get_section(name, default=None) is not None
-
- def get_section(self, name, default=KeyError):
- for section in self.sections:
- if section.name == name:
- return section
- if default is KeyError:
- raise KeyError(name)
- else:
- return default
-
- def __getitem__(self, name):
- return self.get_section(name)
diff --git a/scripts/py/loaders/dxfgrabber/blockssection.py b/scripts/py/loaders/dxfgrabber/blockssection.py
deleted file mode 100644
index c4336a7..0000000
--- a/scripts/py/loaders/dxfgrabber/blockssection.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Purpose: blocks section
-# Created: 09.08.2012, taken from my package ezdxf
-# Copyright (C) 2011, Manfred Moitzi
-# License: MIT-License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from itertools import islice
-
-from .tags import TagGroups
-from .entitysection import build_entities
-
-
-class BlocksSection(object):
- name = 'blocks'
-
- def __init__(self):
- self._blocks = dict()
-
- @staticmethod
- def from_tags(tags, drawing):
- blocks_section = BlocksSection()
- if drawing.grab_blocks:
- blocks_section._build(tags)
- return blocks_section
-
- def _build(self, tags):
- if len(tags) == 3: # empty block section
- return
- groups = list()
- for group in TagGroups(islice(tags, 2, len(tags)-1)):
- groups.append(group)
- if group[0].value == 'ENDBLK':
- entities = build_entities(groups)
- block = entities[0]
- block.set_entities(entities[1:-1])
- self._add(block)
- groups = list()
-
- def _add(self, block):
- self._blocks[block.name] = block
-
- # start of public interface
- def __len__(self):
- return len(self._blocks)
-
- def __iter__(self):
- return iter(self._blocks.values())
-
- def __contains__(self, name):
- return name in self._blocks
-
- def __getitem__(self, name):
- return self._blocks[name]
-
- def get(self, name, default=None):
- return self._blocks.get(name, default)
diff --git a/scripts/py/loaders/dxfgrabber/codepage.py b/scripts/py/loaders/dxfgrabber/codepage.py
deleted file mode 100644
index 76baa54..0000000
--- a/scripts/py/loaders/dxfgrabber/codepage.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Purpose: codepage handling
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-codepages = {
- '874': 'cp874', # Thai,
- '932': 'cp932', # Japanese
- '936': 'gbk', # UnifiedChinese
- '949': 'cp949', # Korean
- '950': 'cp950', # TradChinese
- '1250': 'cp1250', # CentralEurope
- '1251': 'cp1251', # Cyrillic
- '1252': 'cp1252', # WesternEurope
- '1253': 'cp1253', # Greek
- '1254': 'cp1254', # Turkish
- '1255': 'cp1255', # Hebrew
- '1256': 'cp1256', # Arabic
- '1257': 'cp1257', # Baltic
- '1258': 'cp1258', # Vietnam
-}
-
-
-def toencoding(dxfcodepage):
- for codepage, encoding in codepages.items():
- if dxfcodepage.endswith(codepage):
- return encoding
- return 'cp1252'
-
-
-def tocodepage(encoding):
- for codepage, enc in codepages.items():
- if enc == encoding:
- return 'ANSI_'+codepage
- return 'ANSI_1252'
diff --git a/scripts/py/loaders/dxfgrabber/color.py b/scripts/py/loaders/dxfgrabber/color.py
deleted file mode 100644
index 832e4ae..0000000
--- a/scripts/py/loaders/dxfgrabber/color.py
+++ /dev/null
@@ -1,301 +0,0 @@
-__author__ = 'manfred'
-
-
-class TrueColor(int):
- def rgb(self):
- return (self >> 16) & 0xFF, (self >> 8) & 0xFF, self & 0xFF
-
- @property
- def r(self):
- return (self >> 16) & 0xFF
-
- @property
- def g(self):
- return (self >> 8) & 0xFF
-
- @property
- def b(self):
- return self & 0xFF
-
- def __getitem__(self, item):
- if item == 0:
- return self.r
- elif item == 1:
- return self.g
- elif item == 2:
- return self.b
- raise IndexError(item)
-
- @staticmethod
- def from_rgb(r, g, b):
- return TrueColor(((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))
-
- @staticmethod
- def from_aci(index):
- if index < 1:
- raise IndexError(index)
- return dxf_default_colors[index]
-
-
-def aci_to_true_color(index):
- return TrueColor.from_aci(index)
-
-
-dxf_default_colors = [
- TrueColor(0x000000),
- TrueColor(0xff0000),
- TrueColor(0xffff00),
- TrueColor(0x00ff00),
- TrueColor(0x00ffff),
- TrueColor(0x0000ff),
- TrueColor(0xff00ff),
- TrueColor(0xffffff),
- TrueColor(0x414141),
- TrueColor(0x808080),
- TrueColor(0xff0000),
- TrueColor(0xffaaaa),
- TrueColor(0xbd0000),
- TrueColor(0xbd7e7e),
- TrueColor(0x810000),
- TrueColor(0x815656),
- TrueColor(0x680000),
- TrueColor(0x684545),
- TrueColor(0x4f0000),
- TrueColor(0x4f3535),
- TrueColor(0xff3f00),
- TrueColor(0xffbfaa),
- TrueColor(0xbd2e00),
- TrueColor(0xbd8d7e),
- TrueColor(0x811f00),
- TrueColor(0x816056),
- TrueColor(0x681900),
- TrueColor(0x684e45),
- TrueColor(0x4f1300),
- TrueColor(0x4f3b35),
- TrueColor(0xff7f00),
- TrueColor(0xffd4aa),
- TrueColor(0xbd5e00),
- TrueColor(0xbd9d7e),
- TrueColor(0x814000),
- TrueColor(0x816b56),
- TrueColor(0x683400),
- TrueColor(0x685645),
- TrueColor(0x4f2700),
- TrueColor(0x4f4235),
- TrueColor(0xffbf00),
- TrueColor(0xffeaaa),
- TrueColor(0xbd8d00),
- TrueColor(0xbdad7e),
- TrueColor(0x816000),
- TrueColor(0x817656),
- TrueColor(0x684e00),
- TrueColor(0x685f45),
- TrueColor(0x4f3b00),
- TrueColor(0x4f4935),
- TrueColor(0xffff00),
- TrueColor(0xffffaa),
- TrueColor(0xbdbd00),
- TrueColor(0xbdbd7e),
- TrueColor(0x818100),
- TrueColor(0x818156),
- TrueColor(0x686800),
- TrueColor(0x686845),
- TrueColor(0x4f4f00),
- TrueColor(0x4f4f35),
- TrueColor(0xbfff00),
- TrueColor(0xeaffaa),
- TrueColor(0x8dbd00),
- TrueColor(0xadbd7e),
- TrueColor(0x608100),
- TrueColor(0x768156),
- TrueColor(0x4e6800),
- TrueColor(0x5f6845),
- TrueColor(0x3b4f00),
- TrueColor(0x494f35),
- TrueColor(0x7fff00),
- TrueColor(0xd4ffaa),
- TrueColor(0x5ebd00),
- TrueColor(0x9dbd7e),
- TrueColor(0x408100),
- TrueColor(0x6b8156),
- TrueColor(0x346800),
- TrueColor(0x566845),
- TrueColor(0x274f00),
- TrueColor(0x424f35),
- TrueColor(0x3fff00),
- TrueColor(0xbfffaa),
- TrueColor(0x2ebd00),
- TrueColor(0x8dbd7e),
- TrueColor(0x1f8100),
- TrueColor(0x608156),
- TrueColor(0x196800),
- TrueColor(0x4e6845),
- TrueColor(0x134f00),
- TrueColor(0x3b4f35),
- TrueColor(0x00ff00),
- TrueColor(0xaaffaa),
- TrueColor(0x00bd00),
- TrueColor(0x7ebd7e),
- TrueColor(0x008100),
- TrueColor(0x568156),
- TrueColor(0x006800),
- TrueColor(0x456845),
- TrueColor(0x004f00),
- TrueColor(0x354f35),
- TrueColor(0x00ff3f),
- TrueColor(0xaaffbf),
- TrueColor(0x00bd2e),
- TrueColor(0x7ebd8d),
- TrueColor(0x00811f),
- TrueColor(0x568160),
- TrueColor(0x006819),
- TrueColor(0x45684e),
- TrueColor(0x004f13),
- TrueColor(0x354f3b),
- TrueColor(0x00ff7f),
- TrueColor(0xaaffd4),
- TrueColor(0x00bd5e),
- TrueColor(0x7ebd9d),
- TrueColor(0x008140),
- TrueColor(0x56816b),
- TrueColor(0x006834),
- TrueColor(0x456856),
- TrueColor(0x004f27),
- TrueColor(0x354f42),
- TrueColor(0x00ffbf),
- TrueColor(0xaaffea),
- TrueColor(0x00bd8d),
- TrueColor(0x7ebdad),
- TrueColor(0x008160),
- TrueColor(0x568176),
- TrueColor(0x00684e),
- TrueColor(0x45685f),
- TrueColor(0x004f3b),
- TrueColor(0x354f49),
- TrueColor(0x00ffff),
- TrueColor(0xaaffff),
- TrueColor(0x00bdbd),
- TrueColor(0x7ebdbd),
- TrueColor(0x008181),
- TrueColor(0x568181),
- TrueColor(0x006868),
- TrueColor(0x456868),
- TrueColor(0x004f4f),
- TrueColor(0x354f4f),
- TrueColor(0x00bfff),
- TrueColor(0xaaeaff),
- TrueColor(0x008dbd),
- TrueColor(0x7eadbd),
- TrueColor(0x006081),
- TrueColor(0x567681),
- TrueColor(0x004e68),
- TrueColor(0x455f68),
- TrueColor(0x003b4f),
- TrueColor(0x35494f),
- TrueColor(0x007fff),
- TrueColor(0xaad4ff),
- TrueColor(0x005ebd),
- TrueColor(0x7e9dbd),
- TrueColor(0x004081),
- TrueColor(0x566b81),
- TrueColor(0x003468),
- TrueColor(0x455668),
- TrueColor(0x00274f),
- TrueColor(0x35424f),
- TrueColor(0x003fff),
- TrueColor(0xaabfff),
- TrueColor(0x002ebd),
- TrueColor(0x7e8dbd),
- TrueColor(0x001f81),
- TrueColor(0x566081),
- TrueColor(0x001968),
- TrueColor(0x454e68),
- TrueColor(0x00134f),
- TrueColor(0x353b4f),
- TrueColor(0x0000ff),
- TrueColor(0xaaaaff),
- TrueColor(0x0000bd),
- TrueColor(0x7e7ebd),
- TrueColor(0x000081),
- TrueColor(0x565681),
- TrueColor(0x000068),
- TrueColor(0x454568),
- TrueColor(0x00004f),
- TrueColor(0x35354f),
- TrueColor(0x3f00ff),
- TrueColor(0xbfaaff),
- TrueColor(0x2e00bd),
- TrueColor(0x8d7ebd),
- TrueColor(0x1f0081),
- TrueColor(0x605681),
- TrueColor(0x190068),
- TrueColor(0x4e4568),
- TrueColor(0x13004f),
- TrueColor(0x3b354f),
- TrueColor(0x7f00ff),
- TrueColor(0xd4aaff),
- TrueColor(0x5e00bd),
- TrueColor(0x9d7ebd),
- TrueColor(0x400081),
- TrueColor(0x6b5681),
- TrueColor(0x340068),
- TrueColor(0x564568),
- TrueColor(0x27004f),
- TrueColor(0x42354f),
- TrueColor(0xbf00ff),
- TrueColor(0xeaaaff),
- TrueColor(0x8d00bd),
- TrueColor(0xad7ebd),
- TrueColor(0x600081),
- TrueColor(0x765681),
- TrueColor(0x4e0068),
- TrueColor(0x5f4568),
- TrueColor(0x3b004f),
- TrueColor(0x49354f),
- TrueColor(0xff00ff),
- TrueColor(0xffaaff),
- TrueColor(0xbd00bd),
- TrueColor(0xbd7ebd),
- TrueColor(0x810081),
- TrueColor(0x815681),
- TrueColor(0x680068),
- TrueColor(0x684568),
- TrueColor(0x4f004f),
- TrueColor(0x4f354f),
- TrueColor(0xff00bf),
- TrueColor(0xffaaea),
- TrueColor(0xbd008d),
- TrueColor(0xbd7ead),
- TrueColor(0x810060),
- TrueColor(0x815676),
- TrueColor(0x68004e),
- TrueColor(0x68455f),
- TrueColor(0x4f003b),
- TrueColor(0x4f3549),
- TrueColor(0xff007f),
- TrueColor(0xffaad4),
- TrueColor(0xbd005e),
- TrueColor(0xbd7e9d),
- TrueColor(0x810040),
- TrueColor(0x81566b),
- TrueColor(0x680034),
- TrueColor(0x684556),
- TrueColor(0x4f0027),
- TrueColor(0x4f3542),
- TrueColor(0xff003f),
- TrueColor(0xffaabf),
- TrueColor(0xbd002e),
- TrueColor(0xbd7e8d),
- TrueColor(0x81001f),
- TrueColor(0x815660),
- TrueColor(0x680019),
- TrueColor(0x68454e),
- TrueColor(0x4f0013),
- TrueColor(0x4f353b),
- TrueColor(0x333333),
- TrueColor(0x505050),
- TrueColor(0x696969),
- TrueColor(0x828282),
- TrueColor(0xbebebe),
- TrueColor(0xffffff),
-]
diff --git a/scripts/py/loaders/dxfgrabber/const.py b/scripts/py/loaders/dxfgrabber/const.py
deleted file mode 100644
index fc3a747..0000000
--- a/scripts/py/loaders/dxfgrabber/const.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Purpose: constant values
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-ENV_CYTHON = 'DXFGRABBER_CYTHON'
-
-BYBLOCK = 0
-BYLAYER = 256
-
-XTYPE_NONE = 0
-XTYPE_2D = 1
-XTYPE_3D = 2
-XTYPE_2D_3D = 3
-
-acadrelease = {
- 'AC1009': 'R12',
- 'AC1012': 'R13',
- 'AC1014': 'R14',
- 'AC1015': 'R2000',
- 'AC1018': 'R2004',
- 'AC1021': 'R2007',
- 'AC1024': 'R2010',
-}
-
-dxfversion = {
- acad: dxf for dxf, acad in acadrelease.items()
-}
-
-# Entity: Polyline, Polymesh
-# 70 flags
-POLYLINE_CLOSED = 1
-POLYLINE_MESH_CLOSED_M_DIRECTION = POLYLINE_CLOSED
-POLYLINE_CURVE_FIT_VERTICES_ADDED = 2
-POLYLINE_SPLINE_FIT_VERTICES_ADDED = 4
-POLYLINE_3D_POLYLINE = 8
-POLYLINE_3D_POLYMESH = 16
-POLYLINE_MESH_CLOSED_N_DIRECTION = 32
-POLYLINE_POLYFACE = 64
-POLYLINE_GENERATE_LINETYPE_PATTERN =128
-
-# Entity: Polymesh
-# 75 surface smooth type
-POLYMESH_NO_SMOOTH = 0
-POLYMESH_QUADRIC_BSPLINE = 5
-POLYMESH_CUBIC_BSPLINE = 6
-POLYMESH_BEZIER_SURFACE = 8
-
-#Entity: Vertex
-# 70 flags
-VERTEXNAMES = ('vtx0', 'vtx1', 'vtx2', 'vtx3')
-VTX_EXTRA_VERTEX_CREATED = 1 ## Extra vertex created by curve-fitting
-VTX_CURVE_FIT_TANGENT = 2 ## Curve-fit tangent defined for this vertex.
-## A curve-fit tangent direction of 0 may be omitted from the DXF output, but is
-## significant if this bit is set.
-## 4 = unused, never set in dxf files
-VTX_SPLINE_VERTEX_CREATED = 8 ##Spline vertex created by spline-fitting
-VTX_SPLINE_FRAME_CONTROL_POINT = 16
-VTX_3D_POLYLINE_VERTEX = 32
-VTX_3D_POLYGON_MESH_VERTEX = 64
-VTX_3D_POLYFACE_MESH_VERTEX = 128
-
-VERTEX_FLAGS = {
- 'polyline2d': 0,
- 'polyline3d': VTX_3D_POLYLINE_VERTEX,
- 'polymesh': VTX_3D_POLYGON_MESH_VERTEX,
- 'polyface': VTX_3D_POLYGON_MESH_VERTEX | VTX_3D_POLYFACE_MESH_VERTEX,
-}
-POLYLINE_FLAGS = {
- 'polyline2d': 0,
- 'polyline3d': POLYLINE_3D_POLYLINE,
- 'polymesh': POLYLINE_3D_POLYMESH,
- 'polyface': POLYLINE_POLYFACE,
-}
-
-#---block-type flags (bit coded values, may be combined):
-# Entity: BLOCK
-# 70 flags
-BLK_ANONYMOUS = 1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application
-BLK_NON_CONSTANT_ATTRIBUTES = 2 # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all)
-BLK_XREF = 4 # This block is an external reference (xref)
-BLK_XREF_OVERLAY = 8 # This block is an xref overlay
-BLK_EXTERNAL = 16 # This block is externally dependent
-BLK_RESOLVED = 32 # This is a resolved external reference, or dependent of an external reference (ignored on input)
-BLK_REFERENCED = 64 # This definition is a referenced external reference (ignored on input)
-
-LWPOLYLINE_CLOSED = 1
-LWPOLYLINE_PLINEGEN = 128
-
-SPLINE_CLOSED = 1
-SPLINE_PERIODIC = 2
-SPLINE_RATIONAL = 4
-SPLINE_PLANAR = 8
-SPLINE_LINEAR = 16 # planar bit is also set
-
-MTEXT_TOP_LEFT = 1
-MTEXT_TOP_CENTER = 2
-MTEXT_TOP_RIGHT = 3
-MTEXT_MIDDLE_LEFT = 4
-MTEXT_MIDDLE_CENTER = 5
-MTEXT_MIDDLE_RIGHT = 6
-MTEXT_BOTTOM_LEFT = 7
-MTEXT_BOTTOM_CENTER = 8
-MTEXT_BOTTOM_RIGHT = 9
-
-MTEXT_LEFT_TO_RIGHT = 1
-MTEXT_TOP_TO_BOTTOM = 2
-MTEXT_BY_STYLE = 5
diff --git a/scripts/py/loaders/dxfgrabber/decode.py b/scripts/py/loaders/dxfgrabber/decode.py
deleted file mode 100644
index 57ee424..0000000
--- a/scripts/py/loaders/dxfgrabber/decode.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Purpose: decode DXF proprietary data
-# Created: 01.05.2014
-# Copyright (C) 2014, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from . import PYTHON3
-
-_replacement_table = {
- 0x20: ' ',
- 0x40: '_',
- 0x5F: '@',
-}
-for c in range(0x41, 0x5F):
- _replacement_table[c] = chr(0x41 + (0x5E - c)) # 0x5E -> 'A', 0x5D->'B', ...
-
-
-def decode(text_lines):
- def _decode(text):
- s = []
- skip = False
- if PYTHON3:
- text = bytes(text, 'ascii')
- else:
- text = map(ord, text)
-
- for c in text:
- if skip:
- skip = False
- continue
- if c in _replacement_table:
- s += _replacement_table[c]
- skip = (c == 0x5E) # skip space after 'A'
- else:
- s += chr(c ^ 0x5F)
- return ''.join(s)
- return [_decode(line) for line in text_lines]
diff --git a/scripts/py/loaders/dxfgrabber/defaultchunk.py b/scripts/py/loaders/dxfgrabber/defaultchunk.py
deleted file mode 100644
index fda30fa..0000000
--- a/scripts/py/loaders/dxfgrabber/defaultchunk.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Purpose: handle default chunk
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from .tags import Tags, DXFTag
-
-
-class DefaultChunk(object):
- def __init__(self, tags):
- assert isinstance(tags, Tags)
- self.tags = tags
-
- @staticmethod
- def from_tags(tags, drawing):
- return DefaultChunk(tags)
-
- @property
- def name(self):
- return self.tags[1].value.lower()
-
-
-def iterchunks(tagreader, stoptag='EOF', endofchunk='ENDSEC'):
- while True:
- tag = next(tagreader)
- if tag == DXFTag(0, stoptag):
- return
-
- tags = Tags([tag])
- append = tags.append
- end_tag = DXFTag(0, endofchunk)
- while tag != end_tag:
- tag = next(tagreader)
- append(tag)
- yield tags
diff --git a/scripts/py/loaders/dxfgrabber/drawing.py b/scripts/py/loaders/dxfgrabber/drawing.py
deleted file mode 100644
index 7ded631..0000000
--- a/scripts/py/loaders/dxfgrabber/drawing.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Purpose: handle drawing data of DXF files
-# Created: 21.07.12
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-
-__author__ = "mozman "
-
-from .tags import stream_tagger
-from .sections import Sections
-
-DEFAULT_OPTIONS = {
- "grab_blocks": True, # import block definitions True=yes, False=No
- "assure_3d_coords": False, # guarantees (x, y, z) tuples for ALL coordinates
- "resolve_text_styles": True, # Text, Attrib, Attdef and MText attributes will be set by the associated text style if necessary
-}
-
-
-class Drawing(object):
- def __init__(self, stream, options=None):
- if options is None:
- options = DEFAULT_OPTIONS
- self.grab_blocks = options.get('grab_blocks', True)
- self.assure_3d_coords = options.get('assure_3d_coords', False)
- self.resolve_text_styles = options.get('resolve_text_styles', True)
-
- tagreader = stream_tagger(stream, self.assure_3d_coords)
- self.dxfversion = 'AC1009'
- self.encoding = 'cp1252'
- self.filename = None
- sections = Sections(tagreader, self)
- self.header = sections.header
- self.layers = sections.tables.layers
- self.styles = sections.tables.styles
- self.linetypes = sections.tables.linetypes
- self.blocks = sections.blocks
- self.entities = sections.entities
- self.objects = sections.objects if ('objects' in sections) else []
- if 'acdsdata' in sections:
- self.acdsdata = sections.acdsdata
- # sab data introduced with DXF version AC1027 (R2013)
- if self.dxfversion >= 'AC1027':
- self.collect_sab_data()
-
- if self.resolve_text_styles:
- resolve_text_styles(self.entities, self.styles)
- for block in self.blocks:
- resolve_text_styles(block, self.styles)
-
- def modelspace(self):
- return (entity for entity in self.entities if not entity.paperspace)
-
- def paperspace(self):
- return (entity for entity in self.entities if entity.paperspace)
-
- def collect_sab_data(self):
- for entity in self.entities:
- if hasattr(entity, 'set_sab_data'):
- sab_data = self.acdsdata.sab_data[entity.handle]
- entity.set_sab_data(sab_data)
-
-
-def resolve_text_styles(entities, text_styles):
- for entity in entities:
- if hasattr(entity, 'resolve_text_style'):
- entity.resolve_text_style(text_styles)
\ No newline at end of file
diff --git a/scripts/py/loaders/dxfgrabber/dxfentities.py b/scripts/py/loaders/dxfgrabber/dxfentities.py
deleted file mode 100644
index c8770ca..0000000
--- a/scripts/py/loaders/dxfgrabber/dxfentities.py
+++ /dev/null
@@ -1,1352 +0,0 @@
-# encoding: utf-8
-# Purpose: entity classes, new implementation without dxf12/dxf13 layer
-# Created: 17.04.2016
-# Copyright (C) 2016, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-import math
-
-from . import const
-from .color import TrueColor
-from .styles import default_text_style
-from .decode import decode
-
-SPECIAL_CHARS = {
- 'd': '°'
-}
-
-
-basic_attribs = {
- 5: 'handle',
- 6: 'linetype',
- 8: 'layer',
- 39: 'thickness',
- 48: 'ltscale',
- 62: 'color',
- 67: 'paperspace',
- 210: 'extrusion',
- 284: 'shadow_mode',
- 330: 'owner',
- 370: 'line_weight',
- 410: 'layout_tab_name',
-}
-
-
-class DXFEntity(object):
- def __init__(self):
- self.dxftype = 'ENTITY'
- self.handle = None
- self.owner = None
- self.paperspace = None
- self.layer = '0'
- self.linetype = None
- self.thickness = 0.0
- self.extrusion = None
- self.ltscale = 1.0
- self.line_weight = 0
- self.invisible = 0
- self.color = const.BYLAYER
- self.true_color = None
- self.transparency = None
- self.shadow_mode = None
- self.layout_tab_name = None
-
- def setup_attributes(self, tags):
- self.dxftype = tags.get_type()
- for code, value in tags.plain_tags():
- if code in basic_attribs:
- self.__setattr__(basic_attribs[code], value)
- elif code == 420:
- self.true_color = TrueColor(value)
- elif code == 440:
- self.transparency = 1. - float(value & 0xFF) / 255.
- else:
- yield code, value # chain of generators
-
- def set_default_extrusion(self): # call only for 2d entities with extrusion vector
- if self.extrusion is None:
- self.extrusion = (0., 0., 1.)
-
- def __str__(self):
- return "{} [{}]".format(self.dxftype, self.handle)
-
-
-class Point(DXFEntity):
- def __init__(self):
- super(Point, self).__init__()
- self.point = (0, 0, 0)
-
- def setup_attributes(self, tags):
- for code, value in super(Point, self).setup_attributes(tags):
- if code == 10:
- self.point = value
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
-
-class Line(DXFEntity):
- def __init__(self):
- super(Line, self).__init__()
- self.start = (0, 0, 0)
- self.end = (0, 0, 0)
-
- def setup_attributes(self, tags):
- for code, value in super(Line, self).setup_attributes(tags):
- if code == 10:
- self.start = value
- elif code == 11:
- self.end = value
- else:
- yield code, value # chain of generators
-
-
-class Circle(DXFEntity):
- def __init__(self):
- super(Circle, self).__init__()
- self.center = (0, 0, 0)
- self.radius = 1.0
-
- def setup_attributes(self, tags):
- for code, value in super(Circle, self).setup_attributes(tags):
- if code == 10:
- self.center = value
- elif code == 40:
- self.radius = value
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
-
-class Arc(Circle):
- def __init__(self):
- super(Arc, self).__init__()
- self.start_angle = 0.
- self.end_angle = 360.
-
- def setup_attributes(self, tags):
- for code, value in super(Arc, self).setup_attributes(tags):
- if code == 50:
- self.start_angle = value
- elif code == 51:
- self.end_angle = value
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
-TRACE_CODES = frozenset((10, 11, 12, 13))
-
-
-class Trace(DXFEntity):
- def __init__(self):
- super(Trace, self).__init__()
- self.points = []
-
- def setup_attributes(self, tags):
- for code, value in super(Trace, self).setup_attributes(tags):
- if code in TRACE_CODES:
- self.points.append(value)
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
-
-Solid = Trace
-
-
-class Face(Trace):
- def __init__(self):
- super(Face, self).__init__()
- self.points = []
- self.invisible_edge = 0
-
- def setup_attributes(self, tags):
- for code, value in super(Face, self).setup_attributes(tags):
- if code == 70:
- self.invisible_edge = value
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
- def is_edge_invisible(self, edge):
- # edges 0 .. 3
- return bool(self.invisible_edge & (1 << edge))
-
-
-class Text(DXFEntity):
- def __init__(self):
- super(Text, self).__init__()
- self.insert = (0., 0.)
- self.height = 1.0
- self.text = ""
- self.rotation = 0.
- self.oblique = 0.
- self.style = "STANDARD"
- self.width = 1.
- self.is_backwards = False
- self.is_upside_down = False
- self.halign = 0
- self.valign = 0
- self.align_point = None
- self.font = ""
- self.big_font = ""
-
- def setup_attributes(self, tags):
- for code, value in super(Text, self).setup_attributes(tags):
- if code == 10:
- self.insert = value
- elif code == 11:
- self.align_point = value
- elif code == 1:
- self.text = value
- elif code == 7:
- self.style = value
- elif code == 40:
- self.height = value
- elif code == 41:
- self.width = value
- elif code == 50:
- self.rotation = value
- elif code == 51:
- self.oblique = value
- elif code == 71:
- self.is_backwards = bool(value & 2)
- self.is_upside_down = bool(value & 4)
- elif code == 72:
- self.halign = value
- elif code == 73:
- self.valign = value
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
- def resolve_text_style(self, text_styles):
- style = text_styles.get(self.style, None)
- if style is None:
- style = default_text_style
- if self.height == 0:
- self.height = style.height
- if self.width == 0:
- self.width = style.width
- if self.oblique is None:
- self.oblique = style.oblique
- if self.is_backwards is None:
- self.is_backwards = style.is_backwards
- if self.is_upside_down is None:
- self.is_upside_down = style.is_upside_down
- if self.font is None:
- self.font = style.font
- if self.big_font is None:
- self.big_font = style.big_font
-
- def plain_text(self):
- chars = []
- raw_chars = list(reversed(self.text)) # text splitted into chars, in reversed order for efficient pop()
- while len(raw_chars):
- char = raw_chars.pop()
- if char == '%': # formatting codes and special characters
- if len(raw_chars) and raw_chars[-1] == '%':
- raw_chars.pop() # '%'
- if len(raw_chars):
- special_char = raw_chars.pop() # command char
- chars.append(SPECIAL_CHARS.get(special_char, ""))
- else: # char is just a single '%'
- chars.append(char)
- else: # char is what it is, a character
- chars.append(char)
- return "".join(chars)
-
-
-class Attrib(Text):
- def __init__(self):
- super(Attrib, self).__init__()
- self.field_length = 0
- self.tag = ""
-
- def setup_attributes(self, tags):
- for code, value in super(Attrib, self).setup_attributes(tags):
- if code == 2:
- self.tag = value
- elif code == 73:
- self.field_length = value
- else:
- yield code, value
-
-
-class Insert(DXFEntity):
- def __init__(self):
- super(Insert, self).__init__()
- self.name = ""
- self.insert = (0., 0., 0.)
- self.rotation = 0.
- self.scale = (1., 1., 1.)
- self.row_count = 1
- self.row_spacing = 0.
- self.col_count = 1
- self.col_spacing = 0.
- self.attribsfollow = False
- self.attribs = []
-
- def setup_attributes(self, tags):
- xscale = 1.
- yscale = 1.
- zscale = 1.
- for code, value in super(Insert, self).setup_attributes(tags):
- if code == 2:
- self.name = value
- elif code == 10:
- self.insert = value
- elif code == 41:
- xscale = value
- elif code == 42:
- yscale = value
- elif code == 43:
- zscale = value
- elif code == 44:
- self.col_spacing = value
- elif code == 45:
- self.row_spacing = value
- elif code == 50:
- self.rotation = value
- elif code == 66:
- self.attribsfollow = bool(value)
- elif code == 70:
- self.col_count = value
- elif code == 71:
- self.row_count = value
- else:
- yield code, value # chain of generators
- self.scale = (xscale, yscale, zscale)
- self.set_default_extrusion()
-
- def find_attrib(self, attrib_tag):
- for attrib in self.attribs:
- if attrib.tag == attrib_tag:
- return attrib
- return None
-
- def append_data(self, attribs):
- self.attribs = attribs
-
-
-class Polyline(DXFEntity):
- LINE_TYPES = frozenset(('spline2d', 'polyline2d', 'polyline3d'))
-
- def __init__(self):
- super(Polyline, self).__init__()
- self.vertices = [] # set in append data
- self.points = [] # set in append data
- self.control_points = [] # set in append data
- self.width = [] # set in append data
- self.bulge = [] # set in append data
- self.tangents = [] # set in append data
- self.flags = 0
- self.mode = 'polyline2d'
- self.mcount = 0
- self.ncount = 0
- self.default_start_width = 0.
- self.default_end_width = 0.
- self.is_mclosed = False
- self.is_nclosed = False
- self.is_closed = False
- self.elevation = (0., 0., 0.)
- self.m_smooth_density = 0.
- self.n_smooth_density = 0.
- self.smooth_type = 0
- self.spline_type = None
-
- def setup_attributes(self, tags):
- def get_mode():
- flags = self.flags
- if flags & const.POLYLINE_SPLINE_FIT_VERTICES_ADDED:
- return 'spline2d'
- elif flags & const.POLYLINE_3D_POLYLINE:
- return 'polyline3d'
- elif flags & const.POLYLINE_3D_POLYMESH:
- return 'polymesh'
- elif flags & const.POLYLINE_POLYFACE:
- return 'polyface'
- else:
- return 'polyline2d'
-
- for code, value in super(Polyline, self).setup_attributes(tags):
- if code == 10:
- self.elevation = value
- elif code == 40:
- self.default_start_width = value
- elif code == 41:
- self.default_end_width = value
- elif code == 70:
- self.flags = value
- elif code == 71:
- self.mcount = value
- elif code == 72:
- self.ncount = value
- elif code == 73:
- self.m_smooth_density = value
- elif code == 73:
- self.n_smooth_density = value
- elif code == 75:
- self.smooth_type = value
- else:
- yield code, value # chain of generators
-
- self.mode = get_mode()
- if self.mode == 'spline2d':
- if self.smooth_type == const.POLYMESH_CUBIC_BSPLINE:
- self.spline_type = 'cubic_bspline'
- elif self.smooth_type == const.POLYMESH_QUADRIC_BSPLINE:
- self.spline_type = 'quadratic_bspline'
- elif self.smooth_type == const.POLYMESH_BEZIER_SURFACE:
- self.spline_type = 'bezier_curve' # is this a valid spline type for DXF12?
- self.is_mclosed = bool(self.flags & const.POLYLINE_MESH_CLOSED_M_DIRECTION)
- self.is_nclosed = bool(self.flags & const.POLYLINE_MESH_CLOSED_N_DIRECTION)
- self.is_closed = self.is_mclosed
- self.set_default_extrusion()
-
- def __len__(self):
- return len(self.vertices)
-
- def __getitem__(self, item):
- return self.vertices[item]
-
- def __iter__(self):
- return iter(self.vertices)
-
- def append_data(self, vertices):
- def default_width(start_width, end_width):
- if start_width == 0.:
- start_width = self.default_start_width
- if end_width == 0.:
- end_width = self.default_end_width
- return start_width, end_width
-
- self.vertices = vertices
- if self.mode in Polyline.LINE_TYPES:
- for vertex in self.vertices:
- if vertex.flags & const.VTX_SPLINE_FRAME_CONTROL_POINT:
- self.control_points.append(vertex.location)
- else:
- self.points.append(vertex.location)
- self.width.append(default_width(vertex.start_width, vertex.end_width))
- self.bulge.append(vertex.bulge)
- self.tangents.append(vertex.tangent if vertex.flags & const.VTX_CURVE_FIT_TANGENT else None)
-
- def cast(self):
- if self.mode == 'polyface':
- return PolyFace(self)
- elif self.mode == 'polymesh':
- return PolyMesh(self)
- else:
- return self
-
-
-class SubFace(object):
- def __init__(self, face_record, vertices):
- self._vertices = vertices
- self.face_record = face_record
-
- def __len__(self):
- return len(self.face_record.vtx)
-
- def __getitem__(self, item):
- return self._vertices[self._vertex_index(item)]
-
- def __iter__(self):
- return (self._vertices[index].location for index in self.indices())
-
- def _vertex_index(self, pos):
- return abs(self.face_record.vtx[pos]) - 1
-
- def indices(self):
- return tuple(abs(i)-1 for i in self.face_record.vtx if i != 0)
-
- def is_edge_visible(self, pos):
- return self.face_record.vtx[pos] > 0
-
-
-class PolyShape(object):
- def __init__(self, polyline, dxftype):
- # copy all dxf attributes from polyline
- for key, value in polyline.__dict__.items():
- self.__dict__[key] = value
- self.dxftype = dxftype
-
- def __str__(self):
- return "{} [{}]".format(self.dxftype, self.handle)
-
-
-class PolyFace(PolyShape):
- def __init__(self, polyline):
- VERTEX_FLAGS = const.VTX_3D_POLYFACE_MESH_VERTEX + const.VTX_3D_POLYGON_MESH_VERTEX
-
- def is_vertex(flags):
- return flags & VERTEX_FLAGS == VERTEX_FLAGS
-
- super(PolyFace, self).__init__(polyline, 'POLYFACE')
- vertices = []
- face_records = []
- for vertex in polyline.vertices:
- (vertices if is_vertex(vertex.flags) else face_records).append(vertex)
-
- self._face_records = face_records
-
- def __getitem__(self, item):
- return SubFace(self._face_records[item], self.vertices)
-
- def __len__(self):
- return len(self._face_records)
-
- def __iter__(self):
- return (SubFace(f, self.vertices) for f in self._face_records)
-
-
-class PolyMesh(PolyShape):
- def __init__(self, polyline):
- super(PolyMesh, self).__init__(polyline, 'POLYMESH')
-
- def __iter__(self):
- return iter(self.vertices)
-
- def get_location(self, pos):
- return self.get_vertex(pos).location
-
- def get_vertex(self, pos):
- m, n = pos
- if 0 <= m < self.mcount and 0 <= n < self.ncount:
- pos = m * self.ncount + n
- return self.vertices[pos]
- else:
- raise IndexError(repr(pos))
-
-
-class Vertex(DXFEntity):
- def __init__(self):
- super(Vertex, self).__init__()
- self.location = (0., 0., 0.)
- self.flags = 0
- self.start_width = 0.
- self.end_width = 0.
- self.bulge = 0.
- self.tangent = None
- self.vtx = None
-
- def setup_attributes(self, tags):
- vtx0 = 0
- vtx1 = 0
- vtx2 = 0
- vtx3 = 0
- for code, value in super(Vertex, self).setup_attributes(tags):
- if code == 10:
- self.location = value
- elif code == 40:
- self.start_width = value
- elif code == 41:
- self.end_width = value
- elif code == 42:
- self.bulge = value
- elif code == 50:
- self.tangent = value
- elif code == 70:
- self.flags = value
- elif code == 71:
- vtx0 = value
- elif code == 72:
- vtx1 = value
- elif code == 73:
- vtx2 = value
- elif code == 74:
- vtx3 = value
- else:
- yield code, value # chain of generators
- indices = (vtx0, vtx1, vtx2, vtx3)
- if any(indices):
- self.vtx = indices
-
- def __getitem__(self, item):
- return self.location[item]
-
- def __iter__(self):
- return iter(self.location)
-
-
-class Block(DXFEntity):
- def __init__(self):
- super(Block, self).__init__()
- self.basepoint = (0, 0, 0)
- self.name = ''
- self.description = ''
- self.flags = 0
- self.xrefpath = ""
- self._entities = []
-
- def setup_attributes(self, tags):
- for code, value in super(Block, self).setup_attributes(tags):
- if code == 2:
- self.name = value
- elif code == 4:
- self.description = value
- elif code == 1:
- self.xrefpath = value
- elif code == 10:
- self.basepoint = value
- elif code == 70:
- self.flags = value
- else:
- yield code, value # chain of generators
-
- @property
- def is_xref(self):
- return bool(self.flags & const.BLK_XREF)
-
- @property
- def is_xref_overlay(self):
- return bool(self.flags & const.BLK_XREF_OVERLAY)
-
- @property
- def is_anonymous(self):
- return bool(self.flags & const.BLK_ANONYMOUS)
-
- def set_entities(self, entities):
- self._entities = entities
-
- def __iter__(self):
- return iter(self._entities)
-
- def __getitem__(self, item):
- return self._entities[item]
-
- def __len__(self):
- return len(self._entities)
-
-
-class LWPolyline(DXFEntity):
- def __init__(self):
- super(LWPolyline, self).__init__()
- self.points = []
- self.width = []
- self.bulge = []
- self.elevation = 0.
- self.const_width = 0.
- self.flags = 0
-
- def setup_attributes(self, tags):
- bulge, start_width, end_width = 0., 0., 0.
- init = True
-
- for code, value in super(LWPolyline, self).setup_attributes(tags):
- if code == 10:
- if not init:
- self.bulge.append(bulge)
- self.width.append((start_width, end_width))
- bulge, start_width, end_width = 0., 0., 0.
- self.points.append(value)
- init = False
- elif code == 40:
- start_width = value
- elif code == 41:
- end_width = value
- elif code == 42:
- bulge = value
- elif code == 38:
- self.elevation = value
- elif code == 39:
- self.thickness = value
- elif code == 43:
- self.const_width = value
- elif code == 70:
- self.flags = value
- elif code == 210:
- self.extrusion = value
- else:
- yield code, value # chain of generators
-
- # add values for the last point
- self.bulge.append(bulge)
- self.width.append((start_width, end_width))
-
- if self.const_width != 0.:
- self.width = []
- self.set_default_extrusion()
-
- @property
- def is_closed(self):
- return bool(self.flags & 1)
-
- def __len__(self):
- return len(self.points)
-
- def __getitem__(self, item):
- return self.points[item]
-
- def __iter__(self):
- return iter(self.points)
-
-
-class Ellipse(DXFEntity):
- def __init__(self):
- super(Ellipse, self).__init__()
- self.center = (0., 0., 0.)
- self.major_axis = (1., 0., 0.)
- self.ratio = 1.0
- self.start_param = 0.
- self.end_param = 6.283185307179586
-
- def setup_attributes(self, tags):
- for code, value in super(Ellipse, self).setup_attributes(tags):
- if code == 10:
- self.center = value
- elif code == 11:
- self.major_axis = value
- elif code == 40:
- self.ratio = value
- elif code == 41:
- self.start_param = value
- elif code == 42:
- self.end_param = value
- else:
- yield code, value # chain of generators
- self.set_default_extrusion()
-
-
-class Ray(DXFEntity):
- def __init__(self):
- super(Ray, self).__init__()
- self.start = (0, 0, 0)
- self.unit_vector = (1, 0, 0)
-
- def setup_attributes(self, tags):
- for code, value in super(Ray, self).setup_attributes(tags):
- if code == 10:
- self.start = value
- elif code == 11:
- self.unit_vector = value
- else:
- yield code, value # chain of generators
-
-
-def deg2vec(deg):
- rad = float(deg) * math.pi / 180.0
- return math.cos(rad), math.sin(rad), 0.
-
-
-def normalized(vector):
- x, y, z = vector
- m = (x**2 + y**2 + z**2)**0.5
- return x/m, y/m, z/m
-
-##################################################
-# MTEXT inline codes
-# \L Start underline
-# \l Stop underline
-# \O Start overstrike
-# \o Stop overstrike
-# \K Start strike-through
-# \k Stop strike-through
-# \P New paragraph (new line)
-# \pxi Control codes for bullets, numbered paragraphs and columns
-# \X Paragraph wrap on the dimension line (only in dimensions)
-# \Q Slanting (obliquing) text by angle - e.g. \Q30;
-# \H Text height - e.g. \H3x;
-# \W Text width - e.g. \W0.8x;
-# \F Font selection
-#
-# e.g. \Fgdt;o - GDT-tolerance
-# e.g. \Fkroeger|b0|i0|c238|p10 - font Kroeger, non-bold, non-italic, codepage 238, pitch 10
-#
-# \S Stacking, fractions
-#
-# e.g. \SA^B:
-# A
-# B
-# e.g. \SX/Y:
-# X
-# -
-# Y
-# e.g. \S1#4:
-# 1/4
-#
-# \A Alignment
-#
-# \A0; = bottom
-# \A1; = center
-# \A2; = top
-#
-# \C Color change
-#
-# \C1; = red
-# \C2; = yellow
-# \C3; = green
-# \C4; = cyan
-# \C5; = blue
-# \C6; = magenta
-# \C7; = white
-#
-# \T Tracking, char.spacing - e.g. \T2;
-# \~ Non-wrapping space, hard space
-# {} Braces - define the text area influenced by the code
-# \ Escape character - e.g. \\ = "\", \{ = "{"
-#
-# Codes and braces can be nested up to 8 levels deep
-
-ESCAPED_CHARS = "\\{}"
-GROUP_CHARS = "{}"
-ONE_CHAR_COMMANDS = "PLlOoKkX"
-
-
-class MText(DXFEntity):
- def __init__(self):
- super(MText, self).__init__()
- self.insert = (0., 0., 0.)
- self.raw_text = ""
- self.height = 0.
- self.rect_width = None
- self.horizontal_width = None
- self.vertical_height = None
- self.line_spacing = 1.
- self.attachment_point = 1
- self.style = 'STANDARD'
- self.xdirection = (1., 0., 0.)
- self.font = None
- self.big_font = None
-
- def setup_attributes(self, tags):
- text = ""
- lines = []
- rotation = 0.
- xdir = None
- for code, value in super(MText, self).setup_attributes(tags):
- if code == 10:
- self.insert = value
- elif code == 11:
- xdir = value
- elif code == 1:
- text = value
- elif code == 3:
- lines.append(value)
- elif code == 7:
- self.style = value
- elif code == 40:
- self.height = value
- elif code == 41:
- self.rect_width = value
- elif code == 42:
- self.horizontal_width = value
- elif code == 43:
- self.vertical_height = value
- elif code == 44:
- self.line_spacing = value
- elif code == 50:
- rotation = value
- elif code == 71:
- self.attachment_point = value
- else:
- yield code, value # chain of generators
-
- lines.append(text)
- self.raw_text = "".join(lines)
- if xdir is None:
- xdir = deg2vec(rotation)
- self.xdirection = normalized(xdir)
- self.set_default_extrusion()
-
- def lines(self):
- return self.raw_text.split('\P')
-
- def plain_text(self, split=False):
- chars = []
- raw_chars = list(reversed(self.raw_text)) # text splitted into chars, in reversed order for efficient pop()
- while len(raw_chars):
- char = raw_chars.pop()
- if char == '\\': # is a formatting command
- try:
- char = raw_chars.pop()
- except IndexError:
- break # premature end of text - just ignore
-
- if char in ESCAPED_CHARS: # \ { }
- chars.append(char)
- elif char in ONE_CHAR_COMMANDS:
- if char == 'P': # new line
- chars.append('\n')
- # discard other commands
- else: # more character commands are terminated by ';'
- stacking = char == 'S' # stacking command surrounds user data
- try:
- while char != ';': # end of format marker
- char = raw_chars.pop()
- if stacking and char != ';':
- chars.append(char) # append user data of stacking command
- except IndexError:
- break # premature end of text - just ignore
- elif char in GROUP_CHARS: # { }
- pass # discard group markers
- elif char == '%': # special characters
- if len(raw_chars) and raw_chars[-1] == '%':
- raw_chars.pop() # discard next '%'
- if len(raw_chars):
- special_char = raw_chars.pop()
- # replace or discard formatting code
- chars.append(SPECIAL_CHARS.get(special_char, ""))
- else: # char is just a single '%'
- chars.append(char)
- else: # char is what it is, a character
- chars.append(char)
-
- plain_text = "".join(chars)
- return plain_text.split('\n') if split else plain_text
-
- def resolve_text_style(self, text_styles):
- style = text_styles.get(self.style, None)
- if style is None:
- style = default_text_style
- if self.height == 0:
- self.height = style.height
- if self.font is None:
- self.font = style.font
- if self.big_font is None:
- self.big_font = style.font
-
-
-class Light(DXFEntity):
- def __init__(self):
- super(Light, self).__init__()
- self.version = 1
- self.name = ""
- self.light_type = 1 # distant = 1; point = 2; spot = 3
- self.status = False # on/off ?
- self.light_color = 0 # 0 is unset
- self.true_color = None # None is unset
- self.plot_glyph = 0
- self.intensity = 0
- self.position = (0., 0., 1.)
- self.target = (0., 0., 0.)
- self.attenuation_type = 0 # 0 = None; 1 = Inverse Linear; 2 = Inverse Square
- self.use_attenuation_limits = False
- self.attenuation_start_limit = 0
- self.attenuation_end_limit = 0
- self.hotspot_angle = 0
- self.fall_off_angle = 0.
- self.cast_shadows = False
- self.shadow_type = 0 # 0 = Ray traced shadows; 1 = Shadow maps
- self.shadow_map_size = 0
- self.shadow_softness = 0
-
- def setup_attributes(self, tags):
- for code, value in super(Light, self).setup_attributes(tags):
- if code == 1:
- self.name = value
- elif code == 10:
- self.position = value
- elif code == 11:
- self.target = value
- elif code == 40:
- self.intensity = value
- elif code == 41:
- self.attenuation_start_limit = value
- elif code == 42:
- self.attenuation_end_limit = value
- elif code == 50:
- self.hotspot_angle = value
- elif code == 51:
- self.fall_off_angle = value
- elif code == 63:
- self.light_color = value
- elif code == 70:
- self.light_type = value
- elif code == 72:
- self.attenuation_type = value
- elif code == 73:
- self.shadow_type = value
- elif code == 90:
- self.version = value
- elif code == 91:
- self.shadow_map_size = value
- elif code == 280:
- self.shadow_softness = value
- elif code == 290:
- self.status = value
- elif code == 291:
- self.plot_glyph = value
- elif code == 292:
- self.use_attenuation_limits = value
- elif code == 293:
- self.cast_shadows = value
- elif code == 421:
- self.true_color = value
- else:
- yield code, value # chain of generators
-
-
-class Body(DXFEntity):
- def __init__(self):
- super(Body, self).__init__()
- # need handle to get SAB data in DXF version AC1027 and later
- self.version = 1
- self.acis = []
-
- def setup_attributes(self, tags):
- sat = []
- for code, value in super(Body, self).setup_attributes(tags):
- if code == 70:
- self.version = value
- elif code in (1, 3):
- sat.append(value)
- else:
- yield code, value # chain of generators
- self.acis = decode(sat)
-
- def set_sab_data(self, sab_data):
- self.acis = sab_data
-
- @property
- def is_sat(self):
- return isinstance(self.acis, list) # but could be an empty list
-
- @property
- def is_sab(self):
- return not self.is_sat # has binary encoded ACIS data
-
-
-class Surface(Body):
- def __init__(self):
- super(Body, self).__init__()
- self.u_isolines = 0
- self.v_isolines = 0
-
- def setup_attributes(self, tags):
- for code, value in super(Surface, self).setup_attributes(tags):
- if code == 71:
- self.u_isolines = value
- elif code == 72:
- self.v_isolines = value
- else:
- yield code, value # chain of generators
-
-
-class Mesh(DXFEntity):
- def __init__(self):
- super(Mesh, self).__init__()
- self.version = 2
- self.blend_crease = False
- self.subdivision_levels = 1
- # rest are mostly positional tags
- self.vertices = []
- self.faces = []
- self.edges = []
- self.edge_crease_list = []
-
- def setup_attributes(self, tags):
- status = 0
- count = 0
- index_tags = []
- for code, value in super(Mesh, self).setup_attributes(tags):
- if code == 10:
- self.vertices.append(value)
- elif status == -1: # ignore overridden properties at the end of the mesh entity
- pass # property override uses also group codes 90, 91, 92 but only at the end of the MESH entity
- elif code == 71:
- self.version = value
- elif code == 72:
- self.blend_crease = bool(value)
- elif code == 91:
- self.subdivision_levels = value
- elif 92 <= code <= 95: # 92 = vertices, 93 = faces; 94 = edges 95 = edge creases
- if code in (92, 95):
- continue # ignore vertices and edge creases count
- status = code
- count = value
- if status == 94: # edge count
- count *= 2
- elif code == 140:
- self.edge_crease_list.append(value)
- elif code == 90 and count > 0: # faces or edges
- count -= 1
- index_tags.append(value)
- if count < 1:
- if status == 93:
- self.setup_faces(index_tags)
- elif status == 94:
- self.setup_edges(index_tags)
- index_tags = []
- elif code == 90: # count == 0; start of overridden properties (group code 90 after face or edge list)
- status = -1
- else:
- yield code, value # chain of generators
-
- def get_face(self, index):
- return tuple(self.vertices[vertex_index] for vertex_index in self.faces[index])
-
- def get_edge(self, index):
- return tuple(self.vertices[vertex_index] for vertex_index in self.edges[index])
-
- def setup_faces(self, tags):
- face = []
- count = 0
- for value in tags:
- if count == 0:
- if len(face):
- self.faces.append(tuple(face))
- del face[:]
- count = value
- else:
- count -= 1
- face.append(value)
-
- if len(face):
- self.faces.append(tuple(face))
-
- def setup_edges(self, tags):
- self.edges = list(zip(tags[::2], tags[1::2]))
-
-
-class Spline(DXFEntity):
- def __init__(self):
- super(Spline, self).__init__()
- self.normal_vector = None
- self.flags = 0
- self.degree = 3
- self.start_tangent = None
- self.end_tangent = None
- self.knots = []
- self.weights = []
- self.tol_knot = .0000001
- self.tol_control_point = .0000001
- self.tol_fit_point = .0000000001
- self.control_points = []
- self.fit_points = []
-
- def setup_attributes(self, tags):
- subclass = 'AcDbSpline'
- for code, value in super(Spline, self).setup_attributes(tags):
- if subclass == 'AcDbHelix':
- yield code, value # chain of generators
- elif code == 10:
- self.control_points.append(value)
- elif code == 11:
- self.fit_points.append(value)
- elif code == 12:
- self.start_tangent = value
- elif code == 13:
- self.end_tangent = value
- elif code == 40:
- self.knots.append(value)
- elif code == 41:
- self.weights.append(value)
- elif code == 42:
- self.tol_knot = value
- elif code == 43:
- self.tol_control_point = value
- elif code == 44:
- self.tol_fit_point = value
- elif code == 70:
- self.flags = value
- elif code == 71:
- self.degree = value
- elif 72 <= code < 75:
- pass # ignore knot-, control- and fit point count
- elif code == 100:
- subclass = value
- self.normal_vector = self.extrusion
- if len(self.weights) == 0:
- self.weights = [1.0] * len(self.control_points)
-
- @property
- def is_closed(self):
- return bool(self.flags & const.SPLINE_CLOSED)
-
- @property
- def is_periodic(self):
- return bool(self.flags & const.SPLINE_PERIODIC)
-
- @property
- def is_rational(self):
- return bool(self.flags & const.SPLINE_RATIONAL)
-
- @property
- def is_planar(self):
- return bool(self.flags & const.SPLINE_PLANAR)
-
- @property
- def is_linear(self):
- return bool(self.flags & const.SPLINE_LINEAR)
-
-
-class Helix(Spline):
- def __init__(self):
- super(Helix, self).__init__()
- self.helix_version = (1, 1)
- self.axis_base_point = None
- self.start_point = None
- self.axis_vector = None
- self.radius = 0
- self.turns = 0
- self.turn_height = 0
- self.handedness = 0 # 0 = left, 1 = right
- self.constrain = 0
- # 0 = Constrain turn height;
- # 1 = Constrain turns;
- # 2 = Constrain height
-
- def setup_attributes(self, tags):
- helix_major_version = 1
- helix_maintainance_version = 1
- for code, value in super(Helix, self).setup_attributes(tags):
- if code == 10:
- self.axis_base_point = value
- elif code == 11:
- self.start_point = value
- elif code == 12:
- self.axis_vector = value
- elif code == 90:
- helix_major_version = value
- elif code == 91:
- helix_maintainance_version = value
- elif code == 91:
- helix_maintainance_version = value
- elif code == 40:
- self.radius = value
- elif code == 41:
- self.turns = value
- elif code == 42:
- self.turn_height = value
- elif code == 290:
- self.handedness = value
- elif code == 280:
- self.constrain = value
- else:
- yield code, value # chain of generators
- self.helix_version = (helix_major_version, helix_maintainance_version)
-
-class Hatch(DXFEntity):
- def __init__(self):
- super(Hatch, self).__init__()
- self.pattern_name = ''
- self.style = 0
- self.pattern_type = 1
- self.pattern_angle = 0.0
- self.pattern_scale = 1.0
- self.pattern_double = 0
- self.solid_fill = 0
- self.pattern_fill_color = 7 # White
- self.number_of_pattern_line = 0
- self.number_of_boundries = 0
- #~ self.flags = 0
-
- def setup_attributes(self, tags):
- subclass = 'AcDbHatch'
- #~ bulge, start_width, end_width = 0., 0., 0.
- #~ init = True
- boundries = 0
-
- for code, value in super(Hatch, self).setup_attributes(tags):
- print "Hatch : ", code, value
-
- if code == 2:
- self.pattern_name = value
- elif code == 70:
- self.solid_fill = bool(value == 1)
- elif code == 71:
- pass
- elif code == 64:
- self.patter_fill_color = int(value)
- elif code == 91:
- self.number_of_boundries = int(value)
- boundries = int(value)
- # Boundry data
-
- elif code == 75:
- self.style = int(value)
- elif code == 76:
- self.pattern_type = int(value)
- elif code == 52:
- self.pattern_angle = float(value)
- elif code == 41:
- self.pattern_scale = float(value)
- elif code == 77:
- self.pattern_double = int(value)
- elif code == 78:
- self.number_of_pattern_line = int(value)
- # Pattern data
- else:
- print "? Hatch : ", code, value
- yield code, value # chain of generators
-
- #~ elif code == 38:
- #~ self.elevation = value
- #~ elif code == 39:
- #~ self.thickness = value
- #~ elif code == 43:
- #~ self.const_width = value
- #~ elif code == 70:
- #~ self.flags = value
- #~ elif code == 210:
- #~ self.extrusion = value
- #~ else:
- #~ yield code, value # chain of generators
-
- # add values for the last point
- #~ self.bulge.append(bulge)
- #~ self.width.append((start_width, end_width))
-
- #~ if self.const_width != 0.:
- #~ self.width = []
- #~ self.set_default_extrusion()
-
- @property
- def is_solid(self):
- return bool(self.solid_fill)
-
- #~ def __len__(self):
- #~ return len(self.points)
-
- #~ def __getitem__(self, item):
- #~ return self.points[item]
-
- #~ def __iter__(self):
- #~ return iter(self.points)
-
-EntityTable = {
- 'LINE': Line,
- 'POINT': Point,
- 'CIRCLE': Circle,
- 'ARC': Arc,
- 'TRACE': Trace,
- 'SOLID': Solid,
- '3DFACE': Face,
- 'TEXT': Text,
- 'INSERT': Insert,
- 'SEQEND': DXFEntity,
- 'ATTRIB': Attrib,
- 'ATTDEF': Attrib,
- 'POLYLINE': Polyline,
- 'VERTEX': Vertex,
- 'BLOCK': Block,
- 'ENDBLK': DXFEntity,
- 'LWPOLYLINE': LWPolyline,
- 'ELLIPSE': Ellipse,
- 'RAY': Ray,
- 'XLINE': Ray,
- 'SPLINE': Spline,
- 'HELIX': Helix,
- 'HATCH': Hatch,
- 'MTEXT': MText,
- 'MESH': Mesh,
- 'LIGHT': Light,
- 'BODY': Body,
- 'REGION': Body,
- '3DSOLID': Body,
- 'SURFACE': Surface,
- 'PLANESURFACE': Surface,
-}
-
-
-def entity_factory(tags):
- dxftype = tags.get_type()
- cls = EntityTable[dxftype] # get entity class or raise KeyError
- entity = cls() # call constructor
- list(entity.setup_attributes(tags)) # setup dxf attributes - chain of generators
- return entity
-
diff --git a/scripts/py/loaders/dxfgrabber/dxfobjects.py b/scripts/py/loaders/dxfgrabber/dxfobjects.py
deleted file mode 100644
index c6d8a93..0000000
--- a/scripts/py/loaders/dxfgrabber/dxfobjects.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# encoding: utf-8
-# Purpose: objects classes
-# Created: 17.04.2016
-# Copyright (C) 2016, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from datetime import datetime
-from .juliandate import calendar_date
-
-
-class DXFObject(object):
- def __init__(self):
- self.dxftype = 'ENTITY'
- self.handle = None
- self.owner = None
-
- def setup_attributes(self, tags):
- self.dxftype = tags.get_type()
- for code, value in tags.plain_tags():
- if code == 5: # special case 105 handled by STYLE TABLE
- self.handle = value
- elif code == 330:
- self.owner = value
- else:
- yield code, value # chain of generators
-
-
-def unpack_seconds(seconds):
- seconds = int(seconds / 1000) # remove 1/1000 part
- hours = int(seconds / 3600)
- seconds = int(seconds % 3600)
- minutes = int(seconds / 60)
- seconds = int(seconds % 60)
- return hours, minutes, seconds
-
-
-class Sun(DXFObject):
- def __init__(self):
- super(Sun, self).__init__()
- self.version = 1
- self.status = False
- self.sun_color = None
- self.intensity = 0.
- self.shadows = False
- self.date = datetime.now()
- self.daylight_savings_time = False
- self.shadow_type = 0
- self.shadow_map_size = 0
- self.shadow_softness = 0.
-
- def setup_attributes(self, tags):
- date = 0
- time = 0
- for code, value in super(Sun, self).setup_attributes(tags):
- if code == 40:
- self.intensity = value
- elif code == 63:
- self.sun_color = value
- elif code == 70:
- self.shadow_type = value
- elif code == 71:
- self.shadow_map_size = value
- elif code == 90:
- self.version = value
- elif code == 91:
- date = value
- elif code == 92:
- time = value
- elif code == 280:
- self.shadow_softness = value
- elif code == 290:
- self.status = bool(value)
- elif code == 291:
- self.shadows = bool(value)
- elif code == 292:
- self.daylight_savings_time = bool(value)
- else:
- yield code, value # chain of generators
-
- if date > 0:
- date = calendar_date(date)
- else:
- date = datetime.now()
- hours, minutes, seconds = unpack_seconds(time)
- self.date = datetime(date.year, date.month, date.day, hours, minutes, seconds)
-
-ObjectsTable = {
- 'SUN': Sun,
-}
-
-
-def objects_factory(tags):
- dxftype = tags.get_type()
- cls = ObjectsTable.get(dxftype, DXFObject) # get object class
- entity = cls() # call constructor
- list(entity.setup_attributes(tags)) # setup dxf attributes - chain of generators
- return entity
diff --git a/scripts/py/loaders/dxfgrabber/entitysection.py b/scripts/py/loaders/dxfgrabber/entitysection.py
deleted file mode 100644
index f0e4ecc..0000000
--- a/scripts/py/loaders/dxfgrabber/entitysection.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Purpose: handle entity section
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from itertools import islice
-
-from .tags import TagGroups, DXFStructureError
-from .tags import Tags
-from .dxfentities import entity_factory
-
-
-class EntitySection(object):
- name = 'entities'
-
- def __init__(self):
- self._entities = list()
-
- @classmethod
- def from_tags(cls, tags, drawing):
- entity_section = cls()
- entity_section._build(tags)
- return entity_section
-
- def get_entities(self):
- return self._entities
-
- # start of public interface
-
- def __len__(self):
- return len(self._entities)
-
- def __iter__(self):
- return iter(self._entities)
-
- def __getitem__(self, index):
- return self._entities[index]
-
- # end of public interface
-
- def _build(self, tags):
- if len(tags) == 3: # empty entities section
- return
- groups = TagGroups(islice(tags, 2, len(tags)-1))
- self._entities = build_entities(groups)
-
-
-class ObjectsSection(EntitySection):
- name = 'objects'
-
-
-def build_entities(tag_groups):
- def build_entity(group):
- try:
- entity = entity_factory(Tags(group))
- except KeyError:
- entity = None # ignore unsupported entities
- return entity
-
- entities = list()
- collector = None
- for group in tag_groups:
- entity = build_entity(group)
- if entity is not None:
- if collector:
- if entity.dxftype == 'SEQEND':
- collector.stop()
- entities.append(collector.entity)
- collector = None
- else:
- collector.append(entity)
- elif entity.dxftype in ('POLYLINE', 'POLYFACE', 'POLYMESH'):
- collector = _Collector(entity)
- elif entity.dxftype == 'INSERT' and entity.attribsfollow:
- collector = _Collector(entity)
- else:
- entities.append(entity)
- return entities
-
-
-class _Collector:
- def __init__(self, entity):
- self.entity = entity
- self._data = list()
-
- def append(self, entity):
- self._data.append(entity)
-
- def stop(self):
- self.entity.append_data(self._data)
- if hasattr(self.entity, 'cast'):
- self.entity = self.entity.cast()
diff --git a/scripts/py/loaders/dxfgrabber/headersection.py b/scripts/py/loaders/dxfgrabber/headersection.py
deleted file mode 100644
index dd0d5ce..0000000
--- a/scripts/py/loaders/dxfgrabber/headersection.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Purpose: handle header section
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from .tags import TagGroups, DXFTag
-
-
-class HeaderSection(dict):
- name = "header"
-
- def __init__(self):
- super(HeaderSection, self).__init__()
- self._create_default_vars()
-
- @staticmethod
- def from_tags(tags):
- header = HeaderSection()
- if tags[1] == DXFTag(2, 'HEADER'): # DXF12 without a HEADER section is valid!
- header._build(tags)
- return header
-
- def _create_default_vars(self):
- self['$ACADVER'] = 'AC1009'
- self['$DWGCODEPAGE'] = 'ANSI_1252'
-
- def _build(self, tags):
- if len(tags) == 3: # empty header section!
- return
- groups = TagGroups(tags[2:-1], split_code=9)
- for group in groups:
- self[group[0].value] = group[1].value
diff --git a/scripts/py/loaders/dxfgrabber/juliandate.py b/scripts/py/loaders/dxfgrabber/juliandate.py
deleted file mode 100644
index 480c8f2..0000000
--- a/scripts/py/loaders/dxfgrabber/juliandate.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Purpose: julian date
-# Created: 21.03.2011
-# Copyright (C) 2011, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from math import floor
-from datetime import datetime
-
-
-def frac(number):
- return number - floor(number)
-
-
-class JulianDate:
- def __init__(self, date):
- self.date = date
- self.result = self.julian_date() + self.fractional_day()
-
- def fractional_day(self):
- seconds = self.date.hour * 3600. + self.date.minute * 60. + self.date.second
- return seconds / 86400.
-
- def julian_date(self):
- y = self.date.year + (float(self.date.month) - 2.85) / 12.
- A = floor(367. * y) - 1.75 * floor(y) + self.date.day
- B = floor(A) - 0.75 * floor(y / 100.)
- return floor(B) + 1721115.
-
-
-class CalendarDate:
- def __init__(self, juliandate):
- self.jdate = juliandate
- year, month, day = self.get_date()
- hour, minute, second = frac2time(self.jdate)
- self.result = datetime(year, month, day, hour, minute, second)
-
- def get_date(self):
- Z = floor(self.jdate)
-
- if Z < 2299161:
- A = Z # julian calender
- else:
- g = floor((Z - 1867216.25) / 36524.25) # gregorian calendar
- A = Z + 1. + g - floor(g / 4.)
-
- B = A + 1524.
- C = floor((B - 122.1) / 365.25)
- D = floor(365.25 * C)
- E = floor((B - D) / 30.6001)
-
- day = B - D - floor(30.6001 * E)
- month = E - 1 if E < 14 else E - 13
- year = C - 4716 if month > 2 else C - 4715
- return int(year), int(month), int(day)
-
-
-def frac2time(jdate):
- seconds = int(frac(jdate) * 86400.)
- hour = int(seconds / 3600)
- seconds = seconds % 3600
- minute = int(seconds / 60)
- second = seconds % 60
- return hour, minute, second
-
-
-def julian_date(date):
- return JulianDate(date).result
-
-
-def calendar_date(juliandate):
- return CalendarDate(juliandate).result
\ No newline at end of file
diff --git a/scripts/py/loaders/dxfgrabber/layers.py b/scripts/py/loaders/dxfgrabber/layers.py
deleted file mode 100644
index f21fce9..0000000
--- a/scripts/py/loaders/dxfgrabber/layers.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Purpose: handle layers
-# Created: 21.07.12
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-
-__author__ = "mozman "
-
-from .tags import TagGroups
-from .tags import Tags
-
-LOCK = 0b00000100
-FROZEN = 0b00000001
-
-
-class Layer(object):
- def __init__(self, tags):
- self.name = ""
- self.color = 7
- self.linetype = ""
- self.locked = False
- self.frozen = False
- self.on = True
- for code, value in tags.plain_tags():
- if code == 2:
- self.name = value
- elif code == 70:
- self.frozen = bool(value & FROZEN)
- self.locked = bool(value & LOCK)
- elif code == 62:
- if value < 0:
- self.on = False
- self.color = abs(value)
- else:
- self.color = value
- elif code == 6:
- self.linetype = value
-
-
-class Table(object):
-
- def __init__(self):
- self._table_entries = dict()
-
- # start public interface
-
- def get(self, name, default=KeyError):
- try:
- return self._table_entries[name]
- except KeyError:
- if default is KeyError:
- raise
- else:
- return default
-
- def __getitem__(self, item):
- return self.get(item)
-
- def __contains__(self, name):
- return name in self._table_entries
-
- def __iter__(self):
- return iter(self._table_entries.values())
-
- def __len__(self):
- return len(self._table_entries)
-
- def names(self):
- return sorted(self._table_entries.keys())
-
- # end public interface
-
- def entry_tags(self, tags):
- groups = TagGroups(tags)
- assert groups.get_name(0) == 'TABLE'
- assert groups.get_name(-1) == 'ENDTAB'
- for entrytags in groups[1:-1]:
- yield Tags(entrytags)
-
-
-class LayerTable(Table):
- name = 'layers'
-
- @staticmethod
- def from_tags(tags):
- layers = LayerTable()
- for entrytags in layers.entry_tags(tags):
- layer = Layer(entrytags)
- layers._table_entries[layer.name] = layer
- return layers
-
diff --git a/scripts/py/loaders/dxfgrabber/linetypes.py b/scripts/py/loaders/dxfgrabber/linetypes.py
deleted file mode 100644
index 837cdda..0000000
--- a/scripts/py/loaders/dxfgrabber/linetypes.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Purpose: handle linetypes table
-# Created: 06.01.2014
-# Copyright (C) 2014, Manfred Moitzi
-# License: MIT License
-
-__author__ = "mozman "
-
-from .layers import Table
-
-
-class Linetype(object):
- def __init__(self, tags):
- self.name = ""
- self.description = ""
- self.length = 0 # overall length of the pattern
- self.pattern = [] # list of floats: value>0: line, value<0: gap, value=0: dot
- for code, value in tags.plain_tags():
- if code == 2:
- self.name = value
- elif code == 3:
- self.description = value
- elif code == 40:
- self.length = value
- elif code == 49:
- self.pattern.append(value)
-
-
-class LinetypeTable(Table):
- name = 'linetypes'
-
- @staticmethod
- def from_tags(tags):
- styles = LinetypeTable()
- for entry_tags in styles.entry_tags(tags):
- style = Linetype(entry_tags)
- styles._table_entries[style.name] = style
- return styles
-
diff --git a/scripts/py/loaders/dxfgrabber/sections.py b/scripts/py/loaders/dxfgrabber/sections.py
deleted file mode 100644
index 7ddb2bd..0000000
--- a/scripts/py/loaders/dxfgrabber/sections.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Purpose: handle dxf sections
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from .codepage import toencoding
-from .defaultchunk import DefaultChunk, iterchunks
-from .headersection import HeaderSection
-from .tablessection import TablesSection
-from .entitysection import EntitySection, ObjectsSection
-from .blockssection import BlocksSection
-from .acdsdata import AcDsDataSection
-
-
-class Sections(object):
- def __init__(self, tagreader, drawing):
- self._sections = {}
- self._create_default_sections()
- self._setup_sections(tagreader, drawing)
-
- def __contains__(self, name):
- return name in self._sections
-
- def _create_default_sections(self):
- self._sections['header'] = HeaderSection()
- for cls in SECTIONMAP.values():
- section = cls()
- self._sections[section.name] = section
-
- def _setup_sections(self, tagreader, drawing):
- def name(section):
- return section[1].value
-
- bootstrap = True
- for section in iterchunks(tagreader, stoptag='EOF', endofchunk='ENDSEC'):
- if bootstrap:
- new_section = HeaderSection.from_tags(section)
- drawing.dxfversion = new_section.get('$ACADVER', 'AC1009')
- codepage = new_section.get('$DWGCODEPAGE', 'ANSI_1252')
- drawing.encoding = toencoding(codepage)
- bootstrap = False
- else:
- section_name = name(section)
- if section_name in SECTIONMAP:
- section_class = get_section_class(section_name)
- new_section = section_class.from_tags(section, drawing)
- else:
- new_section = None
- if new_section is not None:
- self._sections[new_section.name] = new_section
-
- def __getattr__(self, key):
- try:
- return self._sections[key]
- except KeyError:
- raise AttributeError(key)
-
-SECTIONMAP = {
- 'TABLES': TablesSection,
- 'ENTITIES': EntitySection,
- 'OBJECTS': ObjectsSection,
- 'BLOCKS': BlocksSection,
- 'ACDSDATA': AcDsDataSection,
-}
-
-
-def get_section_class(name):
- return SECTIONMAP.get(name, DefaultChunk)
diff --git a/scripts/py/loaders/dxfgrabber/styles.py b/scripts/py/loaders/dxfgrabber/styles.py
deleted file mode 100644
index 84b0a9d..0000000
--- a/scripts/py/loaders/dxfgrabber/styles.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Purpose: handle text styles
-# Created: 06.01.2014
-# Copyright (C) 2014, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from .layers import Table
-from .tags import Tags
-
-
-class Style(object):
- def __init__(self, tags):
- self.name = ""
- self.height = 1.0
- self.width = 1.0
- self.oblique = 0.
- self.is_backwards = False
- self.is_upside_down = False
- self.font = ""
- self.big_font = ""
- for code, value in tags.plain_tags():
- if code == 2:
- self.name = value
- elif code == 70:
- self.flags = value
- elif code == 40:
- self.height = value
- elif code == 41:
- self.width = value
- elif code == 50:
- self.oblique = value
- elif code == 71:
- self.is_backwards = bool(value & 2)
- self.is_upside_down = bool(value & 4)
- self.oblique = value
- elif code == 3:
- self.font = value
- elif code == 4:
- self.big_font = value
-
-
-class StyleTable(Table):
- name = 'styles'
-
- @staticmethod
- def from_tags(tags):
- styles = StyleTable()
- for entry_tags in styles.entry_tags(tags):
- style = Style(entry_tags)
- styles._table_entries[style.name] = style
- return styles
-
-
-DEFAULT_STYLE = """ 0
-STYLE
- 2
-STANDARD
- 70
-0
- 40
-0.0
- 41
-1.0
- 50
-0.0
- 71
-0
- 42
-1.0
- 3
-Arial
- 4
-
-"""
-
-default_text_style = Style(Tags.from_text(DEFAULT_STYLE))
diff --git a/scripts/py/loaders/dxfgrabber/tablessection.py b/scripts/py/loaders/dxfgrabber/tablessection.py
deleted file mode 100644
index 25ec5ed..0000000
--- a/scripts/py/loaders/dxfgrabber/tablessection.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Purpose: handle tables section
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-from .defaultchunk import iterchunks, DefaultChunk
-from .layers import LayerTable
-from .styles import StyleTable
-from .linetypes import LinetypeTable
-
-TABLENAMES = {
- 'layer': 'layers',
- 'ltype': 'linetypes',
- 'appid': 'appids',
- 'dimstyle': 'dimstyles',
- 'style': 'styles',
- 'ucs': 'ucs',
- 'view': 'views',
- 'vport': 'viewports',
- 'block_record': 'block_records',
- }
-
-
-def tablename(dxfname):
- """ Translate DXF-table-name to attribute-name. ('LAYER' -> 'layers') """
- name = dxfname.lower()
- return TABLENAMES.get(name, name+'s')
-
-
-class DefaultDrawing(object):
- dxfversion = 'AC1009'
- encoding = 'cp1252'
-
-
-class TablesSection(object):
- name = 'tables'
-
- def __init__(self):
- self._tables = dict()
- self._create_default_tables()
-
- def _create_default_tables(self):
- for cls in TABLESMAP.values():
- table = cls()
- self._tables[table.name] = table
-
- @staticmethod
- def from_tags(tags, drawing):
- tables_section = TablesSection()
- tables_section._setup_tables(tags)
- return tables_section
-
- def _setup_tables(self, tags):
- def name(table):
- return table[1].value
-
- def skiptags(tags, count):
- for i in range(count):
- next(tags)
- return tags
-
- itertags = skiptags(iter(tags), 2) # (0, 'SECTION'), (2, 'TABLES')
- for table in iterchunks(itertags, stoptag='ENDSEC', endofchunk='ENDTAB'):
- table_class = table_factory(name(table))
- if table_class is not None:
- new_table = table_class.from_tags(table)
- self._tables[new_table.name] = new_table
-
- def __getattr__(self, key):
- try:
- return self._tables[key]
- except KeyError:
- raise AttributeError(key)
-
-# support for further tables types are possible
-TABLESMAP = {
- 'LAYER': LayerTable,
- 'STYLE': StyleTable,
- 'LTYPE': LinetypeTable,
-}
-
-
-def table_factory(name):
- return TABLESMAP.get(name, None)
diff --git a/scripts/py/loaders/dxfgrabber/tags.py b/scripts/py/loaders/dxfgrabber/tags.py
deleted file mode 100644
index ff4472f..0000000
--- a/scripts/py/loaders/dxfgrabber/tags.py
+++ /dev/null
@@ -1,468 +0,0 @@
-# Purpose: tag reader
-# Created: 21.07.2012, taken from my ezdxf project
-# Copyright (C) 2012, Manfred Moitzi
-# License: MIT License
-from __future__ import unicode_literals
-__author__ = "mozman "
-
-import sys
-from .codepage import toencoding
-from .const import acadrelease
-from array import array
-
-from io import StringIO
-from collections import namedtuple
-from itertools import chain, islice
-from . import tostr
-
-
-DXFTag = namedtuple('DXFTag', 'code value')
-NONE_TAG = DXFTag(999999, 'NONE')
-APP_DATA_MARKER = 102
-SUBCLASS_MARKER = 100
-XDATA_MARKER = 1001
-
-
-class DXFStructureError(Exception):
- pass
-
-
-def point_tuple(value):
- return tuple(float(f) for f in value)
-
-
-POINT_CODES = frozenset(chain(range(10, 20), (210, ), range(110, 113), range(1010, 1020)))
-
-
-def is_point_tag(tag):
- return tag[0] in POINT_CODES
-
-
-infinite = float('inf')
-neg_infinite = float('-inf')
-
-
-def to_float_with_infinite(value):
- try:
- return float(value)
- except ValueError:
- value = value.lower().strip()
- if value.startswith('inf'):
- return infinite
- if value.startswith('-inf'):
- return neg_infinite
- else:
- raise
-
-
-class TagCaster:
- def __init__(self):
- self._cast = self._build()
-
- def _build(self):
- table = {}
- for caster, codes in TYPES:
- for code in codes:
- table[code] = caster
- return table
-
- def cast(self, tag):
- code, value = tag
- typecaster = self._cast.get(code, tostr)
- try:
- value = typecaster(value)
- except ValueError:
- if typecaster is int: # convert float to int
- value = int(float(value))
- else:
- raise
- return DXFTag(code, value)
-
- def cast_value(self, code, value):
- typecaster = self._cast.get(code, tostr)
- try:
- return typecaster(value)
- except ValueError:
- if typecaster is int: # convert float to int
- return int(float(value))
- else:
- raise
-
-TYPES = [
- (tostr, range(0, 10)),
- (point_tuple, range(10, 20)),
- (to_float_with_infinite, range(20, 60)),
- (int, range(60, 100)),
- (tostr, range(100, 106)),
- (point_tuple, range(110, 113)),
- (to_float_with_infinite, range(113, 150)),
- (int, range(170, 180)),
- (point_tuple, [210]),
- (to_float_with_infinite, range(211, 240)),
- (int, range(270, 290)),
- (int, range(290, 300)), # bool 1=True 0=False
- (tostr, range(300, 370)),
- (int, range(370, 390)),
- (tostr, range(390, 400)),
- (int, range(400, 410)),
- (tostr, range(410, 420)),
- (int, range(420, 430)),
- (tostr, range(430, 440)),
- (int, range(440, 460)),
- (to_float_with_infinite, range(460, 470)),
- (tostr, range(470, 480)),
- (tostr, range(480, 482)),
- (tostr, range(999, 1010)),
- (point_tuple, range(1010, 1020)),
- (to_float_with_infinite, range(1020, 1060)),
- (int, range(1060, 1072)),
-]
-
-_TagCaster = TagCaster()
-cast_tag = _TagCaster.cast
-cast_tag_value = _TagCaster.cast_value
-
-
-def stream_tagger(stream, assure_3d_coords=False):
- """ Generates DXFTag() from a stream (untrusted external source). Does not skip comment tags 999.
- """
- class Counter:
- def __init__(self):
- self.counter = 0
-
- undo_tag = None
- line = Counter() # writeable line counter for next_tag(), Python 2.7 does not support the nonlocal statement
-
- def next_tag():
- code = stream.readline()
- value = stream.readline()
- line.counter += 2
- if code and value: # StringIO(): empty strings indicates EOF
- return DXFTag(int(code[:-1]), value[:-1]) # without '\n'
- else: # StringIO(): missing '\n' indicates EOF
- raise EOFError()
-
- while True:
- try:
- if undo_tag is not None:
- x = undo_tag
- undo_tag = None
- else:
- x = next_tag()
- code = x.code
- if code == 999: # skip comments
- continue
- if code in POINT_CODES:
- y = next_tag() # y coordinate is mandatory
- if y.code != code + 10:
- raise DXFStructureError("Missing required y coordinate near line: {}.".format(line.counter))
- z = next_tag() # z coordinate just for 3d points
- try:
- if z.code == code + 20:
- point = (float(x.value), float(y.value), float(z.value))
- else:
- if assure_3d_coords:
- point = (float(x.value), float(y.value), 0.)
- else:
- point = (float(x.value), float(y.value))
- undo_tag = z
- except ValueError:
- raise DXFStructureError('Invalid floating point values near line: {}.'.format(line.counter))
- yield DXFTag(code, point)
- else: # just a single tag
- try:
- yield cast_tag(x)
- except ValueError:
- raise DXFStructureError('Invalid tag (code={code}, value="{value}") near line: {line}.'.format(
- line=line.counter,
- code=x.code,
- value=x.value,
- ))
- except EOFError:
- return
-
-
-def string_tagger(s):
- return stream_tagger(StringIO(s))
-
-
-class Tags(list):
- """ DXFTag() chunk as flat list. """
- def find_all(self, code):
- """ Returns a list of DXFTag(code, ...). """
- return [tag for tag in self if tag.code == code]
-
- def tag_index(self, code, start=0, end=None):
- """Return first index of DXFTag(code, value).
- """
- if end is None:
- end = len(self)
- index = start
- while index < end:
- if self[index].code == code:
- return index
- index += 1
- raise ValueError(code)
-
- def get_value(self, code):
- for tag in self:
- if tag.code == code:
- return tag.value
- raise ValueError(code)
-
- @staticmethod
- def from_text(text):
- return Tags(string_tagger(text))
-
- def get_type(self):
- return self.__getitem__(0).value
-
- def plain_tags(self): # yield no app data and no xdata
- is_app_data = False
- for tag in self:
- if tag.code >= 1000: # skip xdata
- continue
- if tag.code == APP_DATA_MARKER:
- if is_app_data: # tag.value == '}'
- is_app_data = False # end of app data
- else: # value == '{APPID'
- is_app_data = True # start of app data
- continue
- if not is_app_data:
- yield tag
-
- def xdata(self):
- index = 0
- end = len(self)
- while index < end:
- if self[index].code > 999: # all xdata tag codes are >= 1000
- return self[index:] # xdata is always at the end of the DXF entity
- index += 1
- return []
-
- def app_data(self):
- app_data = {}
- app_tags = None
- for tag in self:
- if tag.code == APP_DATA_MARKER:
- if tag.value == '}': # end of app data
- app_tags.append(tag)
- app_data[app_tags[0].value] = app_tags
- app_tags = None
- else:
- app_tags = [tag]
- else:
- if app_tags is not None: # collection app data
- app_tags.append(tag)
- return app_data
-
- def subclasses(self):
- classes = {}
- name = 'noname'
- tags = []
- for tag in self.plain_tags():
- if tag.code == SUBCLASS_MARKER:
- classes[name] = tags
- tags = []
- name = tag.value
- else:
- tags.append(tag)
- classes[name] = tags
- return classes
-
- def get_subclass(self, name):
- classes = self.subclasses()
- return classes.get(name, 'noname')
-
-
-class TagGroups(list):
- """
- Group of tags starting with a SplitTag and ending before the next SplitTag.
-
- A SplitTag is a tag with code == splitcode, like (0, 'SECTION') for splitcode=0.
-
- """
- def __init__(self, tags, split_code=0):
- super(TagGroups, self).__init__()
- self._build_groups(tags, split_code)
-
- def _build_groups(self, tags, splitcode):
- def append(tag): # first do nothing, skip tags in front of the first split tag
- pass
- group = None
- for tag in tags: # has to work with iterators/generators
- if tag.code == splitcode:
- if group is not None:
- self.append(group)
- group = Tags([tag])
- append = group.append # redefine append: add tags to this group
- else:
- append(tag)
- if group is not None:
- self.append(group)
-
- def get_name(self, index):
- return self[index][0].value
-
- @staticmethod
- def from_text(text, split_code=0):
- return TagGroups(Tags.from_text(text), split_code)
-
-
-class ClassifiedTags:
- """ Manage Subclasses, AppData and Extended Data """
-
- def __init__(self, iterable=None):
- self.appdata = list() # code == 102, keys are "{", values are Tags()
- self.subclasses = list() # code == 100, keys are "subclassname", values are Tags()
- self.xdata = list() # code >= 1000, keys are "APPNAME", values are Tags()
- if iterable is not None:
- self._setup(iterable)
-
- @property
- def noclass(self):
- return self.subclasses[0]
-
- def _setup(self, iterable):
- tagstream = iter(iterable)
-
- def collect_subclass(start_tag):
- """ a subclass can contain appdata, but not xdata, ends with
- SUBCLASSMARKER or XDATACODE.
- """
- data = Tags() if start_tag is None else Tags([start_tag])
- try:
- while True:
- tag = next(tagstream)
- if tag.code == APP_DATA_MARKER and tag.value[0] == '{':
- app_data_pos = len(self.appdata)
- data.append(DXFTag(tag.code, app_data_pos))
- collect_appdata(tag)
- elif tag.code in (SUBCLASS_MARKER, XDATA_MARKER):
- self.subclasses.append(data)
- return tag
- else:
- data.append(tag)
- except StopIteration:
- pass
- self.subclasses.append(data)
- return NONE_TAG
-
- def collect_appdata(starttag):
- """ appdata, can not contain xdata or subclasses """
- data = Tags([starttag])
- while True:
- try:
- tag = next(tagstream)
- except StopIteration:
- raise DXFStructureError("Missing closing DXFTag(102, '}') for appdata structure.")
- data.append(tag)
- if tag.code == APP_DATA_MARKER:
- break
- self.appdata.append(data)
-
- def collect_xdata(starttag):
- """ xdata are always at the end of the entity and can not contain
- appdata or subclasses
- """
- data = Tags([starttag])
- try:
- while True:
- tag = next(tagstream)
- if tag.code == XDATA_MARKER:
- self.xdata.append(data)
- return tag
- else:
- data.append(tag)
- except StopIteration:
- pass
- self.xdata.append(data)
- return NONE_TAG
-
- tag = collect_subclass(None) # preceding tags without a subclass
- while tag.code == SUBCLASS_MARKER:
- tag = collect_subclass(tag)
- while tag.code == XDATA_MARKER:
- tag = collect_xdata(tag)
-
- if tag is not NONE_TAG:
- raise DXFStructureError("Unexpected tag '%r' at end of entity." % tag)
-
- def __iter__(self):
- for subclass in self.subclasses:
- for tag in subclass:
- if tag.code == APP_DATA_MARKER and isinstance(tag.value, int):
- for subtag in self.appdata[tag.value]:
- yield subtag
- else:
- yield tag
-
- for xdata in self.xdata:
- for tag in xdata:
- yield tag
-
- def get_subclass(self, name):
- for subclass in self.subclasses:
- if len(subclass) and subclass[0].value == name:
- return subclass
- raise KeyError("Subclass '%s' does not exist." % name)
-
- def get_xdata(self, appid):
- for xdata in self.xdata:
- if xdata[0].value == appid:
- return xdata
- raise ValueError("No extended data for APPID '%s'" % appid)
-
- def get_appdata(self, name):
- for appdata in self.appdata:
- if appdata[0].value == name:
- return appdata
- raise ValueError("Application defined group '%s' does not exist." % name)
-
- def get_type(self):
- return self.noclass[0].value
-
- @staticmethod
- def from_text(text):
- return ClassifiedTags(string_tagger(text))
-
-
-class DXFInfo(object):
- def __init__(self):
- self.release = 'R12'
- self.version = 'AC1009'
- self.encoding = 'cp1252'
- self.handseed = '0'
-
- def DWGCODEPAGE(self, value):
- self.encoding = toencoding(value)
-
- def ACADVER(self, value):
- self.version = value
- self.release = acadrelease.get(value, 'R12')
-
- def HANDSEED(self, value):
- self.handseed = value
-
-
-def dxfinfo(stream):
- info = DXFInfo()
- tag = DXFTag(999999, '')
- tagreader = stream_tagger(stream)
- while tag != DXFTag(0, 'ENDSEC'):
- tag = next(tagreader)
- if tag.code != 9:
- continue
- name = tag.value[1:]
- method = getattr(info, name, None)
- if method is not None:
- method(next(tagreader).value)
- return info
-
-
-def binary_encoded_data_to_bytes(data):
- PY3 = sys.version_info[0] >= 3
- byte_array = array('B' if PY3 else b'B')
- for text in data:
- byte_array.extend(int(text[index:index+2], 16) for index in range(0, len(text), 2))
- return byte_array.tobytes() if PY3 else byte_array.tostring()
diff --git a/scripts/py/loaders/librecadfont1.py b/scripts/py/loaders/librecadfont1.py
deleted file mode 100644
index d2c1db2..0000000
--- a/scripts/py/loaders/librecadfont1.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import os
-
-class Symbol:
- def __init__(self, char, code):
- self.char = char
- self.code = code
- self.ref = None
- self.lines = []
-
-class LibreCADFont1(object):
-
- def __init__(self):
- self.meta = {}
- self.symbols = {}
-
- def getSymbol(self, char):
- return self.symbols[char]
-
- def load_from_file(self, filename):
- """
- Load font data from a file.
- """
- with open(filename) as f:
- lines = f.readlines()
- #print lines
- symbol = None
- lastmeta = None
- valid_meta = ['Format','Creator','Name','Version','Encoding',
- 'LetterSpacing','WordSpacing','LineSpacingFactor','Created',
- 'Last modified','Author','License','Notes']
-
- for line in lines:
-
- if line == '\n':
- if symbol:
- self.symbols[symbol.char] = symbol
- symbol = None
- continue
-
- if line[0] == '#':
- data = line[1:].strip().split(':',1)
-
- if (data[0] not in valid_meta) or len(data) < 2:
- if data[0] and lastmeta:
- self.meta[ lastmeta ] += ' ' + line[1:].strip()
- continue
-
- self.meta[ data[0] ] = data[1].strip()
- lastmeta = data[0]
- elif line[0] == '[':
- if line[1] == '#':
- code = str(line[2:6])
- code_num = int(code, 16)
- sym = unichr(code_num)
- symbol = Symbol(sym, code)
- else:
- code = str(line[1:5])
- sym = line[7]
- symbol = Symbol(sym, code)
- else:
- if symbol:
- points = []
- segments = line.strip().split(';')
- skip = False
- for seg in segments:
- if seg[0] == 'C':
- ref = seg[1:5]
- code_num = int(ref, 16)
- sym = unichr(code_num)
- symbol.ref = sym
- skip = True
- break
-
- else:
- pts = seg.split(',')
- if len(pts) == 3:
- points.append( (float(pts[0]), float(pts[1]), float(pts[2][1:])) )
- else:
- points.append( (float(pts[0]), float(pts[1])) )
- if not skip:
- symbol.lines.append( points )
-
- print self.meta
-
-def readfile(filename):
- """
- Create a LibreCADFont1 object and load it's data from a file.
- """
- lff = LibreCADFont1()
- if os.path.exists(filename):
- lff.load_from_file(filename)
- return lff
- else:
- return None
diff --git a/scripts/py/output/__init__.py b/scripts/py/output/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/scripts/py/output/common.py b/scripts/py/output/common.py
deleted file mode 100644
index f37ad43..0000000
--- a/scripts/py/output/common.py
+++ /dev/null
@@ -1,424 +0,0 @@
-#!/bin/env python
-# -*- coding: utf-8; -*-
-#
-# (c) 2016 FABtotum, http://www.fabtotum.com
-#
-# This file is part of FABUI.
-#
-# FABUI is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# FABUI is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with FABUI. If not, see .
-
-__authors__ = "Daniel Kesler"
-__license__ = "GPL - https://opensource.org/licenses/GPL-3.0"
-__version__ = "1.0"
-
-# Import external modules
-import numpy as np
-
-# Import internal modules
-
-class EngraverOutput(object):
-
- def __init__(self, preset):
- self.preset = preset
- self.interleave = False
-
- self.skip_function = {
- 'modulo' : self.get_y_list_modulo,
- }
-
- def __sort_elements(self, elements, sort_list = [], reverse_list = [], use_reverse = False):
-
- if len(elements) == 0:
- return [], []
-
- if len(sort_list) == 0:
- cur_x = 0.0
- cur_y = 0.0
- else:
- last = sort_list[-1]
- if last in reverse_list:
- p0 = elements[last]['points'][0]
- else:
- p0 = elements[last]['points'][-1]
- cur_x = p0[0]
- cur_y = p0[1]
-
- closest = None
- reverse = None
- dist = 1e20
-
- for i in xrange( len(elements) ):
- #~ print elements[i]
- if i not in sort_list:
- e = elements[i]
- p0 = e['points'][0]
- dx = p0[0] - cur_x
- dy = p0[1] - cur_y
- d0 = np.sqrt(dx*dx + dy*dy)
-
- d1 = d0
- if use_reverse:
- p0 = e['points'][-1]
- dx = p0[0] - cur_x
- dy = p0[1] - cur_y
- d1 = np.sqrt(dx*dx + dy*dy)
-
- if d0 < dist:
- dist = d0
- closest = i
- elif d1 < dist and use_reverse:
- dist = d1
- closest = i
- reverse = i
-
- if reverse is not None:
- reverse_list.append(reverse)
-
- if closest is None:
- return sort_list, reverse_list
- else:
- #~ print "added" , closest
- sort_list.append(closest)
-
- if len(sort_list) == len(elements):
- return sort_list, reverse_list
- else:
- return self.__sort_elements(elements, sort_list, reverse_list)
-
- def draw(self, data, optimize=False):
- #~ print 'draw-area', data.min_x, data.min_y, data.max_x, data.max_y
- width = data.max_x - data.min_x
- height = data.max_y - data.min_y
- #~ print 'draw', width, height
- #~ print 'offset', data.min_x, data.min_y
-
- for lyr in data.layers:
- color = lyr.color
-
- if not self.skip_level(color):
-
- self.level(color)
-
- elements = lyr.primitives
-
- if optimize > 0:
- idx_list, reverse_list = self.__sort_elements(elements, use_reverse=(optimize>1) )
- else:
- idx_list = range(len(elements))
- reverse_list = []
-
- for i in idx_list:
- e = elements[i]
- t = e['type']
- filled = False
- if 'filled' in e:
- filled = e['filled']
-
- if not filled:
- if 'points' in e:
- if i in reverse_list:
- points = []
- for pt in reversed(e['points']):
- points.append(pt)
- else:
- points = e['points']
-
- self.draw_polyline(points, color)
- else:
- if t == 'rect':
- p1 = e['points'][0]
- p2 = e['points'][2]
- self.fill_rect(p1[0], p1[1], p2[0], p2[1], color)
- else:
- print 'TODO (filled)', t
-
- #~ if t == 'line':
- #~ pass
- #~ elif t == 'polyline' or t == 'spline' or t == 'ellipse' or t == 'circle' or t == 'arc':
- #~ self.draw_polyline(e['points'], color)
- #~ elif t == 'circle':
- #~ c = e['center']
- #~ self.draw_circle( c[0], c[1], e['radius'], color)
- #~ elif t == 'arc':
- #~ c = e['center']
- #~ self.draw_arc( c[0], c[1], e['radius'], e['start'], e['end'], color)
- #elif t == 'ellipse':
- #~ c = e['center']
- #~ self.draw_ellipse(c[0], c[1], e['ratio'], e['major_axis'], e['start'], e['end'], color)
- #~ elif t == 'rect':
- #~ if e['filled']:
- #~ p1 = e['points'][0]
- #~ p2 = e['points'][2]
- #~ self.fill_rect(p1[0], p1[1], p2[0], p2[1], color)
- #~ else:
- #~ pass
- #~ else:
- #~ print 'TODO', t
-
- def get_y_list_modulo(self, color, y1, y2):
- """
- Generate a list of Y for drawing a primitive between y1 and y2.
- If lines have to skipped, only the Y values that have to be drawn
- will be in the output list.
-
- :param color:
- :param y1:
- :param y2:
- :type color: uint8
- :type y1: float
- :type y2: float
- :returns: List of Y values that should be drawn
- :rtype: list
- """
- in_list = range(y1,y2)
- out_list = []
- mod = int(self.preset['skip']['mod'])
- tmp = self.preset['skip']['on']
- on = []
- for o in tmp:
- on.append( int(o) )
-
- for i in in_list:
- if self.interleave:
- v = (i+1) % mod
- else:
- v = i % mod
- if v in on:
- out_list.append(i)
-
- return out_list
-
- def __point_on_line(self, p1, p2, t):
- """ for future use """
- dx = p2[0] - p1[0]
- dy = p2[1] - p1[1]
-
- x = p1[0] + float(dx*t)
- y = p1[1] + float(dy*t)
-
- return (x, y)
-
- def __sub_bezier(self, points, t):
- """ for future use """
- sub_points = []
-
- p1 = points[0]
- for p2 in points[1:]:
- pt = self.__point_on_line(p1, p2, t)
- sub_points.append(pt)
- p1 = p2
-
- if len(sub_points) > 1:
- return self.__sub_bezier(sub_points, t)
- else:
- return sub_points
-
- def __bezier_points(self, points, t):
- """ for future use """
- if type(t) == list or type(t) == np.ndarray:
- pts = []
- for t1 in t:
- p = self.__sub_bezier(points, t1)
- pts = pts + p
- return pts
- else:
- return self.__sub_bezier(points, t)
-
- def fill_rect(self, x1, y1, x2, y2, color = 0):
- """
- Draw a rectangle out of hlines. Skip lines if needed.
- """
- y_list = self.skip_function[ self.preset['skip']['type'] ](color, y1, y2)
-
- for y in y_list:
- self.draw_hline(x1, x2, y, color)
-
- def draw_polyline(self, points, color = 0):
- #~ p0 = points[0]
- have_prev = False
-
- for p in points:
- p1 = ( int(p[0]), int(p[1]))
- #cv2.circle(self.dbg_img, p1, 2, 0)
- if have_prev:
- self.draw_line( p0[0], p0[1], p[0], p[1], color)
-
- p0 = p
- have_prev = True
-
- def draw_line(self, x1, y1, x2, y2, color = 0):
- raise NotImplementedError('"draw_line" function must be implemented')
-
- def draw_circle(self, x0, y0, r, color = 0, step = 10.0):
- """
- Draw an arc.
- """
-
- start = 0.0
- end = 360.0
- steps = int( 360.0 / step )
-
- have_prev = False
-
- for a in xrange(steps):
- angle = np.deg2rad(start + a*step)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- if have_prev:
- self.draw_line(x1,y1, x2,y2, color)
-
- x1 = x2
- y1 = y2
- have_prev = True
-
- if (start + (steps-1)*step) != end:
- angle = np.deg2rad(end)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- self.draw_line(x1,y1, x2,y2, color)
-
- def __ellipse_point(self, center, r1, r2, rotM, t):
- x1 = r1 * np.cos( np.radians(t) )
- y1 = r2 * np.sin( np.radians(t) )
- tmp = np.array([x1,y1])
- p1 = center + (tmp * rotM)
- return p1.A1[0], p1.A1[1]
-
- def draw_ellipse(self, x0, y0, ratio, axis, start, end, color = 0, step = 10.0):
- # Get length of axis vector
- r1 = np.linalg.norm(axis)
- # Get second radius
- r2 = r1 * ratio
-
- # Get axis angle
- if axis[0] == 0:
- a = np.radians(90.0)
- elif axis[1] == 0:
- a = np.radians(0.0)
- else:
- a = np.arctan(axis[1] / axis[0])
-
- # Prepare rotation matrix
- center = np.array([x0,y0])
- c1 = np.cos(a)
- c2 = np.sin(a)
- rotM = np.matrix([
- [c1,c2],
- [-c2,c1]
- ])
- rotMCCW = np.matrix([
- [c1,-c2],
- [c2,c1]
- ])
-
- eye = np.matrix([ [1.0, 0.0], [0.0, 1.0] ])
-
- fix = 0
- if start > end:
- start += 180.0
- if start > 360.0:
- start -= 360.0
- end += 180.0
-
- have_prev = False
-
- tl = np.arange(start, end, step)
-
- for t in tl:
- x2,y2 = self.__ellipse_point(center, r1, r2, rotM, t)
-
- if have_prev:
- self.draw_line(x1,y1, x2,y2)
-
- x1 = x2
- y1 = y2
- have_prev = True
-
- x2,y2 = self.__ellipse_point(center, r1, r2, rotM, end)
- self.draw_line(x1,y1, x2,y2)
-
- def draw_arc(self, x0, y0, r, start, end, color = 0, step = 10.0):
- """
- Draw an arc.
- """
-
- angle = end - start
- if angle < 0:
- angle += 360
-
- steps = int(abs(angle / step))
-
- have_prev = False
-
- for a in xrange(steps):
- angle = np.deg2rad(start + a*step)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- if have_prev:
- self.draw_line(x1,y1, x2,y2, color)
-
- x1 = x2
- y1 = y2
- have_prev = True
-
- if (start + (steps-1)*step) != end:
- angle = np.deg2rad(end)
- x2 = x0 + np.cos(angle)*r
- y2 = y0 + np.sin(angle)*r
-
- self.draw_line(x1,y1, x2,y2, color)
-
- def draw_hline(self, x1, x2, y, color = 0):
- """
- Draw a horizontal line.
-
- :param x1: Start X
- :param x2: End X
- :param y: Start and end Y
- :param color: Line color
- """
- self.draw_line(x1, y, x2, y, color)
-
- def start(self):
- """
- Engraver initialization callback.
- """
- pass
-
- def skip_level(self, color):
- return False
-
- def level(self, color):
- """
- Level change callback.
-
- :param color: Level color
- :type color: uint8
- """
- pass
-
- def comment(self, comment):
- """
- Line comment callback.
- """
- pass
-
- def end(self):
- """
- Engraver finalization callback.
- """
- pass
diff --git a/scripts/py/output/debug.py b/scripts/py/output/debug.py
deleted file mode 100644
index 74effc9..0000000
--- a/scripts/py/output/debug.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/env python
-# -*- coding: utf-8; -*-
-#
-# (c) 2016 FABtotum, http://www.fabtotum.com
-#
-# This file is part of FABUI.
-#
-# FABUI is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# FABUI is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with FABUI. If not, see .
-
-__authors__ = "Daniel Kesler"
-__license__ = "GPL - https://opensource.org/licenses/GPL-3.0"
-__version__ = "1.0"
-
-# Import external modules
-import numpy as np
-import cv2,cv
-
-# Import internal modules
-from .common import EngraverOutput
-
-class DebugEngraver(EngraverOutput):
- """
- Engrever Debug class. Stores the result in an image with
- resolution of one dot per pixel.
- """
-
- def __init__(self, filename, width, height, color = 0, dot_size = 0.1, invert = False, preset = {}):
- self.filename = filename
- self.dot_size = dot_size
-
- self.invert = invert
-
- if invert:
- color = 255 - color
-
- self.dbg_img = np.ones((int(height), int(width), 1), np.uint8)*color
- super(DebugEngraver, self).__init__(preset)
-
- def draw_hline(self, x1, x2, y, color = 0):
- tx1 = min(x1,x2)
- tx2 = max(x1,x2)
- if self.invert:
- color = 255 - color
- self.dbg_img[y:y+1, tx1:tx2] = color
-
- def draw_line(self, x1, y1, x2, y2, color = 0):
- if self.invert:
- color = 255 - color
- cv2.line(self.dbg_img, (int(x1),int(y1)), (int(x2),int(y2)), color)
-
- def draw_circle(self, x1, y1, r, color = 0):
- if self.invert:
- color = 255 - color
- cv2.circle(self.dbg_img, (int(x1), int(y1)), int(r), color)
-
- def end(self):
- print "Saving output to file '{0}'".format(self.filename)
- self.dbg_img = cv2.flip(self.dbg_img,0)
- cv2.imwrite(self.filename, self.dbg_img)
-
- def show(self):
- self.dbg_img = cv2.flip(self.dbg_img,0)
- cv2.imshow('image', self.dbg_img)
- cv2.waitKey(0)
- cv2.destroyAllWindows()
diff --git a/scripts/py/output/laser.py b/scripts/py/output/laser.py
deleted file mode 100644
index d1015fc..0000000
--- a/scripts/py/output/laser.py
+++ /dev/null
@@ -1,301 +0,0 @@
-#!/bin/env python
-# -*- coding: utf-8; -*-
-#
-# (c) 2016 FABtotum, http://www.fabtotum.com
-#
-# This file is part of FABUI.
-#
-# FABUI is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# FABUI is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with FABUI. If not, see .
-
-__authors__ = "Daniel Kesler"
-__license__ = "GPL - https://opensource.org/licenses/GPL-3.0"
-__version__ = "1.0"
-
-# Import standard python module
-import time
-
-# Import external modules
-import numpy as np
-
-# Import internal modules
-from .common import EngraverOutput
-
-class LaserEngraver(EngraverOutput):
- """
- Laser engraver gcode generator class.
- """
-
- def __init__(self, filename, scale, preset, statistics=False):
- self.filename = filename
- self.scale = scale
- self.cur_x = 0.0
- self.cur_y = 0.0
- self.laser_pwm = 0
- super(LaserEngraver, self).__init__(preset)
-
- self.pwm_function = {
- 'const' : self.get_pwm_value_const,
- 'linear' : self.get_pwm_value_linear
- }
-
- self.speed_function = {
- 'const' : self.get_burn_speed_value_const,
- 'linear' : self.get_burn_speed_value_linear,
- 'travel' : self.get_travel_speed_value_const
- }
-
- self.get_pwm_value = self.pwm_function[ preset['pwm']['type'] ]
- self.get_burn_speed = self.speed_function[ preset['speed']['type'] ]
- self.get_travel_speed = self.speed_function[ 'travel' ]
-
- self.laser_is_on = False
- self.off_during_travel = False
- if 'off_during_travel' in preset['pwm']:
- self.off_during_travel = preset['pwm']['off_during_travel']
-
- self.statistics = statistics
- self.stats = {
- 'travel_length' : 0.0,
- 'burn_length' : 0.0,
- 'travel_time' : 0.0,
- 'burn_time' : 0.0
- }
-
- self.pwm_gocde = 'M4 S{0}\r\n'
- #~ self.pwm_gocde = 'M4 S{0}\r\nM400\r\n'
- #~ self.fd.write('M400
-
- def comment(self, comment):
- """
- Add comment to the gcode output.
- :param comment: Comment to be added.
-
- """
- self.fd.write(";{0}\r\n".format(comment))
-
- def get_pwm_value_const(self, color):
- """
- Returns constant PWM value.
-
- :returns: PWM value
- :rtype: uint8
- """
- return int(self.preset['pwm']['value'])
-
- def get_pwm_value_linear(self, color):
- """
- Returns PWM value using a linear function to convert color
- to PWM value.
-
- :param color:
- :type color: uint8
- :returns: PWM value.
- :rtype: uint8
- """
- x_min = int(self.preset['pwm']['in_min'])
- x_max = int(self.preset['pwm']['in_max'])
- y_min = int(self.preset['pwm']['out_min'])
- y_max = int(self.preset['pwm']['out_max'])
-
- dx = float(x_max - x_min)
- dy = float(y_max - y_min)
- k = dy / dx
-
- y = 0
-
- if color >= x_min and color < x_max:
- y = y_min + (color-x_min) * k
-
- if color >= x_max:
- y = y_max
-
- return int(y)
-
- def get_burn_speed_value_const(self, color):
- """
- Returns constant burning speed.
-
- :returns: Burn speed value.
- :rtype: uint8
- """
- return int(self.preset['speed']['burn'])
-
- def get_burn_speed_value_linear(self, color):
- """
- Returns burning speed using a linear function to convert color
- to speed value.
-
- :param color:
- :type color: uint8
- :returns: Burn speed value.
- :rtype: int
- """
- x_min = int(self.preset['speed']['in_min'])
- x_max = int(self.preset['speed']['in_max'])
- y_min = int(self.preset['speed']['out_min'])
- y_max = int(self.preset['speed']['out_max'])
-
- dx = float(x_max - x_min)
- dy = float(y_max - y_min)
- k = dy / dx
-
- y = 0
-
- if color >= x_min and color < x_max:
- y = y_min + (color-x_min) * k
-
- if color >= x_max:
- y = y_max
-
- return int(y)
-
- def get_travel_speed_value_const(self, color = 0):
- """
- Returns constant Travel speed.
-
- :returns: Travel speed.
- :rtype: uint8
- """
- return self.preset['speed']['travel']
-
- def level(self, color):
- """
- Level change setup gcode.
- """
- pwm = self.get_pwm_value(color)
- self.laser_pwm = pwm
- speed = self.get_burn_speed(color)
- print "Color {0} => PWM: {1}, Speed: {2}".format(color, pwm, speed)
- self.comment(' Applying PWM value {0}'.format(pwm) )
- self.fd.write(self.pwm_gocde.format(pwm))
- self.fd.write('M400 ;Make sure all previous moves are finished\r\n')
-
- def skip_level(self, color):
- pwm = self.get_pwm_value(color)
- return (pwm == 0)
-
- def start(self):
- """
- Engraver start function.
- """
- self.fd = open(self.filename, 'w')
- self.add_start_code()
-
- def add_start_code(self):
- """
- Engraver start code.
- """
- now = time.strftime("%c")
- self.fd.write("""\
-;FABtotum laser engraving, coded on {0}
-G4 S1 ;1 millisecond pause to buffer the bep bep
-M450 S2 ; Activate laser module
-M793 S4 ;set laser head
-M728 ;FAB bep bep
-G90 ; absolute mode
-G4 S1 ;1 second pause to reach the printer (run fast)
-G1 F10000 ;Set travel speed
-M107
-""".format(now))
-
- def add_engrave_move(self, x, y, color):
- """
- Add gcode for an engraving move.
-
- :param x: Target X.
- :param y: Target y.
- :param color: Engrave color.
- :type x: float
- :type y: float
- :type color: uint8
- """
-
- # mm/min
- feed = self.get_burn_speed(color)
- pwm = self.get_pwm_value(color)
-
- if self.statistics:
- dx = self.cur_x - x
- dy = self.cur_y - y
- travel = np.sqrt(dx*dx + dy*dy)
- self.stats['burn_length'] += travel
- self.stats['burn_time'] += travel / (feed/60)
-
- #~ if not self.laser_is_on:
- #~ self.laser_is_on = True
- if self.off_during_travel:
- self.fd.write('M400\r\n')
- self.fd.write(self.pwm_gocde.format(pwm))
-
-
- self.fd.write("G1 X{0} Y{1} F{2}\r\n".format(x*self.scale[0], y*self.scale[1], feed) )
- self.cur_x = x
- self.cur_y = y
-
- def add_travel_move(self, x, y):
- """
- Add gcode for a travel move.
-
- :param x: Target X
- :param y: Target y
- :type x: float
- :type y: float
- """
-
- # mm/min
- feed = self.get_travel_speed()
-
- if x != self.cur_x or y != self.cur_y:
-
- #~ if self.off_during_travel and self.laser_is_on:
- #~ self.laser_is_on = False
- #~ self.fd.write(self.pwm_gocde.format(0))
-
- if self.off_during_travel:
- self.fd.write('M400\r\n')
- self.fd.write(self.pwm_gocde.format(0))
-
-
- if self.statistics:
- dx = self.cur_x - x
- dy = self.cur_y - y
- travel = np.sqrt(dx*dx + dy*dy)
- self.stats['travel_length'] += travel
- self.stats['travel_time'] += travel / (feed/60)
-
- self.fd.write("G0 X{0} Y{1} F{2}\r\n".format(x*self.scale[0], y*self.scale[1], feed) )
- self.cur_x = x
- self.cur_y = y
-
- def end(self):
- """
- Engraver end function.
- """
- self.add_end_code()
- self.fd.close()
-
- def add_end_code(self):
- """
- Shutdown code.
- """
- self.fd.write("""\
-M400 ;Wait for all moves to finish
-M728 ;FAB bep bep (end print)
-G4 S1 ;pause
-M5 ;shutdown
-""")
-
- def draw_line(self, x1, y1, x2, y2, color = 0):
- self.add_travel_move(x1, y1)
- self.add_engrave_move(x2, y2, color)