From 50e0ad4f85788cd134413c15fceb5aa033bb2ef0 Mon Sep 17 00:00:00 2001 From: daid Date: Tue, 1 Oct 2013 11:59:05 +0200 Subject: [PATCH] Add more 2D drawing code. Unused, but still, I have plans. (And all developed in my own free time) --- Cura/util/drawingLoader/__init__.py | 0 Cura/util/drawingLoader/drawing.py | 184 ++++++++++++++++++++++++++ Cura/util/drawingLoader/dxf.py | 73 +++++++++++ Cura/util/{ => drawingLoader}/svg.py | 185 +++------------------------ 4 files changed, 274 insertions(+), 168 deletions(-) create mode 100644 Cura/util/drawingLoader/__init__.py create mode 100644 Cura/util/drawingLoader/drawing.py create mode 100644 Cura/util/drawingLoader/dxf.py rename Cura/util/{ => drawingLoader}/svg.py (69%) diff --git a/Cura/util/drawingLoader/__init__.py b/Cura/util/drawingLoader/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Cura/util/drawingLoader/drawing.py b/Cura/util/drawingLoader/drawing.py new file mode 100644 index 00000000..d9822f29 --- /dev/null +++ b/Cura/util/drawingLoader/drawing.py @@ -0,0 +1,184 @@ +from __future__ import absolute_import +__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License" + +import math +import numpy + +class Path(object): + LINE = 0 + ARC = 1 + CURVE = 2 + + def __init__(self, x, y, matrix=numpy.matrix(numpy.identity(3, numpy.float64))): + self._matrix = matrix + self._startPoint = complex(x, y) + self._points = [] + self._isClosed = False + + def addLineTo(self, x, y): + self._points.append({'type': Path.LINE, 'p': complex(x, y)}) + + def addArcTo(self, x, y, rot, rx, ry, large, sweep): + self._points.append({ + 'type': Path.ARC, + 'p': complex(x, y), + 'rot': rot, + 'radius': complex(rx, ry), + 'large': large, + 'sweep': sweep + }) + + def addCurveTo(self, x, y, cp1x, cp1y, cp2x, cp2y): + self._points.append({ + 'type': Path.CURVE, + 'p': complex(x, y), + 'cp1': complex(cp1x, cp1y), + 'cp2': complex(cp2x, cp2y) + }) + + def isClosed(self): + return self._isClosed + + def checkClosed(self): + if abs(self._points[-1]['p'] - self._startPoint) < 0.001: + self._isClosed = True + + def closePath(self): + self._points.append({'type': Path.LINE, 'p': self._startPoint}) + self._isClosed = True + + def getPoints(self, accuracy = 1): + pointList = [self._m(self._startPoint)] + p1 = self._startPoint + for p in self._points: + if p['type'] == Path.LINE: + p1 = p['p'] + pointList.append(self._m(p1)) + elif p['type'] == Path.ARC: + p2 = p['p'] + rot = math.radians(p['rot']) + r = p['radius'] + + #http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter + diff = (p1 - p2) / 2 + p1alt = diff #TODO: apply rot + p2alt = -diff #TODO: apply rot + rx2 = r.real*r.real + ry2 = r.imag*r.imag + x1alt2 = p1alt.real*p1alt.real + y1alt2 = p1alt.imag*p1alt.imag + + f = x1alt2 / rx2 + y1alt2 / ry2 + if f >= 1.0: + r *= math.sqrt(f+0.000001) + rx2 = r.real*r.real + ry2 = r.imag*r.imag + + f = math.sqrt((rx2*ry2 - rx2*y1alt2 - ry2*x1alt2) / (rx2*y1alt2+ry2*x1alt2)) + if p['large'] == p['sweep']: + f = -f + cAlt = f * complex(r.real*p1alt.imag/r.imag, -r.imag*p1alt.real/r.real) + + c = cAlt + (p1 + p2) / 2 #TODO: apply rot + + a1 = math.atan2((p1alt.imag - cAlt.imag) / r.imag, (p1alt.real - cAlt.real) / r.real) + a2 = math.atan2((p2alt.imag - cAlt.imag) / r.imag, (p2alt.real - cAlt.real) / r.real) + + large = abs(a2 - a1) > math.pi + if large != p['large']: + if a1 < a2: + a1 += math.pi * 2 + else: + a2 += math.pi * 2 + + pCenter = self._m(c + complex(math.cos(a1 + 0.5*(a2-a1)) * r.real, math.sin(a1 + 0.5*(a2-a1)) * r.imag)) + dist = abs(pCenter - self._m(p1)) + abs(pCenter - self._m(p2)) + segments = int(dist / accuracy) + 1 + for n in xrange(1, segments): + pointList.append(self._m(c + complex(math.cos(a1 + n*(a2-a1)/segments) * r.real, math.sin(a1 + n*(a2-a1)/segments) * r.imag))) + + pointList.append(self._m(p2)) + p1 = p2 + elif p['type'] == Path.CURVE: + p1_ = self._m(p1) + p2 = self._m(p['p']) + cp1 = self._m(p['cp1']) + cp2 = self._m(p['cp2']) + + pCenter = p1_*0.5*0.5*0.5 + cp1*3.0*0.5*0.5*0.5 + cp2*3.0*0.5*0.5*0.5 + p2*0.5*0.5*0.5 + dist = abs(pCenter - p1_) + abs(pCenter - p2) + segments = int(dist / accuracy) + 1 + for n in xrange(1, segments): + f = n / float(segments) + g = 1.0-f + point = p1_*g*g*g + cp1*3.0*g*g*f + cp2*3.0*g*f*f + p2*f*f*f + pointList.append(point) + + pointList.append(p2) + p1 = p['p'] + + return pointList + + #getSVGPath returns an SVG path string. Ths path string is not perfect when matrix transformations are involved. + def getSVGPath(self): + p0 = self._m(self._startPoint) + ret = 'M %f %f ' % (p0.real, p0.imag) + for p in self._points: + if p['type'] == Path.LINE: + p0 = self._m(p['p']) + ret += 'L %f %f' % (p0.real, p0.imag) + elif p['type'] == Path.ARC: + p0 = self._m(p['p']) + radius = p['radius'] + ret += 'A %f %f 0 %d %d %f %f' % (radius.real, radius.imag, 1 if p['large'] else 0, 1 if p['sweep'] else 0, p0.real, p0.imag) + elif p['type'] == Path.CURVE: + p0 = self._m(p['p']) + cp1 = self._m(p['cp1']) + cp2 = self._m(p['cp2']) + ret += 'C %f %f %f %f %f %f' % (cp1.real, cp1.imag, cp2.real, cp2.imag, p0.real, p0.imag) + + return ret + + def _m(self, p): + tmp = numpy.matrix([p.real, p.imag, 1], numpy.float64) * self._matrix + return complex(tmp[0,0], tmp[0,1]) + +def saveAsHtml(paths, filename): + f = open(filename, "w") + + posMax = complex(-1000, -1000) + posMin = complex( 1000, 1000) + for path in paths.paths: + points = path.getPoints() + for p in points: + if p.real > posMax.real: + posMax = complex(p.real, posMax.imag) + if p.imag > posMax.imag: + posMax = complex(posMax.real, p.imag) + if p.real < posMin.real: + posMin = complex(p.real, posMin.imag) + if p.imag < posMin.imag: + posMin = complex(posMin.real, p.imag) + + f.write("\n") + f.write("\n" % ((posMax - posMin).real, (posMax - posMin).imag)) + f.write("\n") + f.write("") + f.write("\n") + + f.write("\n") + f.write("") + f.write("\n") + + f.write("\n") + f.write("") + f.close() diff --git a/Cura/util/drawingLoader/dxf.py b/Cura/util/drawingLoader/dxf.py new file mode 100644 index 00000000..3915572b --- /dev/null +++ b/Cura/util/drawingLoader/dxf.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import +__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License" + +import math +import re +import sys +import numpy +from xml.etree import ElementTree + +from Cura.util.drawingLoader import drawing + +class DXF(object): + def __init__(self, filename): + self.paths = [] + self._lastLine = None + self._polyLine = None + + entityType = 'UNKNOWN' + sectionName = 'UNKNOWN' + activeObject = None + f = open(filename, "r") + while True: + groupCode = f.readline().strip() + if groupCode == '': + break + groupCode = int(groupCode) + value = f.readline().strip() + if groupCode == 0: + if sectionName == 'ENTITIES': + self._checkForNewPath(entityType, activeObject) + entityType = value + activeObject = {} + elif entityType == 'SECTION': + if groupCode == 2: + sectionName = value + else: + activeObject[groupCode] = value + if sectionName == 'ENTITIES': + self._checkForNewPath(entityType, activeObject) + f.close() + + for path in self.paths: + if not path.isClosed(): + path.checkClosed() + + def _checkForNewPath(self, type, obj): + if type == 'LINE': + if self._lastLine is not None and self._lastLinePoint == complex(float(obj[10]), float(obj[20])): + self._lastLine.addLineTo(float(obj[11]), float(obj[21])) + else: + p = drawing.Path(float(obj[10]), float(obj[20])) + p.addLineTo(float(obj[11]), float(obj[21])) + self.paths.append(p) + self._lastLine = p + self._lastLinePoint = complex(float(obj[11]), float(obj[21])) + elif type == 'POLYLINE': + self._polyLine = None + elif type == 'VERTEX': + if self._polyLine is None: + self._polyLine = drawing.Path(float(obj[10]), float(obj[20])) + self.paths.append(self._polyLine) + else: + self._polyLine.addLineTo(float(obj[10]), float(obj[20])) + else: + print type + +if __name__ == '__main__': + for n in xrange(1, len(sys.argv)): + print 'File: %s' % (sys.argv[n]) + dxf = DXF(sys.argv[n]) + + drawing.saveAsHtml(dxf, "test_export.html") + diff --git a/Cura/util/svg.py b/Cura/util/drawingLoader/svg.py similarity index 69% rename from Cura/util/svg.py rename to Cura/util/drawingLoader/svg.py index b91fcc3c..e57b3533 100644 --- a/Cura/util/svg.py +++ b/Cura/util/drawingLoader/svg.py @@ -7,6 +7,8 @@ import sys import numpy from xml.etree import ElementTree +from Cura.util.drawingLoader import drawing + def applyTransformString(matrix, transform): while transform != '': if transform[0] == ',': @@ -50,139 +52,6 @@ def toFloat(f): f = re.search('^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', f).group(0) return float(f) -class Path(object): - LINE = 0 - ARC = 1 - CURVE = 2 - - def __init__(self, x, y, matrix): - self._matrix = matrix - self._relMatrix = numpy.matrix([[matrix[0,0],matrix[0,1]], [matrix[1,0],matrix[1,1]]]) - self._startPoint = complex(x, y) - self._points = [] - - def addLineTo(self, x, y): - self._points.append({'type': Path.LINE, 'p': complex(x, y)}) - - def addArcTo(self, x, y, rot, rx, ry, large, sweep): - self._points.append({ - 'type': Path.ARC, - 'p': complex(x, y), - 'rot': rot, - 'radius': complex(rx, ry), - 'large': large, - 'sweep': sweep - }) - - def addCurveTo(self, x, y, cp1x, cp1y, cp2x, cp2y): - self._points.append({ - 'type': Path.CURVE, - 'p': complex(x, y), - 'cp1': complex(cp1x, cp1y), - 'cp2': complex(cp2x, cp2y) - }) - - def closePath(self): - self._points.append({'type': Path.LINE, 'p': self._startPoint}) - - def getPoints(self, accuracy = 1): - pointList = [self._m(self._startPoint)] - p1 = self._startPoint - for p in self._points: - if p['type'] == Path.LINE: - p1 = p['p'] - pointList.append(self._m(p1)) - elif p['type'] == Path.ARC: - p2 = p['p'] - rot = math.radians(p['rot']) - r = p['radius'] - - #http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter - diff = (p1 - p2) / 2 - p1alt = diff #TODO: apply rot - p2alt = -diff #TODO: apply rot - rx2 = r.real*r.real - ry2 = r.imag*r.imag - x1alt2 = p1alt.real*p1alt.real - y1alt2 = p1alt.imag*p1alt.imag - - f = x1alt2 / rx2 + y1alt2 / ry2 - if f >= 1.0: - r *= math.sqrt(f+0.000001) - rx2 = r.real*r.real - ry2 = r.imag*r.imag - - f = math.sqrt((rx2*ry2 - rx2*y1alt2 - ry2*x1alt2) / (rx2*y1alt2+ry2*x1alt2)) - if p['large'] == p['sweep']: - f = -f - cAlt = f * complex(r.real*p1alt.imag/r.imag, -r.imag*p1alt.real/r.real) - - c = cAlt + (p1 + p2) / 2 #TODO: apply rot - - a1 = math.atan2((p1alt.imag - cAlt.imag) / r.imag, (p1alt.real - cAlt.real) / r.real) - a2 = math.atan2((p2alt.imag - cAlt.imag) / r.imag, (p2alt.real - cAlt.real) / r.real) - - large = abs(a2 - a1) > math.pi - if large != p['large']: - if a1 < a2: - a1 += math.pi * 2 - else: - a2 += math.pi * 2 - - pCenter = self._m(c + complex(math.cos(a1 + 0.5*(a2-a1)) * r.real, math.sin(a1 + 0.5*(a2-a1)) * r.imag)) - dist = abs(pCenter - self._m(p1)) + abs(pCenter - self._m(p2)) - segments = int(dist / accuracy) + 1 - for n in xrange(1, segments): - pointList.append(self._m(c + complex(math.cos(a1 + n*(a2-a1)/segments) * r.real, math.sin(a1 + n*(a2-a1)/segments) * r.imag))) - - pointList.append(self._m(p2)) - p1 = p2 - elif p['type'] == Path.CURVE: - p1_ = self._m(p1) - p2 = self._m(p['p']) - cp1 = self._m(p['cp1']) - cp2 = self._m(p['cp2']) - - pCenter = p1_*0.5*0.5*0.5 + cp1*3.0*0.5*0.5*0.5 + cp2*3.0*0.5*0.5*0.5 + p2*0.5*0.5*0.5 - dist = abs(pCenter - p1_) + abs(pCenter - p2) - segments = int(dist / accuracy) + 1 - for n in xrange(1, segments): - f = n / float(segments) - g = 1.0-f - point = p1_*g*g*g + cp1*3.0*g*g*f + cp2*3.0*g*f*f + p2*f*f*f - pointList.append(point) - - pointList.append(p2) - p1 = p['p'] - - return pointList - - def getSVGPath(self): - p0 = self._m(self._startPoint) - ret = 'M %f %f ' % (p0.real, p0.imag) - for p in self._points: - if p['type'] == Path.LINE: - p0 = self._m(p['p']) - ret += 'L %f %f' % (p0.real, p0.imag) - elif p['type'] == Path.ARC: - p0 = self._m(p['p']) - radius = p['radius'] - ret += 'A %f %f 0 %d %d %f %f' % (radius.real, radius.imag, 1 if p['large'] else 0, 1 if p['sweep'] else 0, p0.real, p0.imag) - elif p['type'] == Path.CURVE: - p0 = self._m(p['p']) - cp1 = self._m(p['cp1']) - cp2 = self._m(p['cp2']) - ret += 'C %f %f %f %f %f %f' % (cp1.real, cp1.imag, cp2.real, cp2.imag, p0.real, p0.imag) - - return ret - - def _m(self, p): - tmp = numpy.matrix([p.real, p.imag, 1], numpy.float64) * self._matrix - return complex(tmp[0,0], tmp[0,1]) - def _r(self, p): - tmp = numpy.matrix([p.real, p.imag], numpy.float64) * self._relMatrix - return complex(tmp[0,0], tmp[0,1]) - class SVG(object): def __init__(self, filename): self.tagProcess = {} @@ -229,6 +98,10 @@ class SVG(object): self._xml = None f.close() + for path in self.paths: + if not path.isClosed(): + path.checkClosed() + def _processGTag(self, tag, baseMatrix): for e in tag: if e.get('transform') is None: @@ -264,20 +137,20 @@ class SVG(object): y1 = toFloat(tag.get('y1', '0')) x2 = toFloat(tag.get('x2', '0')) y2 = toFloat(tag.get('y2', '0')) - p = Path(x1, y1, matrix) + p = drawing.Path(x1, y1, matrix) p.addLineTo(x2, y2) self.paths.append(p) def _processPolylineTag(self, tag, matrix): values = map(toFloat, re.split('[, \t]+', tag.get('points', '').strip())) - p = Path(values[0], values[1], matrix) + p = drawing.Path(values[0], values[1], matrix) for n in xrange(2, len(values)-1, 2): p.addLineTo(values[n], values[n+1]) self.paths.append(p) def _processPolygonTag(self, tag, matrix): values = map(toFloat, re.split('[, \t]+', tag.get('points', '').strip())) - p = Path(values[0], values[1], matrix) + p = drawing.Path(values[0], values[1], matrix) for n in xrange(2, len(values)-1, 2): p.addLineTo(values[n], values[n+1]) p.closePath() @@ -287,7 +160,7 @@ class SVG(object): cx = toFloat(tag.get('cx', '0')) cy = toFloat(tag.get('cy', '0')) r = toFloat(tag.get('r', '0')) - p = Path(cx-r, cy, matrix) + p = drawing.Path(cx-r, cy, matrix) p.addArcTo(cx+r, cy, 0, r, r, False, False) p.addArcTo(cx-r, cy, 0, r, r, False, False) self.paths.append(p) @@ -297,7 +170,7 @@ class SVG(object): cy = toFloat(tag.get('cy', '0')) rx = toFloat(tag.get('rx', '0')) ry = toFloat(tag.get('rx', '0')) - p = Path(cx-rx, cy, matrix) + p = drawing.Path(cx-rx, cy, matrix) p.addArcTo(cx+rx, cy, 0, rx, ry, False, False) p.addArcTo(cx-rx, cy, 0, rx, ry, False, False) self.paths.append(p) @@ -327,8 +200,7 @@ class SVG(object): ry = 0.0 if rx > 0 and ry > 0: - p = Path(x+rx, y, matrix) - p.addLineTo(x+width-rx, y) + p = drawing.Path(x+width-rx, y, matrix) p.addArcTo(x+width,y+ry, 0, rx, ry, False, True) p.addLineTo(x+width, y+height-ry) p.addArcTo(x+width-rx,y+height, 0, rx, ry, False, True) @@ -336,9 +208,10 @@ class SVG(object): p.addArcTo(x,y+height-ry, 0, rx, ry, False, True) p.addLineTo(x, y+ry) p.addArcTo(x+rx,y, 0, rx, ry, False, True) + p.closePath() self.paths.append(p) else: - p = Path(x, y, matrix) + p = drawing.Path(x, y, matrix) p.addLineTo(x,y+height) p.addLineTo(x+width,y+height) p.addLineTo(x+width,y) @@ -364,7 +237,7 @@ class SVG(object): if command == 'm': x += params[0] y += params[1] - path = Path(x, y, matrix) + path = drawing.Path(x, y, matrix) self.paths.append(path) params = params[2:] while len(params) > 1: @@ -376,7 +249,7 @@ class SVG(object): elif command == 'M': x = params[0] y = params[1] - path = Path(x, y, matrix) + path = drawing.Path(x, y, matrix) self.paths.append(path) params = params[2:] while len(params) > 1: @@ -522,28 +395,4 @@ if __name__ == '__main__': print 'File: %s' % (sys.argv[n]) svg = SVG(sys.argv[n]) - f = open("test_export.html", "w") - - f.write("\n") - f.write("\n" % (1000, 1000)) - f.write("\n") - f.write("") - f.write("\n") - - f.write("\n") - f.write("") - f.write("\n") - - f.write("\n") - f.write("") - f.close() - + drawing.saveAsHtml(svg, "test_export.html") -- 2.30.2