diff options
Diffstat (limited to 'Master/texmf-dist/asymptote/GUI')
87 files changed, 10086 insertions, 4351 deletions
diff --git a/Master/texmf-dist/asymptote/GUI/CubicBezier.py b/Master/texmf-dist/asymptote/GUI/CubicBezier.py deleted file mode 100755 index 2eb62577b0a..00000000000 --- a/Master/texmf-dist/asymptote/GUI/CubicBezier.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# Convert a Bezier curve to a polyline -# -# Once Tk supports "RawCurves" this will not be needed. -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -########################################################################### -import math - -def norm(vector): - """Return the norm of a vector""" - return math.sqrt(vector[0]**2+vector[1]**2) - -def splitLine(end0,end1,t): - """Split a line at the distance t, with t in (0,1)""" - return (end0[0]+t*(end1[0]-end0[0]),end0[1]+t*(end1[1]-end0[1])) - -def splitBezier(node0,control0,control1,node1,t): - """Find the nodes and control points for the segments of a Bezier curve split at t""" - a = splitLine(node0,control0,t) - b = splitLine(control0,control1,t) - c = splitLine(control1,node1,t) - d = splitLine(a,b,t) - e = splitLine(b,c,t) - f = splitLine(d,e,t)#this is the point on the curve at t - return ([node0,a,d,f],[f,e,c,node1]) - -def BezierWidth(node0,control0,control1,node1): - """Compute the distance of the control points from the node-node axis""" - deltax = node1[0] - node0[0] - deltay = node1[1] - node0[1] - length = norm((deltax,deltay)) - if length == 0: - y1 = control0[1]-node0[1] - y2 = control1[1]-node0[1] - else: - cosine = deltax/length - sine = deltay/length - y1 = cosine*(control0[1]-node0[1])-sine*(control0[0]-node0[0]) - y2 = cosine*(control1[1]-node0[1])-sine*(control1[0]-node0[0]) - if y1*y2 >= 0: - #same sign - return max(abs(y1),abs(y2)) - else: - #opposite sign - return abs(y1)+abs(y2) - -#If the above algorithm fails, this one will work, but it is far from elegant -#def computeIntermediates(steps,node0,control0,control1,node1): - #pointList = [] - #for a in range(0,100,100/steps)+[100]: - #t = a/100.0 - #t1 = 1-t - #x = node0[0]*t1**3+3*control0[0]*t*t1**2+3*control1[0]*t**2*t1+node1[0]*t**3 - #y = node0[1]*t1**3+3*control0[1]*t*t1**2+3*control1[1]*t**2*t1+node1[1]*t**3 - #pointList.append((x,y)) - #return pointList -#def makeBezier(steps,node0,control0,control1,node1): - #if len(node0)!=2 or len(control0)!=2 or len(control1)!=2 or len(node1)!=2: - #return -1 - #else: - #return [node0]+computeIntermediates(steps,node0,control0,control1,node1)+[node1] - -def makeBezierIntermediates(node0,control0,control1,node1,epsilon): - """Find the points, excluding node0, to be used as the line segment endpoints""" - if(BezierWidth(node0,control0,control1,node1) <= epsilon): - return [node1] - else: - splitUp = splitBezier(node0,control0,control1,node1,0.5) - return makeBezierIntermediates(*splitUp[0]+[epsilon])+makeBezierIntermediates(*splitUp[1]+[epsilon]) - -def makeBezier(node0,control0,control1,node1,epsilon=1): - """Return the vertices to be used in the polyline representation of a Bezier curve""" - return [node0]+makeBezierIntermediates(node0,control0,control1,node1,epsilon) - -if __name__ == '__main__': - pointList = makeBezier((-80,0),(-150,40),(150,120),(80,0),0.5) - from timeit import Timer - t = Timer('makeBezier((-80,0),(-40,-40),(40,120),(80,0),1)','from __main__ import makeBezier') - print (pointList) - print (len(pointList)) - iterations = 1000 - time = t.timeit(iterations) - print ("{:d} iterations took {:f} seconds ({:f} ms for each).".format(iterations,time,1000.0*time/iterations)) - points = [] - for point in pointList: - points.append(point[0]) - points.append(-point[1]) - if sys.version_info >= (3, 0): - from tkinter import * - else: - from Tkinter import * - root = Tk() - canv = Canvas(root,scrollregion=(-100,-100,100,100)) - canv.pack() - canv.create_line(points) - for point in pointList: - canv.create_oval(point[0],-point[1],point[0],-point[1],fill='red',outline='red') - root.mainloop() diff --git a/Master/texmf-dist/asymptote/GUI/CustMatTransform.py b/Master/texmf-dist/asymptote/GUI/CustMatTransform.py new file mode 100755 index 00000000000..563907fc502 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/CustMatTransform.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +import numpy as np +import xasy2asy as x2a +from pyUIClass.custMatTransform import Ui_Dialog + + +class CustMatTransform(Qw.QDialog): + def __init__(self): + super().__init__() + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + self.ui.btnAccept.clicked.connect(self.accept) + self.ui.btnCancel.clicked.connect(self.reject) + self.ui.btnReset.clicked.connect(self.resetDialog) + + self.mainTransformation = Qg.QTransform() + self.mainTransformation.scale(1, -1) + + self.matrixLineInputs = [ + self.ui.lineMat00, self.ui.lineMat01, self.ui.lineMatTx, + self.ui.lineMat10, self.ui.lineMat11, self.ui.lineMatTy] + + validator = Qg.QDoubleValidator() + for lineInput in self.matrixLineInputs: + lineInput.setValidator(validator) + lineInput.textChanged.connect(self.handleUpdateText) + + def show(self): + super().show() + self.createCanvas() + self.updatePreview() + + def createCanvas(self): + self.canvSize = self.ui.imgPreview.size() + self.previewPixmap = Qg.QPixmap(self.canvSize) + tx, ty = self.canvSize.width() / 2, self.canvSize.height() / 2 + self.mainTransformation.translate(tx, -ty) + + def handleUpdateText(self, text): + if str(text) not in {'.', '-', '.-', '-.'} and str(text): + self.updatePreview() + self.ui.btnAccept.setEnabled(True) + else: + self.previewPixmap.fill() + self.ui.imgPreview.setPixmap(self.previewPixmap) + self.ui.btnAccept.setEnabled(False) + + def updatePreview(self): + self.previewPixmap.fill() + canvas = Qg.QPainter(self.previewPixmap) + if not canvas.isActive(): + return + canvas.setTransform(self.mainTransformation) + + canvas.save() + canvas.setPen(Qc.Qt.lightGray) + self.drawBasicGrid(canvas) + transform = x2a.asyTransform.fromNumpyMatrix(self.getTransformationMatrix()) + canvTransform = transform.toQTransform() + canvas.setTransform(canvTransform, True) + + canvas.setPen(Qc.Qt.black) + + if canvTransform.isInvertible(): + self.drawBasicGrid(canvas, False) + + if canvTransform.determinant() <= 0: + canvas.setPen(Qc.Qt.red) + + canvas.drawRect(Qc.QRect(Qc.QPoint(0, 0), Qc.QSize(20, 20))) + + self.ui.imgPreview.setPixmap(self.previewPixmap) + + def resetDialog(self): + self.ui.lineMatTx.setText('0') + self.ui.lineMatTx.setText('0') + + self.ui.lineMat00.setText('1') + self.ui.lineMat01.setText('0') + self.ui.lineMat10.setText('0') + self.ui.lineMat11.setText('1') + + def drawBasicGrid(self, canvas, grid=True): + canvas.drawLine(Qc.QLine(-9999, 0, 9999, 0)) + canvas.drawLine(Qc.QLine(0, -9999, 0, 9999)) + + fromIter, toIter = -7, 7 + gridSize = 20 + if grid: + for iterIndex in range(fromIter, toIter + 1): + canvas.drawLine(Qc.QLine(-9999, iterIndex * gridSize, 9999, iterIndex * gridSize)) + canvas.drawLine(Qc.QLine(iterIndex * gridSize, -9999, iterIndex * gridSize, 9999)) + + def getTransformationMatrix(self): + rawMatrixNum = [float(lineInput.text()) for lineInput in self.matrixLineInputs] + rawMatrixNum.extend([0, 0, 1]) + return np.matrix(rawMatrixNum).reshape((3, 3)) diff --git a/Master/texmf-dist/asymptote/GUI/DebugFlags.py b/Master/texmf-dist/asymptote/GUI/DebugFlags.py new file mode 100755 index 00000000000..8a7eb4b282e --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/DebugFlags.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +keepFiles = False +printFoutTranscript = False +printDeconstTranscript = False +forceRasterizationSVG = False diff --git a/Master/texmf-dist/asymptote/GUI/GuidesManager.py b/Master/texmf-dist/asymptote/GUI/GuidesManager.py new file mode 100755 index 00000000000..5467843377a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/GuidesManager.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +import numpy as np + +class Guide: + def __init__(self, pen=None): + if pen is None: + pen = Qg.QPen() + assert isinstance(pen, Qg.QPen) + self.pen = pen + + def drawShape(self, pen): + assert isinstance(pen, Qg.QPainter) + pen.save() + pen.setPen(self.pen) + +class LineGuide(Guide): + def __init__(self, origin, direction, pen=None): + super().__init__(pen) + self.origin = origin + self.direction = direction + + def drawShape(self, pen): + super().drawShape(pen) + p1 = self.origin + (9999 * Qc.QPointF(np.cos(self.direction), np.sin(self.direction))) + p2 = self.origin - (9999 * Qc.QPointF(np.cos(self.direction), np.sin(self.direction))) + pen.drawLine(Qc.QLineF(p1, p2)) + pen.restore() + +class ArcGuide(Guide): + @classmethod + def radTo16Deg(cls, radians): + return int(round(np.rad2deg(radians) * 16)) + + def __init__(self, center=None, radius=1, startAng=0, endAng=(2*np.pi), pen=None): + if center is None: + center = Qc.QPointF(0, 0) + super().__init__(pen) + self.center = center + self.radius = int(radius) + self.startAng = startAng + self.endAng = endAng + + def drawShape(self, pen): + super().drawShape(pen) + assert isinstance(pen, Qg.QPainter) + x, y = int(round(self.center.x())), int(round(self.center.y())) + pen.drawArc(x - self.radius, y - self.radius, 2 * self.radius, 2 * self.radius, ArcGuide.radTo16Deg(self.startAng), + -ArcGuide.radTo16Deg(self.endAng - self.startAng)) + pen.restore() diff --git a/Master/texmf-dist/asymptote/GUI/InplaceAddObj.py b/Master/texmf-dist/asymptote/GUI/InplaceAddObj.py new file mode 100755 index 00000000000..132969dad19 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/InplaceAddObj.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python3 + +import PyQt5.QtCore as Qc +import PyQt5.QtGui as Qg +import xasy2asy as x2a + +import PrimitiveShape +import math + +import Widg_addPolyOpt +import Widg_addLabel + + +class InplaceObjProcess(Qc.QObject): + objectCreated = Qc.pyqtSignal(Qc.QObject) + objectUpdated = Qc.pyqtSignal() + + def __init__(self, parent=None): + super().__init__(parent) + self._active = False + pass + + @property + def active(self): + return self._active + + def mouseDown(self, pos, info, mouseEvent: Qg.QMouseEvent=None): + raise NotImplementedError + + def mouseMove(self, pos, event: Qg.QMouseEvent): + raise NotImplementedError + + def mouseRelease(self): + raise NotImplementedError + + def forceFinalize(self): + raise NotImplementedError + + def getPreview(self): + return None + + def getObject(self): + raise NotImplementedError + + def getXasyObject(self): + raise NotImplementedError + + def postDrawPreview(self, canvas: Qg.QPainter): + pass + + def createOptWidget(self, info): + return None + + +class AddCircle(InplaceObjProcess): + def __init__(self, parent=None): + super().__init__(parent) + self.center = Qc.QPointF(0, 0) + self.radius = 0 + + def mouseDown(self, pos, info, mouseEvent: Qg.QMouseEvent=None): + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + self.radius = 0 + self.center.setX(x) + self.center.setY(y) + self.fill = info['fill'] + self._active = True + + def mouseMove(self, pos, event): + self.radius = PrimitiveShape.PrimitiveShape.euclideanNorm(pos, self.center) + + def mouseRelease(self): + self.objectCreated.emit(self.getXasyObject()) + self._active = False + + def getPreview(self): + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(self.center) + boundRect = Qc.QRectF(x - self.radius, y - self.radius, 2 * self.radius, 2 * self.radius) + # because the internal image is flipped... + newPath = Qg.QPainterPath() + newPath.addEllipse(boundRect) + # newPath.addRect(boundRect) + return newPath + + def getObject(self): + return PrimitiveShape.PrimitiveShape.circle(self.center, self.radius) + + def getXasyObject(self): + if self.fill: + newObj = x2a.xasyFilledShape(self.getObject(), None) + else: + newObj = x2a.xasyShape(self.getObject(), None) + return newObj + + def forceFinalize(self): + self.mouseRelease() + + +class AddLabel(InplaceObjProcess): + def __init__(self, parent=None): + super().__init__(parent) + self.alignMode = None + self.opt = None + self.text = None + self.anchor = Qc.QPointF(0, 0) + self._active = False + + def createOptWidget(self, info): + self.opt = Widg_addLabel.Widg_addLabel(info) + return self.opt + + def getPreview(self): + return None + + def mouseRelease(self): + self.objectCreated.emit(self.getXasyObject()) + self._active = False + + def mouseMove(self, pos, event): + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + self.anchor.setX(x) + self.anchor.setY(y) + + def mouseDown(self, pos, info, mouseEvent: Qg.QMouseEvent=None): + if self.opt is not None: + self.text = self.opt.labelText + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + self.anchor.setX(x) + self.anchor.setY(y) + + self.alignMode = info['align'] + self.fontSize = info['fontSize'] + self._active = True + + def getObject(self): + finalTuple = PrimitiveShape.PrimitiveShape.pos_to_tuple(self.anchor) + return {'txt': self.text, 'align': str(self.alignMode), 'anchor': finalTuple} + + def getXasyObject(self): + text = self.text + align = str(self.alignMode) + anchor = PrimitiveShape.PrimitiveShape.pos_to_tuple(self.anchor) + newLabel = x2a.xasyText(text=text, location=anchor, pen=None, + align=align, asyengine=None, fontsize=self.fontSize) + newLabel.asyfied = False + return newLabel + + def forceFinalize(self): + self.mouseRelease() + + +class AddBezierShape(InplaceObjProcess): + def __init__(self, parent=None): + super().__init__(parent) + self.asyengine = None + self.basePath = None + self.basePathPreview = None + self.closedPath = None + self.info = None + self.fill = False + self.opt = None + + # list of "committed" points with Linkage information. + # Linkmode should be to the last point. + # (x, y, linkmode), (u, v, lm2) <==> (x, y) <=lm2=> (u, v) + self.pointsList = [] + self.currentPoint = Qc.QPointF(0, 0) + self.pendingPoint = None + self.useLegacy = False + + def mouseDown(self, pos, info, mouseEvent: Qg.QMouseEvent=None): + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + self.currentPoint.setX(x) + self.currentPoint.setY(y) + self.info = info + + if not self._active: + self._active = True + self.fill = info['fill'] + self.asyengine = info['asyengine'] + self.closedPath = info['closedPath'] + self.useBezierBase = info['useBezier'] + self.useLegacy = self.info['options']['useLegacyDrawMode'] + self.pointsList.clear() + self.pointsList.append((x, y, None)) + else: + # see http://doc.qt.io/archives/qt-4.8/qt.html#MouseButton-enum + if (int(mouseEvent.buttons()) if mouseEvent is not None else 0) & 0x2 and self.useLegacy: + self.forceFinalize() + + def _getLinkType(self): + if self.info['useBezier']: + return '..' + else: + return '--' + + def mouseMove(self, pos, event): + # in postscript coords. + if self._active: + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + + if self.useLegacy or int(event.buttons()) != 0: + self.currentPoint.setX(x) + self.currentPoint.setY(y) + else: + self.forceFinalize() + + + def createOptWidget(self, info): + return None + # self.opt = Widg_addBezierInPlace.Widg_addBezierInplace(info) + # return self.opt + + def finalizeClosure(self): + if self.active: + self.closedPath = True + self.forceFinalize() + + def mouseRelease(self): + x, y = self.currentPoint.x(), self.currentPoint.y() + self.pointsList.append((x, y, self._getLinkType())) + # self.updateBasePath() + + def updateBasePath(self): + self.basePath = x2a.asyPath(asyengine=self.asyengine, forceCurve=self.useBezierBase) + newNode = [(x, y) for x, y, _ in self.pointsList] + newLink = [lnk for *args, lnk in self.pointsList[1:]] + if self.useLegacy: + newNode += [(self.currentPoint.x(), self.currentPoint.y())] + newLink += [self._getLinkType()] + if self.closedPath: + newNode.append('cycle') + newLink.append(self._getLinkType()) + self.basePath.initFromNodeList(newNode, newLink) + + if self.useBezierBase: + self.basePath.computeControls() + + def updateBasePathPreview(self): + self.basePathPreview = x2a.asyPath( + asyengine=self.asyengine, forceCurve=self.useBezierBase) + newNode = [(x, y) for x, y, _ in self.pointsList] + [(self.currentPoint.x(), self.currentPoint.y())] + newLink = [lnk for *args, lnk in self.pointsList[1:]] + [self._getLinkType()] + if self.closedPath: + newNode.append('cycle') + newLink.append(self._getLinkType()) + self.basePathPreview.initFromNodeList(newNode, newLink) + + if self.useBezierBase: + self.basePathPreview.computeControls() + + def forceFinalize(self): + self.updateBasePath() + self._active = False + self.pointsList.clear() + self.objectCreated.emit(self.getXasyObject()) + self.basePath = None + + def getObject(self): + if self.basePath is None: + raise RuntimeError('BasePath is None') + self.basePath.asyengine = self.asyengine + return self.basePath + + def getPreview(self): + if self._active: + if self.pointsList: + self.updateBasePathPreview() + newPath = self.basePathPreview.toQPainterPath() + return newPath + + def getXasyObject(self): + if self.fill: + return x2a.xasyFilledShape(self.getObject(), None) + else: + return x2a.xasyShape(self.getObject(), None) + + +class AddPoly(InplaceObjProcess): + def __init__(self, parent=None): + super().__init__(parent) + self.center = Qc.QPointF(0, 0) + self.currPos = Qc.QPointF(0, 0) + self.sides = None + self.inscribed = None + self.centermode = None + self.asyengine = None + self.fill = None + self.opt = None + + def mouseDown(self, pos, info, mouseEvent: Qg.QMouseEvent=None): + self._active = True + self.sides = info['sides'] + self.inscribed = info['inscribed'] + self.centermode = info['centermode'] + self.fill = info['fill'] + + + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + self.center.setX(x) + self.center.setY(y) + self.currPos = Qc.QPointF(self.center) + + def mouseMove(self, pos, event): + x, y = PrimitiveShape.PrimitiveShape.pos_to_tuple(pos) + self.currPos.setX(x) + self.currPos.setY(y) + + def mouseRelease(self): + if self.active: + self.objectCreated.emit(self.getXasyObject()) + self._active = False + + def forceFinalize(self): + self.mouseRelease() + + def getObject(self): + if self.inscribed: + return PrimitiveShape.PrimitiveShape.inscribedRegPolygon(self.sides, self.center, self._rad(), + self._angle()) + else: + return PrimitiveShape.PrimitiveShape.exscribedRegPolygon(self.sides, self.center, self._rad(), + self._angle()) + + def getPreview(self): + if self.inscribed: + poly = PrimitiveShape.PrimitiveShape.inscribedRegPolygon(self.sides, self.center, self._rad(), + self._angle(), qpoly=True) + else: + poly = PrimitiveShape.PrimitiveShape.exscribedRegPolygon(self.sides, self.center, self._rad(), + self._angle(), qpoly=True) + newPath = Qg.QPainterPath() + newPath.addPolygon(poly) + return newPath + + def createOptWidget(self, info): + self.opt = Widg_addPolyOpt.Widg_addPolyOpt(info) + return self.opt + + def _rad(self): + return PrimitiveShape.PrimitiveShape.euclideanNorm(self.currPos, self.center) + + def _angle(self): + dist_x = self.currPos.x() - self.center.x() + dist_y = self.currPos.y() - self.center.y() + if dist_x == 0 and dist_y == 0: + return 0 + else: + return math.atan2(dist_y, dist_x) + + def getXasyObject(self): + if self.fill: + newObj = x2a.xasyFilledShape(self.getObject(), None) + else: + newObj = x2a.xasyShape(self.getObject(), None) + return newObj diff --git a/Master/texmf-dist/asymptote/GUI/PrimitiveShape.py b/Master/texmf-dist/asymptote/GUI/PrimitiveShape.py new file mode 100755 index 00000000000..c75e014079f --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/PrimitiveShape.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import xasy2asy as x2a +import numpy as np +import math +import PyQt5.QtCore as Qc +import PyQt5.QtGui as Qg + + +class PrimitiveShape: + # The magic number. + # see https://www.desmos.com/calculator/lw6j7khikj for unitcircle + # optimal_ctl_pt = 0.5447 + + @staticmethod + def pos_to_tuple(pos): + if isinstance(pos, tuple) or isinstance(pos, np.ndarray): + return pos + elif isinstance(pos, Qc.QPoint) or isinstance(pos, Qc.QPointF): + return pos.x(), pos.y() + else: + raise TypeError("Position must be a valid type!") + + @staticmethod + def euclideanNorm(p1, p2): + x1, y1 = PrimitiveShape.pos_to_tuple(p1) + x2, y2 = PrimitiveShape.pos_to_tuple(p2) + + normSq = ((x1 - x2) ** 2) + ((y1 - y2) ** 2) + return math.sqrt(normSq) + + @classmethod + def circle(cls, position, radius): + pos_x, pos_y = PrimitiveShape.pos_to_tuple(position) + newCircle = x2a.asyPath() + ptsList = [(pos_x + radius, pos_y), (pos_x, pos_y + radius), (pos_x - radius, pos_y), (pos_x, pos_y - radius), + 'cycle'] + # cycle doesn't work for now. + lkList = ['..', '..', '..', '..'] + newCircle.initFromNodeList(ptsList, lkList) + return newCircle + + @classmethod + def inscribedRegPolygon(cls, sides, position, radius, starting_rad, qpoly=False): + pos_x, pos_y = PrimitiveShape.pos_to_tuple(position) + lkList = ['--'] * sides + ptsList = [] + for ang in np.linspace(starting_rad, starting_rad + math.tau, sides, endpoint=False): + ptsList.append((pos_x + radius * math.cos(ang), pos_y + radius * math.sin(ang))) + + if qpoly: + ptsList.append((pos_x + radius * math.cos(starting_rad), pos_y + radius * math.sin(starting_rad))) + qpoints = [Qc.QPointF(x, y) for (x, y) in ptsList] + return Qg.QPolygonF(qpoints) + else: + ptsList.append('cycle') + newPoly = x2a.asyPath() + newPoly.initFromNodeList(ptsList, lkList) + return newPoly + + @classmethod + def exscribedRegPolygon(cls, sides, position, length, starting_rad, qpoly=False): + ang = math.tau/sides + # see notes + adjusted_radius = length / math.cos(ang/2) + return cls.inscribedRegPolygon(sides, position, adjusted_radius, starting_rad - ang/2, qpoly) diff --git a/Master/texmf-dist/asymptote/GUI/SetCustomAnchor.py b/Master/texmf-dist/asymptote/GUI/SetCustomAnchor.py new file mode 100755 index 00000000000..ad5925cddb4 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/SetCustomAnchor.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +from pyUIClass.setCustomAnchor import Ui_Dialog + + +class CustomAnchorDialog(Qw.QDialog): + + def __init__(self): + super().__init__() + self.ui = Ui_Dialog() + self.ui.setupUi(self) + self.ui.buttonBox.button(Qw.QDialogButtonBox.Reset).clicked.connect(self.resetDialog) + + validator = Qg.QDoubleValidator() + + self.ui.lineEditX.setValidator(validator) + self.ui.lineEditY.setValidator(validator) + + self.ui.lineEditX.textChanged.connect(self.checkTextChanged) + self.ui.lineEditY.textChanged.connect(self.checkTextChanged) + + def checkTextChanged(self, text): + if str(text) not in {'.', '-', '.-', '-.'} and str(text): + self.ui.buttonBox.button(Qw.QDialogButtonBox.Ok).setEnabled(True) + else: + self.ui.buttonBox.button(Qw.QDialogButtonBox.Ok).setEnabled(False) + + def getPoint(self): + xPoint = float(self.ui.lineEditX.text()) + yPoint = float(self.ui.lineEditY.text()) + + return Qc.QPointF(xPoint, yPoint) + + def handleBtnBoxClick(self, button): + assert isinstance(button, Qw.QAbstractButton) + if button.text() == 'Reset': + self.resetDialog() + + def resetDialog(self): + self.ui.lineEditX.setText('0') + self.ui.lineEditY.setText('0') diff --git a/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py b/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py index 779d0330a81..7285b15ff2f 100755 --- a/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py +++ b/Master/texmf-dist/asymptote/GUI/UndoRedoStack.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ########################################################################### # # UndoRedoStack implements the usual undo/redo capabilities of a GUI @@ -8,110 +8,125 @@ # ########################################################################### + class action: - def __init__(self,act,inv): - self.act = act - self.inv = inv - def undo(self): - #print ("Undo:",self) - self.inv() - def redo(self): - #print ("Redo:",self) - self.act() - def __str__(self): - return "A generic action" + def __init__(self, actions): + act, inv = actions + self.act = act + self.inv = inv + + def undo(self): + # print ("Undo:",self) + self.inv() + + def redo(self): + # print ("Redo:",self) + self.act() + + def __str__(self): + return "A generic action" + class beginActionGroup: - pass + pass + class endActionGroup: - pass + pass + class actionStack: - def __init__(self): - self.clear() - - def add(self,action): - self.undoStack.append(action) - #print ("Added",action) - self.redoStack = [] - - def undo(self): - if len(self.undoStack) > 0: - op = self.undoStack.pop() - if op is beginActionGroup: - level = 1 - self.redoStack.append(endActionGroup) - while level > 0: - op=self.undoStack.pop() - if op is endActionGroup: - level -= 1 - self.redoStack.append(beginActionGroup) - elif op is beginActionGroup: - level += 1 - self.redoStack.append(endActionGroup) - else: - op.undo() - self.redoStack.append(op) - elif op is endActionGroup: - raise Exception("endActionGroup without previous beginActionGroup") - else: - self.redoStack.append(op) - op.undo() - #print ("undid",op) - else: - pass #print ("nothing to undo") - - def redo(self): - if len(self.redoStack) > 0: - op = self.redoStack.pop() - if op is beginActionGroup: - level = 1 - self.undoStack.append(endActionGroup) - while level > 0: - op = self.redoStack.pop() - if op is endActionGroup: - level -= 1 - self.undoStack.append(beginActionGroup) - elif op is beginActionGroup: - level += 1 - self.undoStack.append(endActionGroup) - else: - op.redo() - self.undoStack.append(op) - elif op is endActionGroup: - raise Exception("endActionGroup without previous beginActionGroup") - else: - self.undoStack.append(op) - op.redo() - #print ("redid",op) - else: - pass #print ("nothing to redo") - - def setCommitLevel(self): - self.commitLevel = len(self.undoStack) - - def changesMade(self): - if len(self.undoStack) != self.commitLevel: - return True - else: - return False - - def clear(self): - self.redoStack = [] - self.undoStack = [] - self.commitLevel = 0 - -if __name__=='__main__': - import sys - def opq(): - print ("action1") - def unopq(): - print ("inverse1") - q = action(opq,unopq) - w = action(lambda:sys.stdout.write("action2\n"),lambda:sys.stdout.write("inverse2\n")) - e = action(lambda:sys.stdout.write("action3\n"),lambda:sys.stdout.write("inverse3\n")) - s = actionStack() - s.add(q) - s.add(w) - s.add(e) + def __init__(self): + self.clear() + + def add(self, action): + self.undoStack.append(action) + # print ("Added",action) + self.redoStack = [] + + def undo(self): + if len(self.undoStack) > 0: + op = self.undoStack.pop() + if op is beginActionGroup: + level = 1 + self.redoStack.append(endActionGroup) + while level > 0: + op = self.undoStack.pop() + if op is endActionGroup: + level -= 1 + self.redoStack.append(beginActionGroup) + elif op is beginActionGroup: + level += 1 + self.redoStack.append(endActionGroup) + else: + op.undo() + self.redoStack.append(op) + elif op is endActionGroup: + raise Exception("endActionGroup without previous beginActionGroup") + else: + self.redoStack.append(op) + op.undo() + # print ("undid",op) + else: + pass # print ("nothing to undo") + + def redo(self): + if len(self.redoStack) > 0: + op = self.redoStack.pop() + if op is beginActionGroup: + level = 1 + self.undoStack.append(endActionGroup) + while level > 0: + op = self.redoStack.pop() + if op is endActionGroup: + level -= 1 + self.undoStack.append(beginActionGroup) + elif op is beginActionGroup: + level += 1 + self.undoStack.append(endActionGroup) + else: + op.redo() + self.undoStack.append(op) + elif op is endActionGroup: + raise Exception("endActionGroup without previous beginActionGroup") + else: + self.undoStack.append(op) + op.redo() + # print ("redid",op) + else: + pass # print ("nothing to redo") + + def setCommitLevel(self): + self.commitLevel = len(self.undoStack) + + def changesMade(self): + if len(self.undoStack) != self.commitLevel: + return True + else: + return False + + def clear(self): + self.redoStack = [] + self.undoStack = [] + self.commitLevel = 0 + + +if __name__ == '__main__': + import sys + + + def opq(): + print("action1") + + + def unopq(): + print("inverse1") + + + q = action(opq, unopq) + w = action(lambda: sys.stdout.write("action2\n"), lambda: sys.stdout.write("inverse2\n")) + e = action(lambda: sys.stdout.write("action3\n"), lambda: sys.stdout.write("inverse3\n")) + s = actionStack() + s.add(q) + s.add(w) + s.add(e) diff --git a/Master/texmf-dist/asymptote/GUI/Widg_addLabel.py b/Master/texmf-dist/asymptote/GUI/Widg_addLabel.py new file mode 100755 index 00000000000..776d3e02df9 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/Widg_addLabel.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +from pyUIClass.widg_addLabel import Ui_Form +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg + +import labelEditor +import xasyUtils as xu + + +class Widg_addLabel(Qw.QWidget): + def __init__(self, info): + super().__init__() + self.ui = Ui_Form() + self.info = info + self.ui.setupUi(self) + self.setFixedSize(self.size()) + + if 'alignIndex' not in self.info.keys(): + self.info['alignIndex'] = 0 + + if 'shift_x' not in self.info.keys(): + self.info['shift_x'] = None + + if 'shift_y' not in self.info.keys(): + self.info['shift_y'] = None + + if 'align' not in self.info.keys(): + self.info['align'] = (0, 0) + + if self.info['shift_x'] is not None: + self.ui.txtShiftX.setText(str(self.info['shift_x'])) + + if self.info['shift_y'] is not None: + self.ui.txtShiftY.setText(str(self.info['shift_y'])) + + + self.ui.cmbFontSize.setCurrentText(str(self.info['fontSize']) if self.info['fontSize'] is not None else '-') + self.ui.cmbAlign.setCurrentIndex(self.info['alignIndex']) + + validator = Qg.QDoubleValidator() + + self.ui.txtShiftX.setValidator(validator) + self.ui.txtShiftY.setValidator(validator) + self.ui.cmbFontSize.setValidator(validator) + + self.ui.cmbAlign.currentTextChanged.connect(self.updateCheck) + self.ui.cmbAlign.currentIndexChanged.connect(self.cmbIndexUpdate) + self.ui.txtShiftX.textEdited.connect(self.shftXUpdate) + self.ui.txtShiftY.textEdited.connect(self.shftYUpdate) + self.ui.btnAdvancedEdit.clicked.connect(self.btnAdvancedEditClicked) + self.ui.cmbFontSize.currentTextChanged.connect(self.cmbFontSizeTextChanged) + + self.updateCheck(self.ui.cmbAlign.currentText()) + + def cmbFontSizeTextChanged(self, text: str): + tryParseVal = xu.tryParse(text, float) + self.info['fontSize'] = tryParseVal + + def btnAdvancedEditClicked(self): + advancedEditDialog = labelEditor.labelEditor(self.ui.txtLabelText.text()) + advancedEditDialog.show() + result = advancedEditDialog.exec_() + if result == Qw.QDialog.Accepted: + self.ui.txtLabelText.setText(advancedEditDialog.getText()) + + @property + def labelText(self): + return self.ui.txtLabelText.text() + + def updateCheck(self, a0): + self.ui.txtShiftX.setEnabled(a0 == 'Custom') + self.ui.txtShiftY.setEnabled(a0 == 'Custom') + + def shftXUpdate(self, text): + if text: + self.info['shift_x'] = float(text) + self.updateAlign() + + def shftYUpdate(self, text): + if text: + self.info['shift_y'] = float(text) + self.updateAlign() + + def updateAlign(self): + index = self.ui.cmbAlign.currentIndex() + self.info['alignIndex'] = index + if self.ui.cmbAlign.currentText() == 'Custom': + self.info['align'] = (self.info['shift_x'], self.info['shift_y']) + elif self.ui.cmbAlign.currentText() == 'None': + self.info['align'] = (0, 0) + else: + self.info['align'] = self.ui.cmbAlign.currentText() + + def cmbIndexUpdate(self, index): + self.updateAlign() diff --git a/Master/texmf-dist/asymptote/GUI/Widg_addPolyOpt.py b/Master/texmf-dist/asymptote/GUI/Widg_addPolyOpt.py new file mode 100755 index 00000000000..b9b9a77f7a1 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/Widg_addPolyOpt.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +from pyUIClass.widg_addPolyOpt import Ui_Form +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc + +import sys + + +class Widg_addPolyOpt(Qw.QWidget): + def __init__(self, info): + super().__init__() + self.ui = Ui_Form() + self.info = info + self.ui.setupUi(self) + self.setFixedSize(self.size()) + + self.ui.chkInscribed.setChecked(self.info['inscribed']) + self.ui.txtSides.setText(str(self.info['sides'])) + self.ui.txtSides.setValidator(Qg.QIntValidator()) + + self.ui.chkInscribed.stateChanged.connect(self.chkInscribedUpdate) + self.ui.txtSides.textChanged.connect(self.txtSidesUpdate) + + def chkInscribedUpdate(self, checked): + self.info['inscribed'] = checked + + def txtSidesUpdate(self, text): + if text: + self.info['sides'] = int(text) diff --git a/Master/texmf-dist/asymptote/GUI/Widg_editBezier.py b/Master/texmf-dist/asymptote/GUI/Widg_editBezier.py new file mode 100755 index 00000000000..3a3cbab94ab --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/Widg_editBezier.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +from pyUIClass.widg_editBezier import Ui_Form + +import PyQt5.QtCore as Qc +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg + +class LockMode: + noLock = 0 + angleLock = 1 + angleAndScaleLock = 2 + +class Widg_editBezier(Qw.QWidget): + def __init__(self, info: dict, enableCurveFeatures: bool=True): + super().__init__() + self.ui = Ui_Form() + self.ui.setupUi(self) + self.info = info + + self.ui.chkRecompute.setChecked(self.info['autoRecompute']) + self.ui.cmbLockMode.setCurrentIndex(self.info['editBezierlockMode']) + + self.ui.cmbLockMode.currentIndexChanged[int].connect(self.cmbLockIndexChange) + self.ui.chkRecompute.stateChanged.connect(self.chkRecomputeChanged) + + self.disableOnAutoRecompute = {self.ui.cmbLockMode, self.ui.btnForceRecompute} + self.curveBtnsOnly = {self.ui.cmbLockMode, self.ui.btnForceRecompute, self.ui.chkRecompute} + + for elem in self.curveBtnsOnly: + elem.setEnabled(enableCurveFeatures) + + @property + def autoRecompute(self) -> bool: + return self.ui.chkRecompute.isChecked() + + @property + def lockMode(self) -> int: + return self.ui.cmbLockMode.currentIndex() + + @Qc.pyqtSlot(int) + def cmbLockIndexChange(self, index: int): + self.info['editBezierlockMode'] = index + + @Qc.pyqtSlot(int) + def chkRecomputeChanged(self, checked: int): + isChecked = (checked == 2) + for obj in self.disableOnAutoRecompute: + obj.setEnabled(not checked) + self.info['autoRecompute'] = checked + + if isChecked: + self.ui.btnForceRecompute.clicked.emit() diff --git a/Master/texmf-dist/asymptote/GUI/Window1.py b/Master/texmf-dist/asymptote/GUI/Window1.py new file mode 100755 index 00000000000..8497b27e5cf --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/Window1.py @@ -0,0 +1,1890 @@ +#!/usr/bin/env python3 + +from pyUIClass.window1 import Ui_MainWindow + +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +import xasyVersion + +import numpy as np +import os +import json +import io +import pathlib +import webbrowser +import subprocess +import tempfile +import datetime +import string +import atexit + +import xasyUtils as xu +import xasy2asy as x2a +import xasyFile as xf +import xasyOptions as xo +import UndoRedoStack as Urs +import xasyArgs as xa +import xasyBezierInterface as xbi +from xasyTransform import xasyTransform as xT +import xasyStrings as xs + +import PrimitiveShape +import InplaceAddObj + +import CustMatTransform +import SetCustomAnchor +import GuidesManager + + +class ActionChanges: + pass + + +# State Invariance: When ActionChanges is at the top, all state of the program & file +# is exactly like what it was the event right after that ActionChanges was created. + +class TransformationChanges(ActionChanges): + def __init__(self, objIndex, transformation, isLocal=False): + self.objIndex = objIndex + self.transformation = transformation + self.isLocal = isLocal + + +class ObjCreationChanges(ActionChanges): + def __init__(self, obj): + self.object = obj + +class HardDeletionChanges(ActionChanges): + def __init__(self, obj, pos): + self.item = obj + self.objIndex = pos + +class AnchorMode: + origin = 0 + center = 1 + topLeft = 2 + topRight = 3 + bottomRight = 4 + bottomLeft = 5 + customAnchor = 6 + + +class GridMode: + cartesian = 0 + polar = 1 + + +class SelectionMode: + select = 0 + pan = 1 + translate = 2 + rotate = 3 + scale = 4 + delete = 5 + setAnchor = 6 + selectEdit = 7 + +class AddObjectMode: + Circle = 0 + Arc = 1 + Polygon = 2 + +class MainWindow1(Qw.QMainWindow): + defaultFrameStyle = """ + QFrame{{ + padding: 4.0; + border-radius: 3.0; + background: rgb({0}, {1}, {2}) + }} + """ + + def __init__(self): + super().__init__() + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + + self.settings = xo.BasicConfigs.defaultOpt + self.keyMaps = xo.BasicConfigs.keymaps + + self.raw_args = Qc.QCoreApplication.arguments() + self.args = xa.parseArgs(self.raw_args) + + self.strings = xs.xasyString(self.args.language) + self.asy2psmap = x2a.identity() + + if self.settings['asyBaseLocation'] is not None: + os.environ['ASYMPTOTE_DIR'] = self.settings['asyBaseLocation'] + + if self.args.asypath is not None: + asyPath = self.args.asypath + else: + asyPath = self.settings['asyPath'] + + self.asyPath = asyPath + self.asyEngine = x2a.AsymptoteEngine(self.asyPath) + + try: + self.asyEngine.start() + finally: + atexit.register(self.asyEngine.cleanup) + + # For initialization purposes + self.canvSize = Qc.QSize() + self.filename = None + self.currDir = None + self.mainCanvas = None + self.dpi = 300 + self.canvasPixmap = None + + # Actions + # <editor-fold> Connecting Actions + self.ui.txtLineWidth.setValidator(Qg.QDoubleValidator()) + + self.connectActions() + self.connectButtons() + + self.ui.txtLineWidth.returnPressed.connect(self.btnTerminalCommandOnClick) + # </editor-fold> + + # Base Transformations + + self.mainTransformation = Qg.QTransform() + self.mainTransformation.scale(1, 1) + self.localTransform = Qg.QTransform() + self.screenTransformation = Qg.QTransform() + self.panTranslation = Qg.QTransform() + + # Internal Settings + self.magnification = self.args.mag + self.inMidTransformation = False + self.addMode = None + self.currentlySelectedObj = {'key': None, 'allSameKey': set(), 'selectedIndex': None, 'keyIndex': None} + self.pendingSelectedObjList = [] + self.pendingSelectedObjIndex = -1 + + self.savedMousePosition = None + self.currentBoundingBox = None + self.selectionDelta = None + self.newTransform = None + self.origBboxTransform = None + self.deltaAngle = 0 + self.scaleFactor = 1 + self.panOffset = [0, 0] + + super().setMouseTracking(True) + # setMouseTracking(True) + + self.undoRedoStack = Urs.actionStack() + + self.lockX = False + self.lockY = False + self.anchorMode = AnchorMode.center + self.currentAnchor = Qc.QPointF(0, 0) + self.customAnchor = None + self.useGlobalCoords = True + self.drawAxes = True + self.drawGrid = False + self.gridSnap = False # TODO: for now. turn it on later + + self.fileChanged = False + + self.terminalPythonMode = self.ui.btnTogglePython.isChecked() + + self.savedWindowMousePos = None + + self.finalPixmap = None + self.preCanvasPixmap = None + self.postCanvasPixmap = None + self.previewCurve = None + self.mouseDown = False + + self.globalObjectCounter = 0 + + self.fileItems = [] + self.drawObjects = [] + self.xasyDrawObj = {'drawDict': self.drawObjects} + + self.modeButtons = { + self.ui.btnTranslate, self.ui.btnRotate, self.ui.btnScale, # self.ui.btnSelect, + self.ui.btnPan, self.ui.btnDeleteMode, self.ui.btnAnchor, + self.ui.btnSelectEdit + } + + self.objButtons = {self.ui.btnCustTransform, self.ui.actionTransform, self.ui.btnSendForwards, + self.ui.btnSendBackwards, self.ui.btnToggleVisible + } + + self.globalTransformOnlyButtons = (self.ui.comboAnchor, self.ui.btnAnchor) + + self.ui.txtTerminalPrompt.setFont(Qg.QFont(self.settings['terminalFont'])) + + self.currAddOptionsWgt = None + self.currAddOptions = { + 'options': self.settings, + 'inscribed': True, + 'sides': 3, + 'centermode': True, + 'fontSize': None, + 'asyengine': self.asyEngine, + 'fill': self.ui.btnFill.isChecked(), + 'closedPath': False, + 'useBezier': True, + 'magnification': self.magnification, + 'editBezierlockMode': xbi.Web.LockMode.angleLock, + 'autoRecompute': False + } + + + self.currentModeStack = [SelectionMode.translate] + self.drawGridMode = GridMode.cartesian + self.setAllInSetEnabled(self.objButtons, False) + self._currentPen = x2a.asyPen() + self.currentGuides = [] + self.selectAsGroup = self.settings['groupObjDefault'] + + # commands switchboard + self.commandsFunc = { + 'quit': Qc.QCoreApplication.quit, + 'undo': self.btnUndoOnClick, + 'redo': self.btnRedoOnClick, + 'manual': self.actionManual, + 'about': self.actionAbout, + 'loadFile': self.btnLoadFileonClick, + 'save': self.actionSave, + 'saveAs': self.actionSaveAs, + 'transform': self.btnCustTransformOnClick, + 'commandPalette': self.enterCustomCommand, + 'clearGuide': self.clearGuides, + 'finalizeAddObj': self.finalizeAddObj, + 'finalizeCurve': self.finalizeCurve, + 'finalizeCurveClosed': self.finalizeCurveClosed, + 'setMag': self.setMagPrompt, + 'deleteObject': self.btnSelectiveDeleteOnClick, + 'anchorMode': self.switchToAnchorMode, + 'moveUp': lambda: self.arrowButtons(0, 1, False), + 'moveDown': lambda: self.arrowButtons(0, -1, False), + + 'scrollLeft': lambda: self.arrowButtons(-1, 0, True), + 'scrollRight': lambda: self.arrowButtons(1, 0, True), + 'scrollUp': lambda: self.arrowButtons(0, 1, True), + 'scrollDown': lambda: self.arrowButtons(0, -1, True), + + 'zoomIn': lambda: self.arrowButtons(0, 1, False, True), + 'zoomOut': lambda: self.arrowButtons(0, -1, False, True) + } + + self.hiddenKeys = set() + + # Coordinates Label + + self.coordLabel = Qw.QLabel(self.ui.statusbar) + self.ui.statusbar.addPermanentWidget(self.coordLabel) + + # Settings Initialization + # from xasyoptions config file + self.loadKeyMaps() + self.setupXasyOptions() + + self.colorDialog = Qw.QColorDialog(x2a.asyPen.convertToQColor(self._currentPen.color), self) + self.initPenInterface() + + def arrowButtons(self, x:int , y:int, shift: bool=False, ctrl: bool=False): + "x, y indicates update button orientation on the cartesian plane." + if not (shift or ctrl): + self.changeSelection(y) + elif not (shift and ctrl): + self.mouseWheel(30*x, 30*y) + self.quickUpdate() + + def cleanup(self): + self.asyengine.cleanup() + + def getScrsTransform(self): + # pipeline: + # assuming origin <==> top left + # (Pan) * (Translate) * (Flip the images) * (Zoom) * (Obj transform) * (Base Information) + + # pipeline --> let x, y be the postscript point + # p = (mx + cx + panoffset, -ny + cy + panoffset) + cx, cy = self.canvSize.width() / 2, self.canvSize.height() / 2 + + newTransf = Qg.QTransform() + newTransf.translate(*self.panOffset) + newTransf.translate(cx, cy) + newTransf.scale(1, 1) + newTransf.scale(self.magnification, self.magnification) + + return newTransf + + def finalizeCurve(self): + if self.addMode is not None: + if self.addMode.active and isinstance(self.addMode, InplaceAddObj.AddBezierShape): + self.addMode.forceFinalize() + self.fileChanged = True + + def finalizeCurveClosed(self): + if self.addMode is not None: + if self.addMode.active and isinstance(self.addMode, InplaceAddObj.AddBezierShape): + self.addMode.finalizeClosure() + self.fileChanged = True + + def getAllBoundingBox(self) -> Qc.QRectF: + newRect = Qc.QRectF() + for majitem in self.drawObjects: + for minitem in majitem: + newRect = newRect.united(minitem.boundingBox) + return newRect + + def finalizeAddObj(self): + if self.addMode is not None: + if self.addMode.active: + self.addMode.forceFinalize() + self.fileChanged = True + + def openAndReloadSettings(self): + settingsFile = self.settings.settingsFileLocation() + subprocess.run(args=self.getExternalEditor(asypath=settingsFile)) + self.settings.load() + self.quickUpdate() + + def setMagPrompt(self): + commandText, result = Qw.QInputDialog.getText(self, '', 'Enter magnification:') + if result: + self.magnification = float(commandText) + self.currAddOptions['magnification'] = self.magnification + self.quickUpdate() + + def btnTogglePythonOnClick(self, checked): + self.terminalPythonMode = checked + + def internationalize(self): + self.ui.btnRotate.setToolTip(self.strings.rotate) + + def handleArguments(self): + if self.args.filename is not None: + self.loadFile(self.args.filename) + else: + self.initializeEmptyFile() + + if self.args.language != 'en': + self.internationalize() + + def initPenInterface(self): + self.ui.txtLineWidth.setText(str(self._currentPen.width)) + self.updateFrameDispColor() + + def updateFrameDispColor(self): + r, g, b = [int(x * 255) for x in self._currentPen.color] + self.ui.frameCurrColor.setStyleSheet(MainWindow1.defaultFrameStyle.format(r, g, b)) + + def initDebug(self): + debugFunc = { + } + self.commandsFunc = {**self.commandsFunc, **debugFunc} + + def dbgRecomputeCtrl(self): + if isinstance(self.addMode, xbi.InteractiveBezierEditor): + self.addMode.recalculateCtrls() + self.quickUpdate() + + def objectUpdated(self): + self.removeAddMode() + self.clearSelection() + self.asyfyCanvas() + + def connectActions(self): + self.ui.actionQuit.triggered.connect(lambda: self.execCustomCommand('quit')) + self.ui.actionUndo.triggered.connect(lambda: self.execCustomCommand('undo')) + self.ui.actionRedo.triggered.connect(lambda: self.execCustomCommand('redo')) + self.ui.actionTransform.triggered.connect(lambda: self.execCustomCommand('transform')) + + self.ui.actionOpen.triggered.connect(self.actionOpen) + self.ui.actionSave.triggered.connect(self.actionSave) + self.ui.actionSaveAs.triggered.connect(self.actionSaveAs) + self.ui.actionManual.triggered.connect(self.actionManual) + self.ui.actionAbout.triggered.connect(self.actionAbout) + self.ui.actionSettings.triggered.connect(self.openAndReloadSettings) + self.ui.actionEnterCommand.triggered.connect(self.enterCustomCommand) + self.ui.actionExportAsymptote.triggered.connect(self.btnExportAsyOnClick) + + def setupXasyOptions(self): + if self.settings['debugMode']: + self.initDebug() + newColor = Qg.QColor(self.settings['defaultPenColor']) + newWidth = self.settings['defaultPenWidth'] + + self._currentPen.setColorFromQColor(newColor) + self._currentPen.setWidth(newWidth) + + def connectButtons(self): + # Button initialization + self.ui.btnUndo.clicked.connect(self.btnUndoOnClick) + self.ui.btnRedo.clicked.connect(self.btnRedoOnClick) + self.ui.btnLoadFile.clicked.connect(self.btnLoadFileonClick) + self.ui.btnSave.clicked.connect(self.btnSaveonClick) + self.ui.btnQuickScreenshot.clicked.connect(self.btnQuickScreenshotOnClick) + + # self.ui.btnExportAsy.clicked.connect(self.btnExportAsyOnClick) + + self.ui.btnDrawAxes.clicked.connect(self.btnDrawAxesOnClick) +# self.ui.btnAsyfy.clicked.connect(lambda: self.asyfyCanvas(True)) + self.ui.btnSetZoom.clicked.connect(self.setMagPrompt) + self.ui.btnResetPan.clicked.connect(self.resetPan) + self.ui.btnPanCenter.clicked.connect(self.btnPanCenterOnClick) + + self.ui.btnTranslate.clicked.connect(self.btnTranslateonClick) + self.ui.btnRotate.clicked.connect(self.btnRotateOnClick) + self.ui.btnScale.clicked.connect(self.btnScaleOnClick) + # self.ui.btnSelect.clicked.connect(self.btnSelectOnClick) + self.ui.btnPan.clicked.connect(self.btnPanOnClick) + + # self.ui.btnDebug.clicked.connect(self.pauseBtnOnClick) + self.ui.btnAlignX.clicked.connect(self.btnAlignXOnClick) + self.ui.btnAlignY.clicked.connect(self.btnAlignYOnClick) + self.ui.comboAnchor.currentIndexChanged.connect(self.handleAnchorComboIndex) + self.ui.btnCustTransform.clicked.connect(self.btnCustTransformOnClick) + self.ui.btnViewCode.clicked.connect(self.btnLoadEditorOnClick) + + self.ui.btnAnchor.clicked.connect(self.btnAnchorModeOnClick) + + self.ui.btnSelectColor.clicked.connect(self.btnColorSelectOnClick) + self.ui.txtLineWidth.textEdited.connect(self.txtLineWithEdited) + + # self.ui.btnCreateCurve.clicked.connect(self.btnCreateCurveOnClick) + self.ui.btnDrawGrid.clicked.connect(self.btnDrawGridOnClick) + + self.ui.btnAddCircle.clicked.connect(self.btnAddCircleOnClick) + self.ui.btnAddPoly.clicked.connect(self.btnAddPolyOnClick) + self.ui.btnAddLabel.clicked.connect(self.btnAddLabelOnClick) + # self.ui.btnAddBezierInplace.clicked.connect(self.btnAddBezierInplaceOnClick) + self.ui.btnClosedCurve.clicked.connect(self.btnAddClosedCurveOnClick) + self.ui.btnOpenCurve.clicked.connect(self.btnAddOpenCurveOnClick) + self.ui.btnClosedPoly.clicked.connect(self.btnAddClosedLineOnClick) + self.ui.btnOpenPoly.clicked.connect(self.btnAddOpenLineOnClick) + + self.ui.btnFill.clicked.connect(self.btnFillOnClick) + + self.ui.btnSendBackwards.clicked.connect(self.btnSendBackwardsOnClick) + self.ui.btnSendForwards.clicked.connect(self.btnSendForwardsOnClick) + # self.ui.btnDelete.clicked.connect(self.btnSelectiveDeleteOnClick) + self.ui.btnDeleteMode.clicked.connect(self.btnDeleteModeOnClick) + # self.ui.btnSoftDelete.clicked.connect(self.btnSoftDeleteOnClick) + self.ui.btnToggleVisible.clicked.connect(self.btnSetVisibilityOnClick) + + self.ui.btnEnterCommand.clicked.connect(self.btnTerminalCommandOnClick) + self.ui.btnTogglePython.clicked.connect(self.btnTogglePythonOnClick) + self.ui.btnSelectEdit.clicked.connect(self.btnSelectEditOnClick) + + def btnDeleteModeOnClick(self): + self.currentModeStack = [SelectionMode.delete] + self.ui.statusbar.showMessage('Delete Mode') + self.clearSelection() + self.updateChecks() + + def btnTerminalCommandOnClick(self): + if self.terminalPythonMode: + exec(self.ui.txtTerminalPrompt.text()) + self.fileChanged = True + else: + pass + # TODO: How to handle this case? + # Like AutoCAD? + self.ui.txtTerminalPrompt.clear() + + def btnFillOnClick(self, checked): + self.currAddOptions['fill'] = checked + self.ui.btnOpenCurve.setEnabled(not checked) + self.ui.btnOpenPoly.setEnabled(not checked) + + def btnSelectEditOnClick(self): + self.ui.statusbar.showMessage('Edit mode') + self.currentModeStack = [SelectionMode.selectEdit] + self.updateChecks() + + @property + def currentPen(self): + return x2a.asyPen.fromAsyPen(self._currentPen) + pass + def debug(self): + print('Put a breakpoint here.') + + def execPythonCmd(self): + commandText, result = Qw.QInputDialog.getText(self, '', 'enter python cmd') + if result: + exec(commandText) + + def deleteAddOptions(self): + if self.currAddOptionsWgt is not None: + self.currAddOptionsWgt.hide() + self.ui.addOptionLayout.removeWidget(self.currAddOptionsWgt) + self.currAddOptionsWgt = None + + def updateOptionWidget(self): + try: + self.addMode.objectCreated.disconnect() + except Exception: + pass + + self.currentModeStack[-1] = None + self.addMode.objectCreated.connect(self.addInPlace) + self.updateModeBtnsOnly() + + + self.deleteAddOptions() + + self.currAddOptionsWgt = self.addMode.createOptWidget(self.currAddOptions) + if self.currAddOptionsWgt is not None: + self.ui.addOptionLayout.addWidget(self.currAddOptionsWgt) + + def addInPlace(self, obj): + obj.asyengine = self.asyEngine + obj.pen = self.currentPen + obj.onCanvas = self.xasyDrawObj + obj.setKey(str(self.globalObjectCounter)) + self.globalObjectCounter = self.globalObjectCounter + 1 + + self.fileItems.append(obj) + self.fileChanged = True + self.addObjCreationUrs(obj) + self.asyfyCanvas() + + def addObjCreationUrs(self, obj): + newAction = self.createAction(ObjCreationChanges(obj)) + self.undoRedoStack.add(newAction) + self.checkUndoRedoButtons() + + def clearGuides(self): + self.currentGuides.clear() + self.quickUpdate() + + def btnAddCircleOnClick(self): + self.addMode = InplaceAddObj.AddCircle(self) + self.ui.statusbar.showMessage('') + self.updateOptionWidget() + + LegacyHint='Click and drag to draw; right click or space bar to finalize' + Hint='Click and drag to draw; release and click in place to add node; continue dragging' + HintClose=' or c to close.' + + def drawHint(self): + if self.settings['useLegacyDrawMode']: + self.ui.statusbar.showMessage(self.LegacyHint+'.') + else: + self.ui.statusbar.showMessage(self.Hint+'.') + + def drawHintOpen(self): + if self.settings['useLegacyDrawMode']: + self.ui.statusbar.showMessage(self.LegacyHint+self.HintClose) + else: + self.ui.statusbar.showMessage(self.Hint+self.HintClose) + + def btnAddBezierInplaceOnClick(self): + self.addMode = InplaceAddObj.AddBezierShape(self) + self.updateOptionWidget() + + def btnAddOpenLineOnClick(self): + self.currAddOptions['useBezier'] = False + self.currAddOptions['closedPath'] = False + self.drawHintOpen() + self.btnAddBezierInplaceOnClick() + + def btnAddClosedLineOnClick(self): + self.currAddOptions['useBezier'] = False + self.currAddOptions['closedPath'] = True + self.drawHint() + self.btnAddBezierInplaceOnClick() + + def btnAddOpenCurveOnClick(self): + self.currAddOptions['useBezier'] = True + self.currAddOptions['closedPath'] = False + self.drawHintOpen() + self.btnAddBezierInplaceOnClick() + + def btnAddClosedCurveOnClick(self): + self.currAddOptions['useBezier'] = True + self.currAddOptions['closedPath'] = True + self.drawHint() + self.btnAddBezierInplaceOnClick() + + def btnAddPolyOnClick(self): + self.addMode = InplaceAddObj.AddPoly(self) + self.ui.statusbar.showMessage('') + self.updateOptionWidget() + + def btnAddLabelOnClick(self): + self.addMode = InplaceAddObj.AddLabel(self) + self.ui.statusbar.showMessage('') + self.updateOptionWidget() + + def updateCurve(self, valid, newCurve): + self.previewCurve = newCurve + self.quickUpdate() + + def addTransformationChanges(self, objIndex, transform, isLocal=False): + self.undoRedoStack.add(self.createAction(TransformationChanges(objIndex, + transform, isLocal))) + self.checkUndoRedoButtons() + + def btnSendForwardsOnClick(self): + if self.currentlySelectedObj['selectedIndex'] is not None: + maj, minor = self.currentlySelectedObj['selectedIndex'] + selectedObj = self.drawObjects[maj][minor] + index = self.fileItems.index(selectedObj.parent()) + + self.clearSelection() + if index == len(self.fileItems) - 1: + return + else: + self.fileItems[index], self.fileItems[index + 1] = self.fileItems[index + 1], self.fileItems[index] + self.asyfyCanvas() + + def btnSelectiveDeleteOnClick(self): + if self.currentlySelectedObj['selectedIndex'] is not None: + maj, minor = self.currentlySelectedObj['selectedIndex'] + selectedObj = self.drawObjects[maj][minor] + + parent = selectedObj.parent() + + if isinstance(parent, x2a.xasyScript): + self.hiddenKeys.add((selectedObj.key, selectedObj.keyIndex)) + self.softDeleteObj((maj, minor)) + else: + index = self.fileItems.index(selectedObj.parent()) + + self.undoRedoStack.add(self.createAction( + HardDeletionChanges(selectedObj.parent(), index) + )) + self.checkUndoRedoButtons() + + self.fileItems.remove(selectedObj.parent()) + + self.fileChanged = True + self.clearSelection() + self.asyfyCanvas() + else: + result = self.selectOnHover() + if result: + self.btnSelectiveDeleteOnClick() + + def btnSetVisibilityOnClick(self): + if self.currentlySelectedObj['selectedIndex'] is not None: + maj, minor = self.currentlySelectedObj['selectedIndex'] + selectedObj = self.drawObjects[maj][minor] + + self.hiddenKeys.symmetric_difference_update({(selectedObj.key, selectedObj.keyIndex)}) + self.clearSelection() + self.quickUpdate() + + def btnSendBackwardsOnClick(self): + if self.currentlySelectedObj['selectedIndex'] is not None: + maj, minor = self.currentlySelectedObj['selectedIndex'] + selectedObj = self.drawObjects[maj][minor] + index = self.fileItems.index(selectedObj.parent()) + + self.clearSelection() + if index == 0: + return + else: + self.fileItems[index], self.fileItems[index - 1] = self.fileItems[index - 1], self.fileItems[index] + self.asyfyCanvas() + + + def btnUndoOnClick(self): + self.undoRedoStack.undo() + self.checkUndoRedoButtons() + + def btnRedoOnClick(self): + self.undoRedoStack.redo() + self.checkUndoRedoButtons() + + def checkUndoRedoButtons(self): + self.ui.btnUndo.setEnabled(self.undoRedoStack.changesMade()) + self.ui.actionUndo.setEnabled(self.undoRedoStack.changesMade()) + + self.ui.btnRedo.setEnabled(len(self.undoRedoStack.redoStack) > 0) + self.ui.actionRedo.setEnabled(len(self.undoRedoStack.redoStack) > 0) + + def handleUndoChanges(self, change): + assert isinstance(change, ActionChanges) + if isinstance(change, TransformationChanges): + self.transformObject(change.objIndex, change.transformation.inverted(), change.isLocal) + elif isinstance(change, ObjCreationChanges): + self.fileItems.pop() + elif isinstance(change, HardDeletionChanges): + self.fileItems.insert(change.objIndex, change.item) + self.asyfyCanvas() + + def handleRedoChanges(self, change): + assert isinstance(change, ActionChanges) + if isinstance(change, TransformationChanges): + self.transformObject( + change.objIndex, change.transformation, change.isLocal) + elif isinstance(change, ObjCreationChanges): + self.fileItems.append(change.object) + elif isinstance(change, HardDeletionChanges): + self.fileItems.remove(change.item) + self.asyfyCanvas() + + # is this a "pythonic" way? + def createAction(self, changes): + def _change(): + return self.handleRedoChanges(changes) + + def _undoChange(): + return self.handleUndoChanges(changes) + + return Urs.action((_change, _undoChange)) + + def execCustomCommand(self, command): + if command in self.commandsFunc: + self.commandsFunc[command]() + else: + self.ui.statusbar.showMessage('Command {0} not found'.format(command)) + + def enterCustomCommand(self): + commandText, result = Qw.QInputDialog.getText(self, 'Enter Custom Command', 'Enter Custom Command') + if result: + self.execCustomCommand(commandText) + + def addItemFromPath(self, path): + newItem = x2a.xasyShape(path, self.asyEngine, pen=self.currentPen) + self.fileItems.append(newItem) + self.fileChanged = True + self.asyfyCanvas() + + def actionManual(self): + asyManualURL = 'http://asymptote.sourceforge.net/asymptote.pdf' + webbrowser.open_new(asyManualURL) + + def actionAbout(self): + Qw.QMessageBox.about(self,"xasy","This is xasy "+xasyVersion.xasyVersion+"; a graphical front end to the Asymptote vector graphics language: http://asymptote.sourceforge.net/") + + def btnExportAsyOnClick(self): + diag = Qw.QFileDialog(self) + diag.setAcceptMode(Qw.QFileDialog.AcceptSave) + + formatId = { + 'pdf': { + 'name': 'PDF Files', + 'ext': ['*.pdf'] + }, + 'svg': { + 'name': 'Scalable Vector Graphics', + 'ext': ['*.svg'] + }, + 'eps': { + 'name': 'Postscript Files', + 'ext': ['*.eps'] + }, + 'png': { + 'name': 'Portable Network Graphics', + 'ext': ['*.png'] + }, + '*': { + 'name': 'Any Files', + 'ext': ['*.*'] + } + } + + formats = ['pdf', 'svg', 'eps', 'png', '*'] + + formatText = ';;'.join('{0:s} ({1:s})'.format(formatId[form]['name'], ' '.join(formatId[form]['ext'])) + for form in formats) + + if self.currDir is not None: + diag.setDirectory(self.currDir) + rawFile = os.path.splitext(os.path.basename(self.filename))[0] + '.pdf' + diag.selectFile(rawFile) + + diag.setNameFilter(formatText) + diag.show() + result = diag.exec_() + + if result != diag.Accepted: + return + + finalFiles = diag.selectedFiles() + + with io.StringIO() as finalCode: + xf.saveFile(finalCode, self.fileItems, self.asy2psmap) + finalString = finalCode.getvalue() + + for file in finalFiles: + ext = os.path.splitext(file) + if len(ext) < 2: + ext = 'pdf' + else: + ext = ext[1][1:] + + with subprocess.Popen(args=[self.asyPath, '-f{0}'.format(ext), '-o{0}'.format(file), '-'], encoding='utf-8', + stdin=subprocess.PIPE) as asy: + print('test:', finalString) + asy.stdin.write(finalString) + asy.stdin.close() + asy.wait(timeout=35) + + + def loadKeyMaps(self): + """Inverts the mapping of the key + Input map is in format 'Action' : 'Key Sequence' """ + for action, key in self.keyMaps.options.items(): + shortcut = Qw.QShortcut(self) + shortcut.setKey(Qg.QKeySequence(key)) + + # hate doing this, but python doesn't have explicit way to pass a + # string to a lambda without an identifier + # attached to it. + exec('shortcut.activated.connect(lambda: self.execCustomCommand("{0}"))'.format(action), + {'self': self, 'shortcut': shortcut}) + + def initializeButtons(self): + self.ui.btnDrawAxes.setChecked(self.settings['defaultShowAxes']) + self.btnDrawAxesOnClick(self.settings['defaultShowAxes']) + + self.ui.btnDrawGrid.setChecked(self.settings['defaultShowGrid']) + self.btnDrawGridOnClick(self.settings['defaultShowGrid']) + + def erase(self): + self.fileItems.clear() + self.fileChanged = False + + def actionOpen(self): + if self.fileChanged: + save="Save current file?" + reply=Qw.QMessageBox.question(self,'Message',save,Qw.QMessageBox.Yes, + Qw.QMessageBox.No) + if reply == Qw.QMessageBox.Yes: + self.actionSave() + + filename = Qw.QFileDialog.getOpenFileName(self, 'Open Asymptote File','', '*.asy') + if filename[0]: + self.loadFile(filename[0]) + + def actionSave(self): + if self.filename is None: + self.actionSaveAs() + else: + saveFile = io.open(self.filename, 'w') + xf.saveFile(saveFile, self.fileItems, self.asy2psmap) + saveFile.close() + self.updateScript() + + def updateScript(self): + for item in self.fileItems: + if isinstance(item, x2a.xasyScript): + if item.updatedCode: + item.setScript(item.updatedCode) + item.updatedCode = None + + def actionSaveAs(self): + saveLocation = Qw.QFileDialog.getSaveFileName(self, 'Save File')[0] + if saveLocation: + saveFile = io.open(saveLocation, 'w') + xf.saveFile(saveFile, self.fileItems, self.asy2psmap) + saveFile.close() + self.filename = saveLocation + self.updateScript() + + + def btnQuickScreenshotOnClick(self): + saveLocation = Qw.QFileDialog.getSaveFileName(self, 'Save Screenshot','') + if saveLocation[0]: + self.ui.imgLabel.pixmap().save(saveLocation[0]) + + def btnLoadFileonClick(self): + self.actionOpen() + + def btnSaveonClick(self): + self.actionSave() + + @Qc.pyqtSlot(int) + def handleAnchorComboIndex(self, index: int): + self.anchorMode = index + if self.anchorMode == AnchorMode.customAnchor: + if self.customAnchor is not None: + self.anchorMode = AnchorMode.customAnchor + else: + self.ui.comboAnchor.setCurrentIndex(AnchorMode.center) + self.anchorMode = AnchorMode.center + self.quickUpdate() + def btnColorSelectOnClick(self): + self.colorDialog.show() + result = self.colorDialog.exec() + if result == Qw.QDialog.Accepted: + self._currentPen.setColorFromQColor(self.colorDialog.selectedColor()) + self.updateFrameDispColor() + + def txtLineWithEdited(self, text): + new_val = xu.tryParse(text, float) + if new_val is not None: + if new_val > 0: + self._currentPen.setWidth(new_val) + + def isReady(self): + return self.mainCanvas is not None + + def resizeEvent(self, resizeEvent): + # super().resizeEvent(resizeEvent) + assert isinstance(resizeEvent, Qg.QResizeEvent) + + if self.isReady(): + if self.mainCanvas.isActive(): + self.mainCanvas.end() + self.canvSize = self.ui.imgFrame.size() + self.ui.imgFrame.setSizePolicy(Qw.QSizePolicy.Ignored, Qw.QSizePolicy.Ignored) + self.canvasPixmap = Qg.QPixmap(self.canvSize) + # self.canvasPixmap.setDevicePixelRatio(3) + self.postCanvasPixmap = Qg.QPixmap(self.canvSize) + + self.quickUpdate() + + def show(self): + super().show() + self.createMainCanvas() # somehow, the coordinates doesn't get updated until after showing. + self.initializeButtons() + self.postShow() + + def postShow(self): + self.handleArguments() + + def roundPositionSnap(self, oldPoint): + minorGridSize = self.settings['gridMajorAxesSpacing'] / (self.settings['gridMinorAxesCount'] + 1) + if isinstance(oldPoint, list) or isinstance(oldPoint, tuple): + return [round(val / minorGridSize) * minorGridSize for val in oldPoint] + elif isinstance(oldPoint, Qc.QPoint) or isinstance(oldPoint, Qc.QPointF): + x, y = oldPoint.x(), oldPoint.y() + x = round(x / minorGridSize) * minorGridSize + y = round(y / minorGridSize) * minorGridSize + return Qc.QPointF(x, y) + else: + raise Exception + + def getAsyCoordinates(self): + canvasPosOrig = self.getCanvasCoordinates() + return canvasPosOrig, canvasPosOrig + + def mouseMoveEvent(self, mouseEvent: Qg.QMouseEvent): # TODO: Actually refine grid snapping... + if not self.ui.imgLabel.underMouse() and not self.mouseDown: + return + + self.updateMouseCoordLabel() + asyPos, canvasPos = self.getAsyCoordinates() + + # add mode + if self.addMode is not None: + if self.addMode.active: + self.addMode.mouseMove(asyPos, mouseEvent) + self.quickUpdate() + return + + # pan mode + if self.currentModeStack[-1] == SelectionMode.pan and int(mouseEvent.buttons()) and self.savedWindowMousePos is not None: + mousePos = self.getWindowCoordinates() + newPos = mousePos - self.savedWindowMousePos + + tx, ty = newPos.x(), newPos.y() + + if self.lockX: + tx = 0 + if self.lockY: + ty = 0 + + self.panOffset[0] += tx + self.panOffset[1] += ty + + self.savedWindowMousePos = self.getWindowCoordinates() + self.quickUpdate() + return + + # otherwise, in transformation + if self.inMidTransformation: + if self.currentModeStack[-1] == SelectionMode.translate: + newPos = canvasPos - self.savedMousePosition + if self.gridSnap: + newPos = self.roundPositionSnap(newPos) # actually round to the nearest minor grid afterwards... + + self.tx, self.ty = newPos.x(), newPos.y() + + if self.lockX: + self.tx = 0 + if self.lockY: + self.ty = 0 + self.newTransform = Qg.QTransform.fromTranslate(self.tx, self.ty) + + elif self.currentModeStack[-1] == SelectionMode.rotate: + if self.gridSnap: + canvasPos = self.roundPositionSnap(canvasPos) + + adjustedSavedMousePos = self.savedMousePosition - self.currentAnchor + adjustedCanvasCoords = canvasPos - self.currentAnchor + + origAngle = np.arctan2(adjustedSavedMousePos.y(), adjustedSavedMousePos.x()) + newAng = np.arctan2(adjustedCanvasCoords.y(), adjustedCanvasCoords.x()) + self.deltaAngle = newAng - origAngle + self.newTransform = xT.makeRotTransform(self.deltaAngle, self.currentAnchor).toQTransform() + + elif self.currentModeStack[-1] == SelectionMode.scale: + if self.gridSnap: + canvasPos = self.roundPositionSnap(canvasPos) + x, y = int(round(canvasPos.x())), int(round(canvasPos.y())) # otherwise it crashes... + canvasPos = Qc.QPoint(x, y) + + originalDeltaPts = self.savedMousePosition - self.currentAnchor + scaleFactor = Qc.QPointF.dotProduct(canvasPos - self.currentAnchor, originalDeltaPts) /\ + (xu.twonorm((originalDeltaPts.x(), originalDeltaPts.y())) ** 2) + if not self.lockX: + self.scaleFactorX = scaleFactor + else: + self.scaleFactorX = 1 + + if not self.lockY: + self.scaleFactorY = scaleFactor + else: + self.scaleFactorY = 1 + + self.newTransform = xT.makeScaleTransform(self.scaleFactorX, self.scaleFactorY, self.currentAnchor).\ + toQTransform() + + self.quickUpdate() + return + + # otherwise, select a candinate for selection + + if self.currentlySelectedObj['selectedIndex'] is None: + selectedIndex, selKeyList = self.selectObject() + if selectedIndex is not None: + if self.pendingSelectedObjList != selKeyList: + self.pendingSelectedObjList = selKeyList + self.pendingSelectedObjIndex = -1 + else: + self.pendingSelectedObjList.clear() + self.pendingSelectedObjIndex = -1 + self.quickUpdate() + return + + + def mouseReleaseEvent(self, mouseEvent): + assert isinstance(mouseEvent, Qg.QMouseEvent) + if not self.mouseDown: + return + + self.mouseDown = False + if self.addMode is not None: + self.addMode.mouseRelease() + if self.inMidTransformation: + self.clearSelection() + self.inMidTransformation = False + self.quickUpdate() + + def clearSelection(self): + if self.currentlySelectedObj['selectedIndex'] is not None: + self.releaseTransform() + self.setAllInSetEnabled(self.objButtons, False) + self.currentlySelectedObj['selectedIndex'] = None + self.currentlySelectedObj['key'] = None + + self.currentlySelectedObj['allSameKey'].clear() + self.newTransform = Qg.QTransform() + self.currentBoundingBox = None + self.quickUpdate() + + def changeSelection(self, offset): + if self.pendingSelectedObjList: + if offset > 0: + if self.pendingSelectedObjIndex + offset <= -1: + self.pendingSelectedObjIndex = self.pendingSelectedObjIndex + offset + else: + if self.pendingSelectedObjIndex + offset >= -len(self.pendingSelectedObjList): + self.pendingSelectedObjIndex = self.pendingSelectedObjIndex + offset + + def mouseWheel(self, rawAngleX: float, rawAngle: float, defaultModifiers: int=0): + keyModifiers = int(Qw.QApplication.keyboardModifiers()) + keyModifiers = keyModifiers | defaultModifiers + if keyModifiers & int(Qc.Qt.ControlModifier): + oldMag = self.magnification + + cx, cy = self.canvSize.width() / 2, self.canvSize.height() / 2 + centerPoint = Qc.QPointF(cx, cy) * self.getScrsTransform().inverted()[0] + + self.magnification += (rawAngle/100) + + if self.magnification < self.settings['minimumMagnification']: + self.magnification = self.settings['minimumMagnification'] + elif self.magnification > self.settings['maximumMagnification']: + self.magnification = self.settings['maximumMagnification'] + + # set the new pan. Let c be the fixed point (center point), + # Let m the old mag, n the new mag + + # find t2 such that + # mc + t1 = nc + t2 ==> t2 = (m - n)c + t1 + + centerPoint = (oldMag - self.magnification) * centerPoint + + self.panOffset = [ + self.panOffset[0] + centerPoint.x(), + self.panOffset[1] + centerPoint.y() + ] + + self.currAddOptions['magnification'] = self.magnification + + if self.addMode is xbi.InteractiveBezierEditor: + self.addMode.setSelectionBoundaries() + + elif keyModifiers & (int(Qc.Qt.ShiftModifier) | int(Qc.Qt.AltModifier)): + self.panOffset[1] += rawAngle/1 + self.panOffset[0] -= rawAngleX/1 + # handle scrolling + else: + # process selection layer change + if rawAngle >= 15: + self.changeSelection(1) + elif rawAngle <= -15: + self.changeSelection(-1) + self.quickUpdate() + + def wheelEvent(self, event: Qg.QWheelEvent): + rawAngle = event.angleDelta().y() / 8 + rawAngleX = event.angleDelta().x() / 8 + self.mouseWheel(rawAngleX, rawAngle) + + def selectOnHover(self): + """Returns True if selection happened, False otherwise. + """ + if self.pendingSelectedObjList: + selectedIndex = self.pendingSelectedObjList[self.pendingSelectedObjIndex] + self.pendingSelectedObjList.clear() + + maj, minor = selectedIndex + + self.currentlySelectedObj['selectedIndex'] = selectedIndex + self.currentlySelectedObj['key'], self.currentlySelectedObj['allSameKey'] = self.selectObjectSet( + ) + + self.currentBoundingBox = self.drawObjects[maj][minor].boundingBox + + if self.selectAsGroup: + for selItems in self.currentlySelectedObj['allSameKey']: + obj = self.drawObjects[selItems[0]][selItems[1]] + self.currentBoundingBox = self.currentBoundingBox.united(obj.boundingBox) + + self.origBboxTransform = self.drawObjects[maj][minor].transform.toQTransform() + self.newTransform = Qg.QTransform() + return True + else: + return False + + def mousePressEvent(self, mouseEvent: Qg.QMouseEvent): + # we make an exception for bezier curve + bezierException = False + if self.addMode is not None: + if self.addMode.active and isinstance(self.addMode, InplaceAddObj.AddBezierShape): + bezierException = True + + if not self.ui.imgLabel.underMouse() and not bezierException: + return + + self.mouseDown = True + asyPos, self.savedMousePosition = self.getAsyCoordinates() + + if self.addMode is not None: + self.addMode.mouseDown(asyPos, self.currAddOptions, mouseEvent) + elif self.currentModeStack[-1] == SelectionMode.pan: + self.savedWindowMousePos = self.getWindowCoordinates() + elif self.currentModeStack[-1] == SelectionMode.setAnchor: + self.customAnchor = self.savedMousePosition + self.currentModeStack.pop() + + self.anchorMode = AnchorMode.customAnchor + self.ui.comboAnchor.setCurrentIndex(AnchorMode.customAnchor) + self.updateChecks() + self.quickUpdate() + elif self.inMidTransformation: + pass + elif self.pendingSelectedObjList: + self.selectOnHover() + + if self.currentModeStack[-1] in {SelectionMode.translate, SelectionMode.rotate, SelectionMode.scale}: + self.setAllInSetEnabled(self.objButtons, False) + self.inMidTransformation = True + self.setAnchor() + elif self.currentModeStack[-1] == SelectionMode.delete: + self.btnSelectiveDeleteOnClick() + elif self.currentModeStack[-1] == SelectionMode.selectEdit: + self.setupSelectEdit() + else: + self.setAllInSetEnabled(self.objButtons, True) + self.inMidTransformation = False + self.setAnchor() + + else: + self.setAllInSetEnabled(self.objButtons, False) + self.currentBoundingBox = None + self.inMidTransformation = False + self.clearSelection() + + self.quickUpdate() + + def removeAddMode(self): + self.addMode = None + self.deleteAddOptions() + + def editFinalized(self): + self.addMode.forceFinalize() + self.removeAddMode() + self.fileChanged = True + self.quickUpdate() + + def editRejected(self): + self.addMode.resetObj() + self.editFinalized() + + def setupSelectEdit(self): + """For Select-Edit mode. For now, if the object selected is a bezier curve, opens up a bezier editor""" + maj, minor = self.currentlySelectedObj['selectedIndex'] + obj = self.fileItems[maj] + if isinstance(obj, x2a.xasyDrawnItem): + # bezier path + self.addMode = xbi.InteractiveBezierEditor(self, obj, self.currAddOptions) + self.addMode.objectUpdated.connect(self.objectUpdated) + self.addMode.editAccepted.connect(self.editFinalized) + self.addMode.editRejected.connect(self.editRejected) + self.updateOptionWidget() + self.currentModeStack[-1] = SelectionMode.selectEdit + self.fileChanged = True + else: + self.clearSelection() + self.quickUpdate() + + def setAnchor(self): + if self.anchorMode == AnchorMode.center: + self.currentAnchor = self.currentBoundingBox.center() + elif self.anchorMode == AnchorMode.topLeft: + self.currentAnchor = self.currentBoundingBox.bottomLeft() # due to internal image being flipped + elif self.anchorMode == AnchorMode.topRight: + self.currentAnchor = self.currentBoundingBox.bottomRight() + elif self.anchorMode == AnchorMode.bottomLeft: + self.currentAnchor = self.currentBoundingBox.topLeft() + elif self.anchorMode == AnchorMode.bottomRight: + self.currentAnchor = self.currentBoundingBox.topRight() + elif self.anchorMode == AnchorMode.customAnchor: + self.currentAnchor = self.customAnchor + else: + self.currentAnchor = Qc.QPointF(0, 0) + + if self.anchorMode != AnchorMode.origin: + pass + # TODO: Record base points/bbox before hand and use that for + # anchor? + # adjTransform = + # self.drawObjects[selectedIndex].transform.toQTransform() + # self.currentAnchor = adjTransform.map(self.currentAnchor) + + + def releaseTransform(self): + if self.newTransform.isIdentity(): + return + newTransform = x2a.asyTransform.fromQTransform(self.newTransform) + objKey = self.currentlySelectedObj['selectedIndex'] + self.addTransformationChanges(objKey, newTransform, not self.useGlobalCoords) + self.transformObject(objKey, newTransform, not self.useGlobalCoords) + + def adjustTransform(self, appendTransform): + self.screenTransformation = self.screenTransformation * appendTransform + + def createMainCanvas(self): + self.canvSize = self.ui.imgFrame.size() + self.ui.imgFrame.setSizePolicy(Qw.QSizePolicy.Ignored, Qw.QSizePolicy.Ignored) + x, y = self.canvSize.width() / 2, self.canvSize.height() / 2 + + self.canvasPixmap = Qg.QPixmap(self.canvSize) + self.canvasPixmap.fill() + + self.finalPixmap = Qg.QPixmap(self.canvSize) + + self.preCanvasPixmap = Qg.QPixmap(self.canvSize) + self.postCanvasPixmap = Qg.QPixmap(self.canvSize) + + self.mainCanvas = Qg.QPainter(self.canvasPixmap) + self.mainCanvas.setRenderHint(Qg.QPainter.Antialiasing) + self.mainCanvas.setRenderHint(Qg.QPainter.SmoothPixmapTransform) + self.mainCanvas.setRenderHint(Qg.QPainter.HighQualityAntialiasing) + self.xasyDrawObj['canvas'] = self.mainCanvas + + self.mainTransformation = Qg.QTransform() + self.mainTransformation.scale(1, 1) + self.mainTransformation.translate(x, y) + + self.mainCanvas.setTransform(self.getScrsTransform(), True) + + self.ui.imgLabel.setPixmap(self.canvasPixmap) + + def resetPan(self): + self.panOffset = [0, 0] + self.quickUpdate() + + def btnPanCenterOnClick(self): + newCenter = self.getAllBoundingBox().center() + + # adjust to new magnification + # technically, doable through getscrstransform() + # and subtract pan offset and center points + # but it's much more work... + newCenter = self.magnification * newCenter + self.panOffset = [-newCenter.x(), newCenter.y()] + + self.quickUpdate() + + def selectObject(self): + if not self.ui.imgLabel.underMouse(): + return None, [] + canvasCoords = self.getCanvasCoordinates() + highestDrawPriority = -np.inf + collidedObjKey = None + rawObjNumList = [] + for objKeyMaj in range(len(self.drawObjects)): + for objKeyMin in range(len(self.drawObjects[objKeyMaj])): + obj = self.drawObjects[objKeyMaj][objKeyMin] + if obj.collide(canvasCoords) and (obj.key, obj.keyIndex) not in self.hiddenKeys: + rawObjNumList.append(((objKeyMaj, objKeyMin), obj.drawOrder)) + if obj.drawOrder > highestDrawPriority: + collidedObjKey = (objKeyMaj, objKeyMin) + if collidedObjKey is not None: + rawKey = self.drawObjects[collidedObjKey[0]][collidedObjKey[1]].key +# self.ui.statusbar.showMessage('Collide with {0}, Key is {1}'.format(str(collidedObjKey), rawKey), 2500) + self.ui.statusbar.showMessage('Key: {0}'.format(rawKey), 2500) + return collidedObjKey, [rawObj[0] for rawObj in sorted(rawObjNumList, key=lambda ordobj: ordobj[1])] + else: + return None, [] + + def selectObjectSet(self): + objKey = self.currentlySelectedObj['selectedIndex'] + if objKey is None: + return set() + assert isinstance(objKey, (tuple, list)) and len(objKey) == 2 + rawObj = self.drawObjects[objKey[0]][objKey[1]] + rawKey = rawObj.key + rawSet = {objKey} + for objKeyMaj in range(len(self.drawObjects)): + for objKeyMin in range(len(self.drawObjects[objKeyMaj])): + obj = self.drawObjects[objKeyMaj][objKeyMin] + if obj.key == rawKey: + rawSet.add((objKeyMaj, objKeyMin)) + return rawKey, rawSet + + def getCanvasCoordinates(self): + # assert self.ui.imgLabel.underMouse() + uiPos = self.mapFromGlobal(Qg.QCursor.pos()) + canvasPos = self.ui.imgLabel.mapFrom(self, uiPos) + + # Issue: For magnification, should xasy treats this at xasy level, or asy level? + return canvasPos * self.getScrsTransform().inverted()[0] + + def getWindowCoordinates(self): + # assert self.ui.imgLabel.underMouse() + return self.mapFromGlobal(Qg.QCursor.pos()) + + def refreshCanvas(self): + if self.mainCanvas.isActive(): + self.mainCanvas.end() + self.mainCanvas.begin(self.canvasPixmap) + self.mainCanvas.setTransform(self.getScrsTransform()) + + def asyfyCanvas(self, force=False): + self.drawObjects = [] + self.populateCanvasWithItems(force) + self.quickUpdate() + if self.currentModeStack[-1] == SelectionMode.translate: + self.ui.statusbar.showMessage(self.strings.asyfyComplete) + + def updateMouseCoordLabel(self): + *args, canvasPos = self.getAsyCoordinates() + nx, ny = self.asy2psmap.inverted() * (canvasPos.x(), canvasPos.y()) + self.coordLabel.setText('{0:.2f}, {1:.2f} '.format(nx, ny)) + + def quickUpdate(self): + self.updateMouseCoordLabel() + self.refreshCanvas() + + self.preDraw(self.mainCanvas) + self.quickDraw() + + self.mainCanvas.end() + self.postDraw() + self.updateScreen() + + def quickDraw(self): + assert self.isReady() + dpi = self.magnification * self.dpi + activeItem = None + for majorItem in self.drawObjects: + for item in majorItem: + # hidden objects - toggleable + if (item.key, item.keyIndex) in self.hiddenKeys: + continue + isSelected = item.key == self.currentlySelectedObj['key'] + if not self.selectAsGroup and isSelected and self.currentlySelectedObj['selectedIndex'] is not None: + maj, min_ = self.currentlySelectedObj['selectedIndex'] + isSelected = isSelected and item is self.drawObjects[maj][min_] + if isSelected and self.settings['enableImmediatePreview']: + activeItem = item + if self.useGlobalCoords: + item.draw(self.newTransform, canvas=self.mainCanvas, dpi=dpi) + else: + item.draw(self.newTransform, applyReverse=True, canvas=self.mainCanvas, dpi=dpi) + else: + item.draw(canvas=self.mainCanvas, dpi=dpi) + + if self.settings['drawSelectedOnTop']: + if self.pendingSelectedObjList: + maj, minor = self.pendingSelectedObjList[self.pendingSelectedObjIndex] + self.drawObjects[maj][minor].draw(canvas=self.mainCanvas, dpi=dpi) + # and apply the preview too... + elif activeItem is not None: + if self.useGlobalCoords: + activeItem.draw(self.newTransform, canvas=self.mainCanvas, dpi=dpi) + else: + activeItem.draw(self.newTransform, applyReverse=True, canvas=self.mainCanvas, dpi=dpi) + activeItem = None + + def updateScreen(self): + self.finalPixmap = Qg.QPixmap(self.canvSize) + self.finalPixmap.fill(Qc.Qt.black) + with Qg.QPainter(self.finalPixmap) as finalPainter: + drawPoint = Qc.QPoint(0, 0) + # finalPainter.drawPixmap(drawPoint, self.preCanvasPixmap) + finalPainter.drawPixmap(drawPoint, self.canvasPixmap) + finalPainter.drawPixmap(drawPoint, self.postCanvasPixmap) + self.ui.imgLabel.setPixmap(self.finalPixmap) + + def drawCartesianGrid(self, preCanvas): + majorGrid = self.settings['gridMajorAxesSpacing'] * self.asy2psmap.xx + minorGridCount = self.settings['gridMinorAxesCount'] + + majorGridCol = Qg.QColor(self.settings['gridMajorAxesColor']) + minorGridCol = Qg.QColor(self.settings['gridMinorAxesColor']) + + panX, panY = self.panOffset + + x_range = (self.canvSize.width() / 2 + (2 * abs(panX)))/self.magnification + y_range = (self.canvSize.height() / 2 + (2 * abs(panY)))/self.magnification + + for x in np.arange(0, 2 * x_range + 1, majorGrid): # have to do + # this in two stages... + preCanvas.setPen(minorGridCol) + for xMinor in range(1, minorGridCount + 1): + xCoord = x + ((xMinor / (minorGridCount + 1)) * majorGrid) + preCanvas.drawLine(Qc.QLine(xCoord, -9999, xCoord, 9999)) + preCanvas.drawLine(Qc.QLine(-xCoord, -9999, -xCoord, 9999)) + + for y in np.arange(0, 2 * y_range + 1, majorGrid): + preCanvas.setPen(minorGridCol) + for yMinor in range(1, minorGridCount + 1): + yCoord = y + ((yMinor / (minorGridCount + 1)) * majorGrid) + preCanvas.drawLine(Qc.QLine(-9999, yCoord, 9999, yCoord)) + preCanvas.drawLine(Qc.QLine(-9999, -yCoord, 9999, -yCoord)) + + preCanvas.setPen(majorGridCol) + preCanvas.drawLine(Qc.QLine(-9999, y, 9999, y)) + preCanvas.drawLine(Qc.QLine(-9999, -y, 9999, -y)) + + for x in np.arange(0, 2 * x_range + 1, majorGrid): + preCanvas.setPen(majorGridCol) + preCanvas.drawLine(Qc.QLine(x, -9999, x, 9999)) + preCanvas.drawLine(Qc.QLine(-x, -9999, -x, 9999)) + + def drawPolarGrid(self, preCanvas): + center = Qc.QPointF(0, 0) + majorGridCol = Qg.QColor(self.settings['gridMajorAxesColor']) + minorGridCol = Qg.QColor(self.settings['gridMinorAxesColor']) + majorGrid = self.settings['gridMajorAxesSpacing'] + minorGridCount = self.settings['gridMinorAxesCount'] + + majorAxisAng = (np.pi/4) # 45 degrees - for now. + minorAxisCount = 2 # 15 degrees each + + subRadiusSize = int(round((majorGrid / (minorGridCount + 1)))) + subAngleSize = majorAxisAng / (minorAxisCount + 1) + + for radius in range(majorGrid, 9999 + 1, majorGrid): + preCanvas.setPen(majorGridCol) + preCanvas.drawEllipse(center, radius, radius) + + preCanvas.setPen(minorGridCol) + + for minorRing in range(minorGridCount): + subRadius = round(radius - (subRadiusSize * (minorRing + 1))) + preCanvas.drawEllipse(center, subRadius, subRadius) + + currAng = majorAxisAng + while currAng <= (2 * np.pi): + preCanvas.setPen(majorGridCol) + p1 = center + (9999 * Qc.QPointF(np.cos(currAng), np.sin(currAng))) + preCanvas.drawLine(Qc.QLineF(center, p1)) + + preCanvas.setPen(minorGridCol) + for minorAngLine in range(minorAxisCount): + newAng = currAng - (subAngleSize * (minorAngLine + 1)) + p1 = center + (9999 * Qc.QPointF(np.cos(newAng), np.sin(newAng))) + preCanvas.drawLine(Qc.QLineF(center, p1)) + + currAng = currAng + majorAxisAng + + def preDraw(self, painter): + # self.preCanvasPixmap.fill(Qc.Qt.white) + self.canvasPixmap.fill() + preCanvas = painter + + # preCanvas = Qg.QPainter(self.preCanvasPixmap) + preCanvas.setTransform(self.getScrsTransform()) + + if self.drawAxes: + preCanvas.setPen(Qc.Qt.gray) + preCanvas.drawLine(Qc.QLine(-9999, 0, 9999, 0)) + preCanvas.drawLine(Qc.QLine(0, -9999, 0, 9999)) + + if self.drawGrid: + if self.drawGridMode == GridMode.cartesian: + self.drawCartesianGrid(painter) + elif self.drawGridMode == GridMode.polar: + self.drawPolarGrid(painter) + + if self.currentGuides: + for guide in self.currentGuides: + guide.drawShape(preCanvas) + # preCanvas.end() + + def drawAddModePreview(self, painter): + if self.addMode is not None: + if self.addMode.active: + # Preview Object + if self.addMode.getPreview() is not None: + painter.setPen(self.currentPen.toQPen()) + painter.drawPath(self.addMode.getPreview()) + self.addMode.postDrawPreview(painter) + + + def drawTransformPreview(self, painter): + if self.currentBoundingBox is not None and self.currentlySelectedObj['selectedIndex'] is not None: + painter.save() + maj, minor = self.currentlySelectedObj['selectedIndex'] + selObj = self.drawObjects[maj][minor] + if not self.useGlobalCoords: + painter.save() + painter.setTransform( + selObj.transform.toQTransform(), True) + # painter.setTransform(selObj.baseTransform.toQTransform(), True) + painter.setPen(Qc.Qt.gray) + painter.drawLine(Qc.QLine(-9999, 0, 9999, 0)) + painter.drawLine(Qc.QLine(0, -9999, 0, 9999)) + painter.setPen(Qc.Qt.black) + painter.restore() + + painter.setTransform(selObj.getInteriorScrTransform( + self.newTransform).toQTransform(), True) + painter.drawRect(selObj.localBoundingBox) + else: + painter.setTransform(self.newTransform, True) + painter.drawRect(self.currentBoundingBox) + painter.restore() + + def postDraw(self): + self.postCanvasPixmap.fill(Qc.Qt.transparent) + with Qg.QPainter(self.postCanvasPixmap) as postCanvas: + postCanvas.setRenderHints(self.mainCanvas.renderHints()) + postCanvas.setTransform(self.getScrsTransform()) + + self.drawTransformPreview(postCanvas) + + if self.pendingSelectedObjList: + maj, minor = self.pendingSelectedObjList[self.pendingSelectedObjIndex] + postCanvas.drawRect(self.drawObjects[maj][minor].boundingBox) + + self.drawAddModePreview(postCanvas) + + if self.customAnchor is not None and self.anchorMode == AnchorMode.customAnchor: + self.drawAnchorCursor(postCanvas) + + # postCanvas.drawRect(self.getAllBoundingBox()) + + def drawAnchorCursor(self, painter): + painter.drawEllipse(self.customAnchor, 6, 6) + newCirclePath = Qg.QPainterPath() + newCirclePath.addEllipse(self.customAnchor, 2, 2) + + painter.fillPath(newCirclePath, Qg.QColor.fromRgb(0, 0, 0)) + + def updateModeBtnsOnly(self): + if self.currentModeStack[-1] == SelectionMode.translate: + activeBtn = self.ui.btnTranslate + elif self.currentModeStack[-1] == SelectionMode.rotate: + activeBtn = self.ui.btnRotate + elif self.currentModeStack[-1] == SelectionMode.scale: + activeBtn = self.ui.btnScale + elif self.currentModeStack[-1] == SelectionMode.pan: + activeBtn = self.ui.btnPan + elif self.currentModeStack[-1] == SelectionMode.setAnchor: + activeBtn = self.ui.btnAnchor + elif self.currentModeStack[-1] == SelectionMode.delete: + activeBtn = self.ui.btnDeleteMode + elif self.currentModeStack[-1] == SelectionMode.selectEdit: + activeBtn = self.ui.btnSelectEdit + else: + activeBtn = None + + + disableFill = isinstance(self.addMode, InplaceAddObj.AddBezierShape) and not self.currAddOptions['closedPath'] + self.ui.btnFill.setEnabled(not disableFill) + if disableFill and self.ui.btnFill.isEnabled(): + self.ui.btnFill.setChecked(not disableFill) + + + for button in self.modeButtons: + button.setChecked(button is activeBtn) + + def updateChecks(self): + self.removeAddMode() + self.updateModeBtnsOnly() + self.quickUpdate() + + def btnAlignXOnClick(self, checked): + self.lockY = checked + if self.lockX: + self.lockX = False + self.ui.btnAlignY.setChecked(False) + + def btnAlignYOnClick(self, checked): + self.lockX = checked + if self.lockY: + self.lockY = False + self.ui.btnAlignX.setChecked(False) + + def btnAnchorModeOnClick(self): + if self.currentModeStack[-1] != SelectionMode.setAnchor: + self.currentModeStack.append(SelectionMode.setAnchor) + self.updateChecks() + + def switchToAnchorMode(self): + if self.currentModeStack[-1] != SelectionMode.setAnchor: + self.currentModeStack.append(SelectionMode.setAnchor) + self.updateChecks() + + def btnTranslateonClick(self): + self.currentModeStack = [SelectionMode.translate] + self.ui.statusbar.showMessage('Translate mode') + self.clearSelection() + self.updateChecks() + + def btnRotateOnClick(self): + self.currentModeStack = [SelectionMode.rotate] + self.ui.statusbar.showMessage('Rotate mode') + self.clearSelection() + self.updateChecks() + + def btnScaleOnClick(self): + self.currentModeStack = [SelectionMode.scale] + self.ui.statusbar.showMessage('Scale mode') + self.clearSelection() + self.updateChecks() + + def btnPanOnClick(self): + self.currentModeStack = [SelectionMode.pan] + self.ui.statusbar.showMessage('Pan mode') + self.clearSelection() + self.updateChecks() + + def btnWorldCoordsOnClick(self, checked): + self.useGlobalCoords = checked + if not self.useGlobalCoords: + self.ui.comboAnchor.setCurrentIndex(AnchorMode.origin) + self.setAllInSetEnabled(self.globalTransformOnlyButtons, checked) + + def setAllInSetEnabled(self, widgetSet, enabled): + for widget in widgetSet: + widget.setEnabled(enabled) + + def btnDrawAxesOnClick(self, checked): + self.drawAxes = checked + self.quickUpdate() + + def btnDrawGridOnClick(self, checked): + self.drawGrid = checked + self.quickUpdate() + + def btnCustTransformOnClick(self): + matrixDialog = CustMatTransform.CustMatTransform() + matrixDialog.show() + result = matrixDialog.exec_() + if result == Qw.QDialog.Accepted: + objKey = self.currentlySelectedObj['selectedIndex'] + self.transformObject(objKey, + matrixDialog.getTransformationMatrix(), not + self.useGlobalCoords) + + # for now, unless we update the bouding box transformation. + self.clearSelection() + self.quickUpdate() + + def btnLoadEditorOnClick(self): + if self.fileChanged: + save = "Save current file?" + reply = Qw.QMessageBox.question(self, 'Message', save, Qw.QMessageBox.Yes, + Qw.QMessageBox.No) + if reply == Qw.QMessageBox.Yes: + self.actionSave() + + rawExternalEditor = self.settings['externalEditor'] + rawExtEditorArgs = self.settings['externalEditorArgs'] + execEditor = [rawExternalEditor] + + for arg in rawExtEditorArgs: + execEditor.append(string.Template( + arg).substitute(asypath=(self.filename))) + + subprocess.Popen(args=execEditor) + + def btnAddCodeOnClick(self): + header = """ +// xasy object created at $time +// Object Number: $uid +// This header is automatically generated by xasy. +// Your code here +""" + header = string.Template(header).substitute(time=str(datetime.datetime.now()), uid=str(self.globalObjectCounter)) + + with tempfile.TemporaryDirectory() as tmpdir: + newPath = os.path.join(tmpdir, 'tmpcode.asy') + f = io.open(newPath, 'w') + f.write(header) + f.close() + + subprocess.run(args=self.getExternalEditor(asypath=newPath)) + + f = io.open(newPath, 'r') + newItem = x2a.xasyScript(engine=self.asyEngine, canvas=self.xasyDrawObj) + newItem.setScript(f.read()) + f.close() + + # newItem.replaceKey(str(self.globalObjectCounter) + ':') + self.fileItems.append(newItem) + self.addObjCreationUrs(newItem) + self.asyfyCanvas() + + self.globalObjectCounter = self.globalObjectCounter + 1 + def softDeleteObj(self, objKey): + maj, minor = objKey + drawObj = self.drawObjects[maj][minor] + item = drawObj.originalObj + key = drawObj.key + keyIndex = drawObj.keyIndex + + + item.transfKeymap[key][keyIndex].deleted = True + # item.asyfied = False + + def getSelectedObjInfo(self, objIndex): + maj, minor = objIndex + drawObj = self.drawObjects[maj][minor] + item = drawObj.originalObj + key = drawObj.key + keyIndex = drawObj.keyIndex + + return item, key, keyIndex + + def transformObjKey(self, item, key, keyIndex, transform, applyFirst=False, drawObj=None): + if isinstance(transform, np.ndarray): + obj_transform = x2a.asyTransform.fromNumpyMatrix(transform) + elif isinstance(transform, Qg.QTransform): + assert transform.isAffine() + obj_transform = x2a.asyTransform.fromQTransform(transform) + else: + obj_transform = transform + + scr_transform = obj_transform + + if not applyFirst: + item.transfKeymap[key][keyIndex] = obj_transform * \ + item.transfKeymap[key][keyIndex] + if drawObj is not None: + drawObj.transform = scr_transform * drawObj.transform + else: + item.transfKeymap[key][keyIndex] = item.transfKeymap[key][keyIndex] * obj_transform + if drawObj is not None: + drawObj.transform = drawObj.transform * scr_transform + + if self.selectAsGroup: + for (maj2, min2) in self.currentlySelectedObj['allSameKey']: + if (maj2, min2) == (maj, minor): + continue + obj = self.drawObjects[maj2][min2] + newIndex = obj.keyIndex + if not applyFirst: + item.transfKeymap[key][newIndex] = obj_transform * \ + item.transfKeymap[key][newIndex] + obj.transform = scr_transform * obj.transform + else: + item.transfKeymap[key][newIndex] = item.transfKeymap[key][newIndex] * obj_transform + obj.transform = obj.transform * scr_transform + + self.fileChanged = True + self.quickUpdate() + + def transformObject(self, objKey, transform, applyFirst=False): + maj, minor = objKey + drawObj = self.drawObjects[maj][minor] + item, key, keyIndex = self.getSelectedObjInfo(objKey) + self.transformObjKey(item, key, keyIndex, transform, applyFirst, drawObj) + + def initializeEmptyFile(self): + pass + + def getExternalEditor(self, **kwargs) -> str: + rawExternalEditor = self.settings['externalEditor'] + rawExtEditorArgs = self.settings['externalEditorArgs'] + execEditor = [rawExternalEditor] + + for arg in rawExtEditorArgs: + execEditor.append(string.Template(arg).substitute(**kwargs)) + + return execEditor + + + def loadFile(self, name): + filename = os.path.abspath(name) + if not os.path.isfile(filename): + filename = filename + '.asy' + + if not os.path.isfile(filename): + self.ui.statusbar.showMessage('File {0} not found'.format(filename)) + return + + self.ui.statusbar.showMessage('Load {0}'.format(filename)) + self.filename = filename + self.currDir = os.path.dirname(self.filename) + + self.erase() + + f = open(self.filename, 'rt') + try: + rawFileStr = f.read() + except IOError: + Qw.QMessageBox.critical(self, self.strings.fileOpenFailed, self.strings.fileOpenFailedText) + else: + rawText, transfDict, maxKey = xf.extractTransformsFromFile(rawFileStr) + item = x2a.xasyScript(canvas=self.xasyDrawObj, engine=self.asyEngine, transfKeyMap=transfDict) + + item.setScript(rawText) + self.fileItems.append(item) + self.asyfyCanvas(True) + + maxKey2 = item.getMaxKeyCounter() + self.asy2psmap = item.asy2psmap + self.globalObjectCounter = max(maxKey + 1, maxKey2) + finally: + f.close() + + def populateCanvasWithItems(self, forceUpdate=False): + self.itemCount = 0 + for item in self.fileItems: + self.drawObjects.append(item.generateDrawObjects(forceUpdate)) diff --git a/Master/texmf-dist/asymptote/GUI/__init__.py b/Master/texmf-dist/asymptote/GUI/__init__.py new file mode 100755 index 00000000000..5f7ce86af66 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python3
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/configs/xasyconfig.cson b/Master/texmf-dist/asymptote/GUI/configs/xasyconfig.cson new file mode 100644 index 00000000000..01aaf66c74b --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/configs/xasyconfig.cson @@ -0,0 +1,57 @@ +# Default Options for xasy + +# External editor. $asypath will be replaced by the current file. +externalEditor: "emacs" +externalEditorArgs: ["$asypath"] + +# Path to Asymptote executable +asyPath: "asy" + +# Overwrites the ASYMPTOTE_DIR Environment variable if set. Otherwise, leaves asymptote to decide. +asyBaseLocation: null + +# Show Debugging Information +showDebug: false + +# Default Pen Options +defaultPenOptions: "" + +# Default Pen Color +defaultPenColor: "#000000" + +# +defaultPenWidth: 0.5 +useLegacyDrawMode: false +enableImmediatePreview: true +useDegrees: false +groupObjDefault: false + +# +terminalFont: "Courier" +terminalFontSize: 9 + +# +defaultShowAxes: true +defaultShowGrid: false +defaultGridSnap: false + +# Draw Selected Objects on top of the frame +drawSelectedOnTop: true + +# Grid Settings + +gridMajorAxesColor: "#858585" +gridMinorAxesColor: "#dddddd" +gridMajorAxesSpacing: 5 +gridMinorAxesCount: 9 + +# Magnification Settings +minimumMagnification: 0.01 +maximumMagnification: 100 + +# Debug Mode +debugMode: true + +# Settings Override for Windows +windows: + externalEditor: "notepad.exe" diff --git a/Master/texmf-dist/asymptote/GUI/configs/xasykeymap.cson b/Master/texmf-dist/asymptote/GUI/configs/xasykeymap.cson new file mode 100644 index 00000000000..7ab1b34a2fd --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/configs/xasykeymap.cson @@ -0,0 +1,24 @@ +# Default Keymaps for xasy + +commandPalette: "Ctrl+P" +quit: "Ctrl+Q" +deleteObject: "Del" + +finalizeCurve: "Space" +finalizeCurveClosed: "c" + +anchorMode: "Ctrl+A" + +undo: 'Ctrl+Z' +redo: 'Ctrl+Y' + +moveUp: 'Up' +moveDown: 'Down' + +scrollUp: 'Shift+Up' +scrollDown: 'Shift+Down' +scrollLeft: 'Shift+Left' +scrollRight: 'Shift+Right' + +zoomIn: 'Ctrl+Up' +zoomOut: 'Ctrl+Down'
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/icons_rc.py b/Master/texmf-dist/asymptote/GUI/icons_rc.py new file mode 100755 index 00000000000..c2c877a6bb0 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/icons_rc.py @@ -0,0 +1,2680 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created by: The Resource Compiler for PyQt5 (Qt v5.10.1) +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore + +qt_resource_data = b"\ +\x00\x00\x04\x10\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x31\x38\x2e\x37\x35\x70\x74\x27\x20\x76\x65\x72\x73\ +\x69\x6f\x6e\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\ +\x78\x3d\x27\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\ +\x38\x33\x20\x31\x38\x2e\x37\x35\x20\x31\x38\x2e\x37\x35\x27\x20\ +\x77\x69\x64\x74\x68\x3d\x27\x31\x38\x2e\x37\x35\x70\x74\x27\x20\ +\x78\x6d\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ +\x67\x27\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x27\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x27\x3e\x0a\x3c\ +\x67\x20\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\x3e\x0a\x3c\x67\ +\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\ +\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\ +\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x35\x2e\x37\x38\x34\x34\ +\x20\x36\x33\x2e\x32\x33\x33\x33\x29\x27\x3e\x0a\x3c\x70\x61\x74\ +\x68\x20\x64\x3d\x27\x4d\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x20\ +\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x38\x2e\x36\x35\x37\x33\x34\ +\x20\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x38\x2e\x36\x35\x37\x33\ +\x34\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x2d\x38\x2e\x36\ +\x35\x37\x33\x34\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x2d\ +\x38\x2e\x36\x35\x37\x33\x34\x20\x38\x2e\x36\x35\x37\x33\x34\x5a\ +\x27\x20\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x20\x73\ +\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3d\x27\x72\ +\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\ +\x65\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3d\ +\x27\x31\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\x72\x6f\x6b\x65\ +\x2d\x77\x69\x64\x74\x68\x3d\x27\x31\x2e\x35\x30\x35\x36\x32\x27\ +\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\ +\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\ +\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\ +\x36\x34\x20\x36\x35\x2e\x37\x38\x34\x34\x20\x36\x33\x2e\x32\x33\ +\x33\x33\x29\x27\x3e\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\ +\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x20\x2d\x30\x4c\x20\x38\x2e\ +\x36\x35\x37\x33\x34\x20\x30\x27\x20\x66\x69\x6c\x6c\x3d\x27\x6e\ +\x6f\x6e\x65\x27\x20\x73\x74\x72\x6f\x6b\x65\x3d\x27\x23\x30\x30\ +\x30\x30\x30\x30\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\ +\x65\x63\x61\x70\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\ +\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\ +\x72\x6c\x69\x6d\x69\x74\x3d\x27\x31\x30\x2e\x30\x33\x37\x35\x27\ +\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3d\x27\x31\ +\x2e\x35\x30\x35\x36\x32\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\ +\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\ +\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\ +\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x35\x2e\x37\x38\x34\ +\x34\x20\x36\x33\x2e\x32\x33\x33\x33\x29\x27\x3e\x0a\x3c\x70\x61\ +\x74\x68\x20\x64\x3d\x27\x4d\x20\x30\x20\x38\x2e\x36\x35\x37\x33\ +\x34\x4c\x20\x30\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x27\x20\x66\ +\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\x27\x20\x73\x74\x72\x6f\x6b\ +\x65\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x20\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3d\x27\x72\x6f\x75\x6e\ +\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\ +\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\ +\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3d\x27\x31\x30\ +\x2e\x30\x33\x37\x35\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\ +\x64\x74\x68\x3d\x27\x31\x2e\x35\x30\x35\x36\x32\x27\x2f\x3e\x0a\ +\x3c\x2f\x67\x3e\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x02\xc9\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\ +\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x6e\x65\x77\x20\x30\x20\x30\x20\ +\x35\x31\x32\x20\x35\x31\x32\x3b\x22\x20\x78\x6d\x6c\x3a\x73\x70\ +\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\ +\x0a\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\x6f\x69\x6e\x74\x73\ +\x3d\x22\x34\x38\x30\x2c\x32\x35\x36\x20\x33\x38\x34\x2c\x31\x36\ +\x30\x20\x33\x38\x34\x2c\x32\x33\x36\x20\x32\x37\x36\x2c\x32\x33\ +\x36\x20\x32\x37\x36\x2c\x31\x32\x38\x20\x33\x35\x32\x2c\x31\x32\ +\x38\x20\x32\x35\x36\x2c\x33\x32\x20\x31\x36\x30\x2c\x31\x32\x38\ +\x20\x32\x33\x36\x2c\x31\x32\x38\x20\x32\x33\x36\x2c\x32\x33\x36\ +\x20\x31\x32\x38\x2c\x32\x33\x36\x20\x31\x32\x38\x2c\x31\x36\x30\ +\x20\x33\x32\x2c\x32\x35\x36\x20\x31\x32\x38\x2c\x33\x35\x32\x20\ +\x0d\x0a\x09\x31\x32\x38\x2c\x32\x37\x36\x20\x32\x33\x36\x2c\x32\ +\x37\x36\x20\x32\x33\x36\x2c\x33\x38\x34\x20\x31\x36\x30\x2c\x33\ +\x38\x34\x20\x32\x35\x36\x2c\x34\x38\x30\x20\x33\x35\x32\x2c\x33\ +\x38\x34\x20\x32\x37\x35\x2e\x38\x2c\x33\x38\x34\x20\x32\x37\x35\ +\x2e\x34\x2c\x32\x37\x35\x2e\x35\x20\x33\x38\x34\x2c\x32\x37\x35\ +\x2e\x38\x20\x33\x38\x34\x2c\x33\x35\x32\x20\x22\x2f\x3e\x0d\x0a\ +\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\x93\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x30\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4d\x61\x67\x6e\x69\x66\x79\x69\x6e\x67\x5f\x67\x6c\x61\ +\x73\x73\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\ +\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\ +\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\ +\x22\x20\x78\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x79\x3d\x22\ +\x30\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\x20\ +\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\x6e\x61\x62\x6c\x65\x2d\ +\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3d\x22\x6e\x65\x77\x20\ +\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x78\x6d\x6c\x3a\x73\ +\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x3e\ +\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x37\x2e\x35\ +\x34\x35\x2c\x31\x35\x2e\x34\x36\x37\x6c\x2d\x33\x2e\x37\x37\x39\ +\x2d\x33\x2e\x37\x37\x39\x63\x30\x2e\x35\x37\x2d\x30\x2e\x39\x33\ +\x35\x2c\x30\x2e\x38\x39\x38\x2d\x32\x2e\x30\x33\x35\x2c\x30\x2e\ +\x38\x39\x38\x2d\x33\x2e\x32\x31\x63\x30\x2d\x33\x2e\x34\x31\x37\ +\x2d\x32\x2e\x39\x36\x31\x2d\x36\x2e\x33\x37\x37\x2d\x36\x2e\x33\ +\x37\x38\x2d\x36\x2e\x33\x37\x37\x0d\x0a\x09\x43\x34\x2e\x38\x36\ +\x39\x2c\x32\x2e\x31\x2c\x32\x2e\x31\x2c\x34\x2e\x38\x37\x2c\x32\ +\x2e\x31\x2c\x38\x2e\x32\x38\x37\x63\x30\x2c\x33\x2e\x34\x31\x36\ +\x2c\x32\x2e\x39\x36\x31\x2c\x36\x2e\x33\x37\x37\x2c\x36\x2e\x33\ +\x37\x37\x2c\x36\x2e\x33\x37\x37\x63\x31\x2e\x31\x33\x37\x2c\x30\ +\x2c\x32\x2e\x32\x2d\x30\x2e\x33\x30\x39\x2c\x33\x2e\x31\x31\x35\ +\x2d\x30\x2e\x38\x34\x34\x6c\x33\x2e\x37\x39\x39\x2c\x33\x2e\x38\ +\x30\x31\x0d\x0a\x09\x63\x30\x2e\x33\x37\x32\x2c\x30\x2e\x33\x37\ +\x31\x2c\x30\x2e\x39\x37\x35\x2c\x30\x2e\x33\x37\x31\x2c\x31\x2e\ +\x33\x34\x36\x2c\x30\x6c\x30\x2e\x39\x34\x33\x2d\x30\x2e\x39\x34\ +\x33\x43\x31\x38\x2e\x30\x35\x31\x2c\x31\x36\x2e\x33\x30\x37\x2c\ +\x31\x37\x2e\x39\x31\x36\x2c\x31\x35\x2e\x38\x33\x38\x2c\x31\x37\ +\x2e\x35\x34\x35\x2c\x31\x35\x2e\x34\x36\x37\x7a\x20\x4d\x34\x2e\ +\x30\x30\x34\x2c\x38\x2e\x32\x38\x37\x0d\x0a\x09\x63\x30\x2d\x32\ +\x2e\x33\x36\x36\x2c\x31\x2e\x39\x31\x37\x2d\x34\x2e\x32\x38\x33\ +\x2c\x34\x2e\x32\x38\x32\x2d\x34\x2e\x32\x38\x33\x63\x32\x2e\x33\ +\x36\x36\x2c\x30\x2c\x34\x2e\x34\x37\x34\x2c\x32\x2e\x31\x30\x37\ +\x2c\x34\x2e\x34\x37\x34\x2c\x34\x2e\x34\x37\x34\x63\x30\x2c\x32\ +\x2e\x33\x36\x35\x2d\x31\x2e\x39\x31\x38\x2c\x34\x2e\x32\x38\x33\ +\x2d\x34\x2e\x32\x38\x33\x2c\x34\x2e\x32\x38\x33\x0d\x0a\x09\x43\ +\x36\x2e\x31\x31\x31\x2c\x31\x32\x2e\x37\x36\x2c\x34\x2e\x30\x30\ +\x34\x2c\x31\x30\x2e\x36\x35\x32\x2c\x34\x2e\x30\x30\x34\x2c\x38\ +\x2e\x32\x38\x37\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\ +\x0d\x0a\ +\x00\x00\x03\x46\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\ +\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x6e\x65\x77\x20\x30\x20\x30\x20\ +\x35\x31\x32\x20\x35\x31\x32\x3b\x22\x20\x78\x6d\x6c\x3a\x73\x70\ +\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\ +\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x34\x33\x37\x2e\x35\ +\x2c\x33\x38\x36\x2e\x36\x4c\x33\x30\x36\x2e\x39\x2c\x32\x35\x36\ +\x6c\x31\x33\x30\x2e\x36\x2d\x31\x33\x30\x2e\x36\x63\x31\x34\x2e\ +\x31\x2d\x31\x34\x2e\x31\x2c\x31\x34\x2e\x31\x2d\x33\x36\x2e\x38\ +\x2c\x30\x2d\x35\x30\x2e\x39\x63\x2d\x31\x34\x2e\x31\x2d\x31\x34\ +\x2e\x31\x2d\x33\x36\x2e\x38\x2d\x31\x34\x2e\x31\x2d\x35\x30\x2e\ +\x39\x2c\x30\x4c\x32\x35\x36\x2c\x32\x30\x35\x2e\x31\x4c\x31\x32\ +\x35\x2e\x34\x2c\x37\x34\x2e\x35\x0d\x0a\x09\x63\x2d\x31\x34\x2e\ +\x31\x2d\x31\x34\x2e\x31\x2d\x33\x36\x2e\x38\x2d\x31\x34\x2e\x31\ +\x2d\x35\x30\x2e\x39\x2c\x30\x63\x2d\x31\x34\x2e\x31\x2c\x31\x34\ +\x2e\x31\x2d\x31\x34\x2e\x31\x2c\x33\x36\x2e\x38\x2c\x30\x2c\x35\ +\x30\x2e\x39\x4c\x32\x30\x35\x2e\x31\x2c\x32\x35\x36\x4c\x37\x34\ +\x2e\x35\x2c\x33\x38\x36\x2e\x36\x63\x2d\x31\x34\x2e\x31\x2c\x31\ +\x34\x2e\x31\x2d\x31\x34\x2e\x31\x2c\x33\x36\x2e\x38\x2c\x30\x2c\ +\x35\x30\x2e\x39\x0d\x0a\x09\x63\x31\x34\x2e\x31\x2c\x31\x34\x2e\ +\x31\x2c\x33\x36\x2e\x38\x2c\x31\x34\x2e\x31\x2c\x35\x30\x2e\x39\ +\x2c\x30\x4c\x32\x35\x36\x2c\x33\x30\x36\x2e\x39\x6c\x31\x33\x30\ +\x2e\x36\x2c\x31\x33\x30\x2e\x36\x63\x31\x34\x2e\x31\x2c\x31\x34\ +\x2e\x31\x2c\x33\x36\x2e\x38\x2c\x31\x34\x2e\x31\x2c\x35\x30\x2e\ +\x39\x2c\x30\x43\x34\x35\x31\x2e\x35\x2c\x34\x32\x33\x2e\x34\x2c\ +\x34\x35\x31\x2e\x35\x2c\x34\x30\x30\x2e\x36\x2c\x34\x33\x37\x2e\ +\x35\x2c\x33\x38\x36\x2e\x36\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\x73\ +\x76\x67\x3e\x0d\x0a\ +\x00\x00\x00\xdd\ +\x3c\ +\x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x38\x22\ +\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x38\x22\x20\x76\x69\x65\x77\ +\x42\x6f\x78\x3d\x22\x30\x20\x30\x20\x38\x20\x38\x22\x3e\x0a\x20\ +\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x30\x20\x30\x76\x32\ +\x68\x2e\x35\x63\x30\x2d\x2e\x35\x35\x2e\x34\x35\x2d\x31\x20\x31\ +\x2d\x31\x68\x31\x2e\x35\x76\x35\x2e\x35\x63\x30\x20\x2e\x32\x38\ +\x2d\x2e\x32\x32\x2e\x35\x2d\x2e\x35\x2e\x35\x68\x2d\x2e\x35\x76\ +\x31\x68\x34\x76\x2d\x31\x68\x2d\x2e\x35\x63\x2d\x2e\x32\x38\x20\ +\x30\x2d\x2e\x35\x2d\x2e\x32\x32\x2d\x2e\x35\x2d\x2e\x35\x76\x2d\ +\x35\x2e\x35\x68\x31\x2e\x35\x63\x2e\x35\x35\x20\x30\x20\x31\x20\ +\x2e\x34\x35\x20\x31\x20\x31\x68\x2e\x35\x76\x2d\x32\x68\x2d\x38\ +\x7a\x22\x20\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x02\x7f\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\ +\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x6e\x65\x77\x20\x30\x20\x30\x20\ +\x35\x31\x32\x20\x35\x31\x32\x3b\x22\x20\x78\x6d\x6c\x3a\x73\x70\ +\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\ +\x0a\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\x6f\x69\x6e\x74\x73\ +\x3d\x22\x32\x38\x38\x2c\x39\x36\x20\x33\x33\x37\x2e\x39\x2c\x31\ +\x34\x35\x2e\x39\x20\x32\x37\x34\x2c\x32\x30\x39\x2e\x37\x20\x32\ +\x37\x34\x2c\x32\x30\x39\x2e\x37\x20\x31\x34\x35\x2e\x39\x2c\x33\ +\x33\x37\x2e\x39\x20\x39\x36\x2c\x32\x38\x38\x20\x39\x36\x2c\x34\ +\x31\x36\x20\x32\x32\x34\x2c\x34\x31\x36\x20\x31\x37\x34\x2e\x31\ +\x2c\x33\x36\x36\x2e\x31\x20\x33\x35\x37\x2e\x34\x2c\x31\x38\x32\ +\x2e\x39\x20\x33\x36\x36\x2e\x31\x2c\x31\x37\x34\x2e\x31\x20\x0d\ +\x0a\x09\x34\x31\x36\x2c\x32\x32\x34\x20\x34\x31\x36\x2c\x39\x36\ +\x20\x22\x2f\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x04\x22\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x32\x34\x70\x74\x27\x20\x76\x65\x72\x73\x69\x6f\x6e\ +\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x27\ +\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\x38\x33\x20\ +\x32\x34\x20\x32\x34\x27\x20\x77\x69\x64\x74\x68\x3d\x27\x32\x34\ +\x70\x74\x27\x20\x78\x6d\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x27\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\ +\x6e\x6b\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\ +\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\ +\x27\x3e\x0a\x3c\x67\x20\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\ +\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\ +\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\ +\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\ +\x37\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\x20\x2d\x39\x2e\x33\x39\ +\x31\x31\x38\x20\x2d\x30\x4c\x20\x2d\x36\x2e\x36\x34\x30\x35\x37\ +\x20\x2d\x31\x32\x2e\x30\x34\x35\x4c\x20\x38\x2e\x36\x37\x36\x33\ +\x32\x20\x2d\x36\x2e\x35\x31\x38\x37\x31\x4c\x20\x33\x2e\x33\x32\ +\x30\x32\x38\x20\x36\x2e\x30\x32\x32\x35\x27\x20\x66\x69\x6c\x6c\ +\x3d\x27\x6e\x6f\x6e\x65\x27\x20\x73\x74\x72\x6f\x6b\x65\x3d\x27\ +\x23\x30\x30\x30\x30\x30\x30\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6c\x69\x6e\x65\x63\x61\x70\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\ +\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3d\ +\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6d\ +\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3d\x27\x31\x30\x2e\x30\x33\ +\x37\x35\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\ +\x3d\x27\x31\x2e\x37\x35\x36\x35\x36\x27\x2f\x3e\x0a\x3c\x2f\x67\ +\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\ +\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\ +\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\ +\x37\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\ +\x3c\x63\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x2d\x39\x2e\x33\ +\x39\x31\x31\x38\x27\x20\x63\x79\x3d\x27\x2d\x30\x27\x20\x72\x3d\ +\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\ +\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\ +\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\ +\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\ +\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\ +\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\x3c\x63\ +\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x2d\x36\x2e\x36\x34\x30\ +\x35\x37\x27\x20\x63\x79\x3d\x27\x2d\x31\x32\x2e\x30\x34\x35\x27\ +\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\ +\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\ +\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\ +\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\ +\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\ +\x2e\x37\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\ +\x0a\x3c\x63\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x38\x2e\x36\ +\x37\x36\x33\x32\x27\x20\x63\x79\x3d\x27\x2d\x36\x2e\x35\x31\x38\ +\x37\x31\x27\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\ +\x66\x69\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\ +\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\ +\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\ +\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\ +\x20\x36\x38\x2e\x37\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\ +\x29\x27\x3e\x0a\x3c\x63\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\ +\x33\x2e\x33\x32\x30\x32\x38\x27\x20\x63\x79\x3d\x27\x36\x2e\x30\ +\x32\x32\x35\x27\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\ +\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\ +\x3e\x0a\x3c\x2f\x67\x3e\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\ +\x3e\ +\x00\x00\x03\xb5\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x43\x68\x65\x76\x72\x6f\x6e\x5f\x63\x69\x72\x63\x6c\x65\ +\x64\x5f\x72\x69\x67\x68\x74\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ +\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ +\x78\x6c\x69\x6e\x6b\x22\x0d\x0a\x09\x20\x78\x3d\x22\x30\x70\x78\ +\x22\x20\x79\x3d\x22\x30\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\ +\x78\x3d\x22\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\x6e\ +\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3d\ +\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\ +\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\ +\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\ +\x4d\x31\x31\x2c\x31\x30\x4c\x38\x2e\x36\x39\x38\x2c\x37\x2e\x34\ +\x39\x34\x63\x2d\x30\x2e\x31\x39\x36\x2d\x30\x2e\x31\x39\x38\x2d\ +\x30\x2e\x31\x39\x36\x2d\x30\x2e\x35\x31\x39\x2c\x30\x2d\x30\x2e\ +\x37\x31\x38\x63\x30\x2e\x31\x39\x36\x2d\x30\x2e\x31\x39\x37\x2c\ +\x30\x2e\x35\x31\x35\x2d\x30\x2e\x31\x39\x37\x2c\x30\x2e\x37\x31\ +\x2c\x30\x6c\x32\x2e\x38\x30\x37\x2c\x32\x2e\x38\x36\x34\x0d\x0a\ +\x09\x63\x30\x2e\x31\x39\x36\x2c\x30\x2e\x31\x39\x39\x2c\x30\x2e\ +\x31\x39\x36\x2c\x30\x2e\x35\x32\x2c\x30\x2c\x30\x2e\x37\x31\x37\ +\x6c\x2d\x32\x2e\x38\x30\x37\x2c\x32\x2e\x38\x36\x34\x63\x2d\x30\ +\x2e\x31\x39\x35\x2c\x30\x2e\x31\x39\x39\x2d\x30\x2e\x35\x31\x34\ +\x2c\x30\x2e\x31\x39\x38\x2d\x30\x2e\x37\x31\x2c\x30\x63\x2d\x30\ +\x2e\x31\x39\x36\x2d\x30\x2e\x31\x39\x37\x2d\x30\x2e\x31\x39\x36\ +\x2d\x30\x2e\x35\x31\x38\x2c\x30\x2d\x30\x2e\x37\x31\x37\x4c\x31\ +\x31\x2c\x31\x30\x7a\x20\x4d\x31\x30\x2c\x30\x2e\x34\x0d\x0a\x09\ +\x63\x35\x2e\x33\x30\x32\x2c\x30\x2c\x39\x2e\x36\x2c\x34\x2e\x32\ +\x39\x38\x2c\x39\x2e\x36\x2c\x39\x2e\x36\x63\x30\x2c\x35\x2e\x33\ +\x30\x33\x2d\x34\x2e\x32\x39\x38\x2c\x39\x2e\x36\x2d\x39\x2e\x36\ +\x2c\x39\x2e\x36\x53\x30\x2e\x34\x2c\x31\x35\x2e\x33\x30\x33\x2c\ +\x30\x2e\x34\x2c\x31\x30\x43\x30\x2e\x34\x2c\x34\x2e\x36\x39\x38\ +\x2c\x34\x2e\x36\x39\x38\x2c\x30\x2e\x34\x2c\x31\x30\x2c\x30\x2e\ +\x34\x7a\x20\x4d\x31\x30\x2c\x31\x38\x2e\x33\x35\x34\x0d\x0a\x09\ +\x63\x34\x2e\x36\x31\x33\x2c\x30\x2c\x38\x2e\x33\x35\x34\x2d\x33\ +\x2e\x37\x34\x2c\x38\x2e\x33\x35\x34\x2d\x38\x2e\x33\x35\x34\x63\ +\x30\x2d\x34\x2e\x36\x31\x34\x2d\x33\x2e\x37\x34\x31\x2d\x38\x2e\ +\x33\x35\x34\x2d\x38\x2e\x33\x35\x34\x2d\x38\x2e\x33\x35\x34\x63\ +\x2d\x34\x2e\x36\x31\x35\x2c\x30\x2d\x38\x2e\x33\x35\x34\x2c\x33\ +\x2e\x37\x34\x2d\x38\x2e\x33\x35\x34\x2c\x38\x2e\x33\x35\x34\x0d\ +\x0a\x09\x43\x31\x2e\x36\x34\x35\x2c\x31\x34\x2e\x36\x31\x34\x2c\ +\x35\x2e\x33\x38\x35\x2c\x31\x38\x2e\x33\x35\x34\x2c\x31\x30\x2c\ +\x31\x38\x2e\x33\x35\x34\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\x73\x76\ +\x67\x3e\x0d\x0a\ +\x00\x00\x02\x79\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x31\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\x3e\ +\x0d\x0a\x09\x09\x3c\x67\x3e\x0d\x0a\x09\x09\x09\x3c\x70\x6f\x6c\ +\x79\x67\x6f\x6e\x20\x70\x6f\x69\x6e\x74\x73\x3d\x22\x31\x38\x36\ +\x2e\x33\x30\x31\x2c\x33\x33\x39\x2e\x38\x39\x33\x20\x39\x36\x2c\ +\x32\x34\x39\x2e\x34\x36\x31\x20\x36\x34\x2c\x32\x37\x39\x2e\x39\ +\x36\x38\x20\x31\x38\x36\x2e\x33\x30\x31\x2c\x34\x30\x32\x20\x34\ +\x34\x38\x2c\x31\x34\x30\x2e\x35\x30\x36\x20\x34\x31\x36\x2c\x31\ +\x31\x30\x20\x09\x09\x09\x22\x2f\x3e\x0d\x0a\x09\x09\x3c\x2f\x67\ +\x3e\x0d\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\ +\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x04\x79\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x30\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x43\x6f\x64\x65\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\ +\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\ +\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\ +\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\ +\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\x20\x79\x3d\ +\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\x6e\x61\ +\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3d\x22\ +\x6e\x65\x77\x20\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x78\ +\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\ +\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\ +\x35\x2e\x37\x31\x39\x2c\x31\x34\x2e\x37\x35\x63\x2d\x30\x2e\x32\ +\x33\x36\x2c\x30\x2d\x30\x2e\x34\x37\x34\x2d\x30\x2e\x30\x38\x33\ +\x2d\x30\x2e\x36\x36\x34\x2d\x30\x2e\x32\x35\x32\x4c\x2d\x30\x2e\ +\x30\x30\x35\x2c\x31\x30\x6c\x35\x2e\x33\x34\x31\x2d\x34\x2e\x37\ +\x34\x38\x43\x35\x2e\x37\x34\x38\x2c\x34\x2e\x38\x38\x37\x2c\x36\ +\x2e\x33\x38\x2c\x34\x2e\x39\x32\x32\x2c\x36\x2e\x37\x34\x37\x2c\ +\x35\x2e\x33\x33\x35\x0d\x0a\x09\x63\x30\x2e\x33\x36\x37\x2c\x30\ +\x2e\x34\x31\x33\x2c\x30\x2e\x33\x33\x2c\x31\x2e\x30\x34\x35\x2d\ +\x30\x2e\x30\x38\x33\x2c\x31\x2e\x34\x31\x32\x4c\x33\x2e\x30\x30\ +\x35\x2c\x31\x30\x6c\x33\x2e\x33\x37\x38\x2c\x33\x2e\x30\x30\x32\ +\x63\x30\x2e\x34\x31\x33\x2c\x30\x2e\x33\x36\x37\x2c\x30\x2e\x34\ +\x35\x2c\x30\x2e\x39\x39\x39\x2c\x30\x2e\x30\x38\x33\x2c\x31\x2e\ +\x34\x31\x32\x0d\x0a\x09\x43\x36\x2e\x32\x36\x39\x2c\x31\x34\x2e\ +\x36\x33\x37\x2c\x35\x2e\x39\x39\x34\x2c\x31\x34\x2e\x37\x35\x2c\ +\x35\x2e\x37\x31\x39\x2c\x31\x34\x2e\x37\x35\x7a\x20\x4d\x31\x34\ +\x2e\x36\x36\x34\x2c\x31\x34\x2e\x37\x34\x38\x4c\x32\x30\x2e\x30\ +\x30\x35\x2c\x31\x30\x6c\x2d\x35\x2e\x30\x36\x2d\x34\x2e\x34\x39\ +\x38\x63\x2d\x30\x2e\x34\x31\x33\x2d\x30\x2e\x33\x36\x37\x2d\x31\ +\x2e\x30\x34\x35\x2d\x30\x2e\x33\x33\x2d\x31\x2e\x34\x31\x31\x2c\ +\x30\x2e\x30\x38\x33\x0d\x0a\x09\x63\x2d\x30\x2e\x33\x36\x37\x2c\ +\x30\x2e\x34\x31\x33\x2d\x30\x2e\x33\x33\x2c\x31\x2e\x30\x34\x35\ +\x2c\x30\x2e\x30\x38\x33\x2c\x31\x2e\x34\x31\x32\x4c\x31\x36\x2e\ +\x39\x39\x35\x2c\x31\x30\x6c\x2d\x33\x2e\x36\x35\x39\x2c\x33\x2e\ +\x32\x35\x32\x63\x2d\x30\x2e\x34\x31\x33\x2c\x30\x2e\x33\x36\x37\ +\x2d\x30\x2e\x34\x35\x2c\x30\x2e\x39\x39\x39\x2d\x30\x2e\x30\x38\ +\x33\x2c\x31\x2e\x34\x31\x32\x43\x31\x33\x2e\x34\x35\x2c\x31\x34\ +\x2e\x38\x38\x37\x2c\x31\x33\x2e\x37\x32\x35\x2c\x31\x35\x2c\x31\ +\x34\x2c\x31\x35\x0d\x0a\x09\x43\x31\x34\x2e\x32\x33\x36\x2c\x31\ +\x35\x2c\x31\x34\x2e\x34\x37\x34\x2c\x31\x34\x2e\x39\x31\x37\x2c\ +\x31\x34\x2e\x36\x36\x34\x2c\x31\x34\x2e\x37\x34\x38\x7a\x20\x4d\ +\x39\x2e\x39\x38\x36\x2c\x31\x36\x2e\x31\x36\x35\x6c\x32\x2d\x31\ +\x32\x63\x30\x2e\x30\x39\x31\x2d\x30\x2e\x35\x34\x35\x2d\x30\x2e\ +\x32\x37\x37\x2d\x31\x2e\x30\x36\x2d\x30\x2e\x38\x32\x32\x2d\x31\ +\x2e\x31\x35\x31\x0d\x0a\x09\x63\x2d\x30\x2e\x35\x34\x37\x2d\x30\ +\x2e\x30\x39\x32\x2d\x31\x2e\x30\x36\x31\x2c\x30\x2e\x32\x37\x37\ +\x2d\x31\x2e\x31\x35\x2c\x30\x2e\x38\x32\x32\x6c\x2d\x32\x2c\x31\ +\x32\x63\x2d\x30\x2e\x30\x39\x31\x2c\x30\x2e\x35\x34\x35\x2c\x30\ +\x2e\x32\x37\x37\x2c\x31\x2e\x30\x36\x2c\x30\x2e\x38\x32\x32\x2c\ +\x31\x2e\x31\x35\x31\x43\x38\x2e\x38\x39\x32\x2c\x31\x36\x2e\x39\ +\x39\x36\x2c\x38\x2e\x39\x34\x36\x2c\x31\x37\x2c\x39\x2e\x30\x30\ +\x31\x2c\x31\x37\x0d\x0a\x09\x43\x39\x2e\x34\x38\x31\x2c\x31\x37\ +\x2c\x39\x2e\x39\x30\x35\x2c\x31\x36\x2e\x36\x35\x33\x2c\x39\x2e\ +\x39\x38\x36\x2c\x31\x36\x2e\x31\x36\x35\x7a\x22\x2f\x3e\x0d\x0a\ +\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x04\x30\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x32\x34\x70\x74\x27\x20\x76\x65\x72\x73\x69\x6f\x6e\ +\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x27\ +\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\x38\x33\x20\ +\x32\x34\x20\x32\x34\x27\x20\x77\x69\x64\x74\x68\x3d\x27\x32\x34\ +\x70\x74\x27\x20\x78\x6d\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x27\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\ +\x6e\x6b\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\ +\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\ +\x27\x3e\x0a\x3c\x67\x20\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\ +\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\ +\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\ +\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\ +\x37\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\x20\x2d\x39\x2e\x33\x39\ +\x31\x31\x38\x20\x2d\x30\x4c\x20\x2d\x36\x2e\x36\x34\x30\x35\x37\ +\x20\x2d\x31\x32\x2e\x30\x34\x35\x4c\x20\x38\x2e\x36\x37\x36\x33\ +\x32\x20\x2d\x36\x2e\x35\x31\x38\x37\x31\x4c\x20\x33\x2e\x33\x32\ +\x30\x32\x38\x20\x36\x2e\x30\x32\x32\x35\x4c\x20\x2d\x39\x2e\x33\ +\x39\x31\x31\x38\x20\x2d\x30\x5a\x27\x20\x66\x69\x6c\x6c\x3d\x27\ +\x6e\x6f\x6e\x65\x27\x20\x73\x74\x72\x6f\x6b\x65\x3d\x27\x23\x30\ +\x30\x30\x30\x30\x30\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\ +\x6e\x65\x63\x61\x70\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3d\x27\x72\ +\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\ +\x65\x72\x6c\x69\x6d\x69\x74\x3d\x27\x31\x30\x2e\x30\x33\x37\x35\ +\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3d\x27\ +\x31\x2e\x37\x35\x36\x35\x36\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\ +\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\ +\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\ +\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\ +\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\x3c\x63\ +\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x2d\x39\x2e\x33\x39\x31\ +\x31\x38\x27\x20\x63\x79\x3d\x27\x2d\x30\x27\x20\x72\x3d\x27\x33\ +\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\ +\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\ +\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\ +\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\ +\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\ +\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\x3c\x63\x69\x72\ +\x63\x6c\x65\x20\x63\x78\x3d\x27\x2d\x36\x2e\x36\x34\x30\x35\x37\ +\x27\x20\x63\x79\x3d\x27\x2d\x31\x32\x2e\x30\x34\x35\x27\x20\x72\ +\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\ +\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\ +\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\ +\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\ +\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\ +\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\x3e\x0a\x3c\ +\x63\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x38\x2e\x36\x37\x36\ +\x33\x32\x27\x20\x63\x79\x3d\x27\x2d\x36\x2e\x35\x31\x38\x37\x31\ +\x27\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\ +\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\ +\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\ +\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\ +\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\ +\x38\x2e\x37\x36\x35\x35\x20\x36\x38\x2e\x38\x35\x38\x33\x29\x27\ +\x3e\x0a\x3c\x63\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x33\x2e\ +\x33\x32\x30\x32\x38\x27\x20\x63\x79\x3d\x27\x36\x2e\x30\x32\x32\ +\x35\x27\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\ +\x69\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\ +\x3c\x2f\x67\x3e\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x07\x80\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x3e\x0d\x0a\x09\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x39\x33\x2e\x34\x36\ +\x2c\x32\x34\x39\x2e\x30\x35\x36\x63\x33\x2e\x37\x32\x33\x2d\x30\ +\x2e\x36\x37\x2c\x37\x2e\x35\x38\x39\x2d\x31\x2e\x30\x34\x31\x2c\ +\x31\x31\x2e\x35\x38\x36\x2d\x31\x2e\x30\x34\x31\x4c\x32\x30\x31\ +\x2e\x39\x32\x34\x2c\x32\x34\x38\x68\x31\x30\x33\x2e\x38\x32\x33\ +\x63\x34\x2e\x35\x30\x33\x2c\x30\x2c\x38\x2e\x38\x30\x36\x2d\x30\ +\x2e\x36\x31\x37\x2c\x31\x32\x2e\x39\x30\x38\x2d\x31\x2e\x37\x35\ +\x34\x0d\x0a\x09\x09\x63\x31\x39\x2e\x33\x37\x2d\x35\x2e\x33\x36\ +\x33\x2c\x33\x33\x2e\x33\x34\x35\x2d\x32\x32\x2e\x35\x33\x37\x2c\ +\x33\x33\x2e\x33\x34\x35\x2d\x34\x33\x2e\x36\x36\x33\x76\x2d\x33\ +\x30\x2e\x38\x32\x32\x76\x2d\x35\x36\x2e\x34\x30\x32\x63\x30\x2d\ +\x32\x34\x2e\x38\x33\x32\x2d\x32\x31\x2e\x31\x35\x2d\x34\x33\x2e\ +\x34\x38\x34\x2d\x34\x36\x2e\x32\x38\x39\x2d\x34\x37\x2e\x36\x30\ +\x36\x0d\x0a\x09\x09\x63\x2d\x31\x35\x2e\x39\x33\x31\x2d\x32\x2e\ +\x36\x32\x34\x2d\x33\x39\x2e\x32\x35\x38\x2d\x33\x2e\x38\x32\x37\ +\x2d\x35\x35\x2e\x30\x38\x39\x2d\x33\x2e\x37\x34\x39\x63\x2d\x31\ +\x35\x2e\x38\x32\x39\x2c\x30\x2e\x30\x38\x36\x2d\x33\x30\x2e\x39\ +\x38\x31\x2c\x31\x2e\x34\x30\x34\x2d\x34\x34\x2e\x32\x37\x37\x2c\ +\x33\x2e\x37\x34\x39\x43\x31\x36\x37\x2e\x31\x34\x33\x2c\x37\x34\ +\x2e\x35\x37\x36\x2c\x31\x36\x30\x2c\x38\x38\x2e\x39\x32\x38\x2c\ +\x31\x36\x30\x2c\x31\x31\x35\x2e\x33\x35\x39\x56\x31\x34\x34\x68\ +\x39\x36\x0d\x0a\x09\x09\x76\x31\x36\x48\x31\x32\x38\x2e\x38\x32\ +\x63\x2d\x33\x35\x2e\x36\x32\x38\x2c\x30\x2d\x36\x34\x2e\x35\x33\ +\x38\x2c\x34\x32\x2e\x35\x37\x31\x2d\x36\x34\x2e\x38\x31\x33\x2c\ +\x39\x35\x2e\x32\x34\x32\x43\x36\x34\x2e\x30\x30\x35\x2c\x32\x35\ +\x35\x2e\x34\x39\x35\x2c\x36\x34\x2c\x32\x35\x35\x2e\x37\x34\x37\ +\x2c\x36\x34\x2c\x32\x35\x36\x63\x30\x2c\x39\x2e\x35\x32\x33\x2c\ +\x30\x2e\x39\x34\x2c\x31\x38\x2e\x37\x32\x2c\x32\x2e\x36\x38\x35\ +\x2c\x32\x37\x2e\x34\x30\x34\x0d\x0a\x09\x09\x43\x37\x34\x2e\x36\ +\x34\x38\x2c\x33\x32\x33\x2e\x30\x37\x2c\x39\x39\x2e\x34\x35\x31\ +\x2c\x33\x35\x32\x2c\x31\x32\x38\x2e\x38\x32\x2c\x33\x35\x32\x48\ +\x31\x34\x34\x76\x2d\x32\x2e\x36\x36\x32\x76\x2d\x34\x33\x2e\x32\ +\x37\x33\x43\x31\x34\x34\x2c\x32\x37\x39\x2e\x32\x33\x38\x2c\x31\ +\x36\x34\x2e\x31\x34\x36\x2c\x32\x35\x34\x2e\x33\x33\x32\x2c\x31\ +\x39\x33\x2e\x34\x36\x2c\x32\x34\x39\x2e\x30\x35\x36\x7a\x20\x4d\ +\x32\x30\x33\x2e\x36\x35\x36\x2c\x31\x32\x37\x2e\x30\x30\x32\x0d\ +\x0a\x09\x09\x63\x2d\x39\x2e\x35\x39\x32\x2c\x30\x2d\x31\x37\x2e\ +\x33\x38\x34\x2d\x37\x2e\x37\x38\x35\x2d\x31\x37\x2e\x33\x38\x34\ +\x2d\x31\x37\x2e\x34\x30\x33\x63\x30\x2d\x39\x2e\x36\x36\x34\x2c\ +\x37\x2e\x37\x37\x34\x2d\x31\x37\x2e\x35\x32\x2c\x31\x37\x2e\x33\ +\x38\x34\x2d\x31\x37\x2e\x35\x32\x63\x39\x2e\x35\x37\x34\x2c\x30\ +\x2c\x31\x37\x2e\x33\x39\x39\x2c\x37\x2e\x38\x35\x35\x2c\x31\x37\ +\x2e\x33\x39\x39\x2c\x31\x37\x2e\x35\x32\x0d\x0a\x09\x09\x43\x32\ +\x32\x31\x2e\x30\x35\x36\x2c\x31\x31\x39\x2e\x32\x31\x37\x2c\x32\ +\x31\x33\x2e\x32\x34\x36\x2c\x31\x32\x37\x2e\x30\x30\x32\x2c\x32\ +\x30\x33\x2e\x36\x35\x36\x2c\x31\x32\x37\x2e\x30\x30\x32\x7a\x22\ +\x2f\x3e\x0d\x0a\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x34\ +\x34\x33\x2e\x39\x35\x31\x2c\x32\x32\x32\x2e\x35\x34\x33\x43\x34\ +\x33\x34\x2e\x37\x38\x2c\x31\x38\x36\x2e\x30\x32\x31\x2c\x34\x31\ +\x31\x2e\x30\x33\x33\x2c\x31\x36\x30\x2c\x33\x38\x33\x2e\x31\x38\ +\x2c\x31\x36\x30\x48\x33\x36\x38\x76\x32\x2e\x36\x32\x36\x76\x33\ +\x38\x2e\x30\x34\x36\x63\x30\x2c\x33\x33\x2e\x39\x31\x35\x2d\x32\ +\x32\x2e\x32\x38\x36\x2c\x35\x38\x2e\x34\x37\x34\x2d\x34\x39\x2e\ +\x34\x38\x39\x2c\x36\x32\x2e\x36\x38\x31\x0d\x0a\x09\x09\x63\x2d\ +\x32\x2e\x37\x33\x37\x2c\x30\x2e\x34\x32\x34\x2d\x35\x2e\x34\x38\ +\x33\x2c\x30\x2e\x36\x34\x36\x2d\x38\x2e\x33\x30\x31\x2c\x30\x2e\ +\x36\x34\x36\x48\x32\x30\x36\x2e\x33\x35\x31\x63\x2d\x34\x2e\x35\ +\x31\x38\x2c\x30\x2d\x38\x2e\x39\x30\x34\x2c\x30\x2e\x35\x38\x34\ +\x2d\x31\x33\x2e\x30\x34\x39\x2c\x31\x2e\x36\x37\x32\x43\x31\x37\ +\x34\x2e\x31\x38\x2c\x32\x37\x30\x2e\x36\x38\x39\x2c\x31\x36\x30\ +\x2c\x32\x38\x36\x2e\x36\x2c\x31\x36\x30\x2c\x33\x30\x37\x2e\x32\ +\x33\x36\x76\x33\x32\x2e\x39\x32\x32\x0d\x0a\x09\x09\x76\x35\x34\ +\x2e\x33\x30\x35\x63\x30\x2c\x32\x34\x2e\x38\x33\x32\x2c\x32\x34\ +\x2e\x39\x37\x37\x2c\x33\x39\x2e\x34\x32\x36\x2c\x34\x39\x2e\x34\ +\x38\x31\x2c\x34\x36\x2e\x35\x35\x31\x63\x32\x39\x2e\x33\x32\x37\ +\x2c\x38\x2e\x35\x33\x31\x2c\x36\x31\x2e\x32\x36\x37\x2c\x31\x30\ +\x2e\x30\x36\x38\x2c\x39\x36\x2e\x33\x36\x36\x2c\x30\x43\x33\x32\ +\x39\x2e\x31\x35\x2c\x34\x33\x34\x2e\x33\x35\x34\x2c\x33\x35\x32\ +\x2c\x34\x32\x30\x2e\x38\x39\x33\x2c\x33\x35\x32\x2c\x33\x39\x34\ +\x2e\x34\x36\x33\x56\x33\x36\x38\x0d\x0a\x09\x09\x68\x2d\x39\x36\ +\x76\x2d\x31\x36\x68\x31\x32\x37\x2e\x31\x38\x63\x32\x35\x2e\x32\ +\x34\x2c\x30\x2c\x34\x37\x2e\x31\x30\x37\x2d\x32\x31\x2e\x33\x36\ +\x35\x2c\x35\x37\x2e\x38\x31\x34\x2d\x35\x32\x2e\x35\x34\x39\x43\ +\x34\x34\x35\x2e\x34\x37\x34\x2c\x32\x38\x36\x2e\x34\x30\x34\x2c\ +\x34\x34\x38\x2c\x32\x37\x31\x2e\x36\x34\x31\x2c\x34\x34\x38\x2c\ +\x32\x35\x36\x0d\x0a\x09\x09\x43\x34\x34\x38\x2c\x32\x34\x34\x2e\ +\x32\x33\x32\x2c\x34\x34\x36\x2e\x35\x36\x37\x2c\x32\x33\x32\x2e\ +\x39\x36\x32\x2c\x34\x34\x33\x2e\x39\x35\x31\x2c\x32\x32\x32\x2e\ +\x35\x34\x33\x7a\x20\x4d\x33\x30\x37\x2e\x38\x36\x37\x2c\x33\x38\ +\x32\x2e\x38\x32\x63\x39\x2e\x35\x39\x2c\x30\x2c\x31\x37\x2e\x33\ +\x38\x31\x2c\x37\x2e\x37\x38\x35\x2c\x31\x37\x2e\x33\x38\x31\x2c\ +\x31\x37\x2e\x34\x0d\x0a\x09\x09\x63\x30\x2c\x39\x2e\x36\x35\x2d\ +\x37\x2e\x37\x39\x31\x2c\x31\x37\x2e\x35\x32\x31\x2d\x31\x37\x2e\ +\x33\x38\x31\x2c\x31\x37\x2e\x35\x32\x31\x63\x2d\x39\x2e\x35\x37\ +\x37\x2c\x30\x2d\x31\x37\x2e\x33\x39\x39\x2d\x37\x2e\x38\x37\x31\ +\x2d\x31\x37\x2e\x33\x39\x39\x2d\x31\x37\x2e\x35\x32\x31\x43\x32\ +\x39\x30\x2e\x34\x36\x38\x2c\x33\x39\x30\x2e\x35\x39\x2c\x32\x39\ +\x38\x2e\x32\x37\x34\x2c\x33\x38\x32\x2e\x38\x32\x2c\x33\x30\x37\ +\x2e\x38\x36\x37\x2c\x33\x38\x32\x2e\x38\x32\x7a\x22\x2f\x3e\x0d\ +\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x05\x27\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x31\x32\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\ +\x3e\x0d\x0a\x09\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\ +\x35\x36\x2c\x36\x34\x43\x31\x35\x30\x2e\x34\x30\x31\x2c\x36\x34\ +\x2c\x36\x34\x2c\x31\x35\x30\x2e\x34\x30\x31\x2c\x36\x34\x2c\x32\ +\x35\x36\x63\x30\x2c\x31\x30\x35\x2e\x36\x30\x34\x2c\x38\x36\x2e\ +\x34\x30\x31\x2c\x31\x39\x32\x2c\x31\x39\x32\x2c\x31\x39\x32\x63\ +\x31\x38\x2e\x31\x33\x36\x2c\x30\x2c\x33\x32\x2d\x31\x33\x2e\x38\ +\x36\x34\x2c\x33\x32\x2d\x33\x32\x0d\x0a\x09\x09\x09\x63\x30\x2d\ +\x38\x2e\x35\x33\x31\x2d\x33\x2e\x31\x39\x38\x2d\x31\x36\x2d\x38\ +\x2e\x35\x33\x31\x2d\x32\x31\x2e\x33\x33\x33\x63\x2d\x35\x2e\x33\ +\x33\x33\x2d\x35\x2e\x33\x33\x34\x2d\x38\x2e\x35\x33\x31\x2d\x31\ +\x32\x2e\x38\x30\x33\x2d\x38\x2e\x35\x33\x31\x2d\x32\x31\x2e\x33\ +\x33\x34\x63\x30\x2d\x31\x38\x2e\x31\x33\x35\x2c\x31\x33\x2e\x38\ +\x36\x34\x2d\x33\x32\x2c\x33\x32\x2d\x33\x32\x68\x33\x38\x2e\x33\ +\x39\x36\x0d\x0a\x09\x09\x09\x63\x35\x38\x2e\x36\x36\x37\x2c\x30\ +\x2c\x31\x30\x36\x2e\x36\x36\x37\x2d\x34\x38\x2c\x31\x30\x36\x2e\ +\x36\x36\x37\x2d\x31\x30\x36\x2e\x36\x36\x36\x43\x34\x34\x38\x2c\ +\x31\x34\x30\x2e\x38\x30\x32\x2c\x33\x36\x31\x2e\x36\x30\x34\x2c\ +\x36\x34\x2c\x32\x35\x36\x2c\x36\x34\x7a\x20\x4d\x31\x33\x38\x2e\ +\x36\x36\x37\x2c\x32\x35\x36\x63\x2d\x31\x38\x2e\x31\x33\x36\x2c\ +\x30\x2d\x33\x32\x2d\x31\x33\x2e\x38\x36\x34\x2d\x33\x32\x2d\x33\ +\x32\x73\x31\x33\x2e\x38\x36\x34\x2d\x33\x32\x2c\x33\x32\x2d\x33\ +\x32\x0d\x0a\x09\x09\x09\x63\x31\x38\x2e\x31\x33\x35\x2c\x30\x2c\ +\x33\x32\x2c\x31\x33\x2e\x38\x36\x34\x2c\x33\x32\x2c\x33\x32\x53\ +\x31\x35\x36\x2e\x38\x30\x32\x2c\x32\x35\x36\x2c\x31\x33\x38\x2e\ +\x36\x36\x37\x2c\x32\x35\x36\x7a\x20\x4d\x32\x30\x32\x2e\x36\x36\ +\x37\x2c\x31\x37\x30\x2e\x36\x36\x37\x63\x2d\x31\x38\x2e\x31\x33\ +\x36\x2c\x30\x2d\x33\x32\x2d\x31\x33\x2e\x38\x36\x35\x2d\x33\x32\ +\x2d\x33\x32\x63\x30\x2d\x31\x38\x2e\x31\x33\x36\x2c\x31\x33\x2e\ +\x38\x36\x34\x2d\x33\x32\x2c\x33\x32\x2d\x33\x32\x0d\x0a\x09\x09\ +\x09\x63\x31\x38\x2e\x31\x33\x35\x2c\x30\x2c\x33\x32\x2c\x31\x33\ +\x2e\x38\x36\x34\x2c\x33\x32\x2c\x33\x32\x43\x32\x33\x34\x2e\x36\ +\x36\x37\x2c\x31\x35\x36\x2e\x38\x30\x32\x2c\x32\x32\x30\x2e\x38\ +\x30\x32\x2c\x31\x37\x30\x2e\x36\x36\x37\x2c\x32\x30\x32\x2e\x36\ +\x36\x37\x2c\x31\x37\x30\x2e\x36\x36\x37\x7a\x20\x4d\x33\x30\x39\ +\x2e\x33\x33\x33\x2c\x31\x37\x30\x2e\x36\x36\x37\x63\x2d\x31\x38\ +\x2e\x31\x33\x35\x2c\x30\x2d\x33\x32\x2d\x31\x33\x2e\x38\x36\x35\ +\x2d\x33\x32\x2d\x33\x32\x0d\x0a\x09\x09\x09\x63\x30\x2d\x31\x38\ +\x2e\x31\x33\x36\x2c\x31\x33\x2e\x38\x36\x35\x2d\x33\x32\x2c\x33\ +\x32\x2d\x33\x32\x63\x31\x38\x2e\x31\x33\x36\x2c\x30\x2c\x33\x32\ +\x2c\x31\x33\x2e\x38\x36\x34\x2c\x33\x32\x2c\x33\x32\x43\x33\x34\ +\x31\x2e\x33\x33\x33\x2c\x31\x35\x36\x2e\x38\x30\x32\x2c\x33\x32\ +\x37\x2e\x34\x36\x39\x2c\x31\x37\x30\x2e\x36\x36\x37\x2c\x33\x30\ +\x39\x2e\x33\x33\x33\x2c\x31\x37\x30\x2e\x36\x36\x37\x7a\x20\x4d\ +\x33\x37\x33\x2e\x33\x33\x33\x2c\x32\x35\x36\x0d\x0a\x09\x09\x09\ +\x63\x2d\x31\x38\x2e\x31\x33\x35\x2c\x30\x2d\x33\x32\x2d\x31\x33\ +\x2e\x38\x36\x34\x2d\x33\x32\x2d\x33\x32\x73\x31\x33\x2e\x38\x36\ +\x35\x2d\x33\x32\x2c\x33\x32\x2d\x33\x32\x63\x31\x38\x2e\x31\x33\ +\x36\x2c\x30\x2c\x33\x32\x2c\x31\x33\x2e\x38\x36\x34\x2c\x33\x32\ +\x2c\x33\x32\x53\x33\x39\x31\x2e\x34\x36\x39\x2c\x32\x35\x36\x2c\ +\x33\x37\x33\x2e\x33\x33\x33\x2c\x32\x35\x36\x7a\x22\x2f\x3e\x0d\ +\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\ +\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x02\xa2\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x3e\x0d\x0a\x09\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x32\x38\x2c\x34\x30\ +\x35\x2e\x34\x32\x39\x43\x31\x32\x38\x2c\x34\x32\x38\x2e\x38\x34\ +\x36\x2c\x31\x34\x37\x2e\x31\x39\x38\x2c\x34\x34\x38\x2c\x31\x37\ +\x30\x2e\x36\x36\x37\x2c\x34\x34\x38\x68\x31\x37\x30\x2e\x36\x36\ +\x37\x43\x33\x36\x34\x2e\x38\x30\x32\x2c\x34\x34\x38\x2c\x33\x38\ +\x34\x2c\x34\x32\x38\x2e\x38\x34\x36\x2c\x33\x38\x34\x2c\x34\x30\ +\x35\x2e\x34\x32\x39\x56\x31\x36\x30\x48\x31\x32\x38\x56\x34\x30\ +\x35\x2e\x34\x32\x39\x7a\x20\x4d\x34\x31\x36\x2c\x39\x36\x0d\x0a\ +\x09\x09\x68\x2d\x38\x30\x6c\x2d\x32\x36\x2e\x37\x38\x35\x2d\x33\ +\x32\x48\x32\x30\x32\x2e\x37\x38\x36\x4c\x31\x37\x36\x2c\x39\x36\ +\x48\x39\x36\x76\x33\x32\x68\x33\x32\x30\x56\x39\x36\x7a\x22\x2f\ +\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\ +\x0a\ +\x00\x00\x03\x22\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x3e\x0d\x0a\x09\ +\x3c\x63\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x22\x32\x35\x36\x22\ +\x20\x63\x79\x3d\x22\x32\x38\x30\x22\x20\x72\x3d\x22\x36\x33\x22\ +\x2f\x3e\x0d\x0a\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x34\ +\x34\x30\x2c\x39\x36\x68\x2d\x38\x38\x6c\x2d\x33\x32\x2d\x33\x32\ +\x48\x31\x39\x32\x6c\x2d\x33\x32\x2c\x33\x32\x48\x37\x32\x63\x2d\ +\x32\x32\x2e\x30\x39\x32\x2c\x30\x2d\x34\x30\x2c\x31\x37\x2e\x39\ +\x30\x38\x2d\x34\x30\x2c\x34\x30\x76\x32\x37\x32\x63\x30\x2c\x32\ +\x32\x2e\x30\x39\x32\x2c\x31\x37\x2e\x39\x30\x38\x2c\x34\x30\x2c\ +\x34\x30\x2c\x34\x30\x68\x33\x36\x38\x63\x32\x32\x2e\x30\x39\x32\ +\x2c\x30\x2c\x34\x30\x2d\x31\x37\x2e\x39\x30\x38\x2c\x34\x30\x2d\ +\x34\x30\x0d\x0a\x09\x09\x56\x31\x33\x36\x43\x34\x38\x30\x2c\x31\ +\x31\x33\x2e\x39\x30\x38\x2c\x34\x36\x32\x2e\x30\x39\x32\x2c\x39\ +\x36\x2c\x34\x34\x30\x2c\x39\x36\x7a\x20\x4d\x32\x35\x36\x2c\x33\ +\x39\x32\x63\x2d\x36\x31\x2e\x38\x35\x35\x2c\x30\x2d\x31\x31\x32\ +\x2d\x35\x30\x2e\x31\x34\x35\x2d\x31\x31\x32\x2d\x31\x31\x32\x73\ +\x35\x30\x2e\x31\x34\x35\x2d\x31\x31\x32\x2c\x31\x31\x32\x2d\x31\ +\x31\x32\x73\x31\x31\x32\x2c\x35\x30\x2e\x31\x34\x35\x2c\x31\x31\ +\x32\x2c\x31\x31\x32\x0d\x0a\x09\x09\x53\x33\x31\x37\x2e\x38\x35\ +\x35\x2c\x33\x39\x32\x2c\x32\x35\x36\x2c\x33\x39\x32\x7a\x22\x2f\ +\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\ +\x0a\ +\x00\x00\x02\x7d\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x38\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\x3e\ +\x0d\x0a\x09\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x38\x35\ +\x2c\x32\x37\x37\x2e\x33\x37\x35\x68\x32\x35\x39\x2e\x37\x30\x34\ +\x4c\x32\x32\x35\x2e\x30\x30\x32\x2c\x33\x39\x37\x2e\x30\x37\x37\ +\x4c\x32\x35\x36\x2c\x34\x32\x37\x6c\x31\x37\x31\x2d\x31\x37\x31\ +\x4c\x32\x35\x36\x2c\x38\x35\x6c\x2d\x32\x39\x2e\x39\x32\x32\x2c\ +\x32\x39\x2e\x39\x32\x34\x6c\x31\x31\x38\x2e\x36\x32\x36\x2c\x31\ +\x31\x39\x2e\x37\x30\x31\x48\x38\x35\x56\x32\x37\x37\x2e\x33\x37\ +\x35\x7a\x22\x2f\x3e\x0d\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\ +\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x05\xb7\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x32\x34\x70\x74\x27\x20\x76\x65\x72\x73\x69\x6f\x6e\ +\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x27\ +\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\x38\x33\x20\ +\x31\x38\x2e\x34\x33\x37\x35\x20\x32\x34\x27\x20\x77\x69\x64\x74\ +\x68\x3d\x27\x31\x38\x2e\x34\x33\x37\x35\x70\x74\x27\x20\x78\x6d\ +\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\ +\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x27\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x27\x68\x74\ +\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\ +\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x27\x3e\x0a\x3c\x67\x20\ +\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\x3e\x0a\x3c\x67\x20\x74\ +\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\ +\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\ +\x39\x39\x36\x32\x36\x34\x20\x36\x35\x2e\x36\x32\x38\x32\x20\x37\ +\x34\x2e\x32\x30\x32\x29\x27\x3e\x0a\x3c\x70\x61\x74\x68\x20\x64\ +\x3d\x27\x4d\x20\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x31\x2e\ +\x31\x36\x36\x37\x43\x20\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\ +\x32\x2e\x37\x30\x38\x35\x20\x34\x2e\x36\x32\x35\x34\x31\x20\x2d\ +\x31\x33\x2e\x39\x35\x38\x34\x20\x30\x20\x2d\x31\x33\x2e\x39\x35\ +\x38\x34\x43\x20\x2d\x34\x2e\x36\x32\x35\x34\x31\x20\x2d\x31\x33\ +\x2e\x39\x35\x38\x34\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\ +\x31\x32\x2e\x37\x30\x38\x35\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\ +\x20\x2d\x31\x31\x2e\x31\x36\x36\x37\x43\x20\x2d\x38\x2e\x33\x37\ +\x35\x30\x34\x20\x2d\x39\x2e\x36\x32\x34\x39\x32\x20\x2d\x34\x2e\ +\x36\x32\x35\x34\x31\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x30\ +\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x43\x20\x34\x2e\x36\x32\x35\ +\x34\x31\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x38\x2e\x33\x37\ +\x35\x30\x34\x20\x2d\x39\x2e\x36\x32\x34\x39\x32\x20\x38\x2e\x33\ +\x37\x35\x30\x34\x20\x2d\x31\x31\x2e\x31\x36\x36\x37\x5a\x27\x20\ +\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\x27\x20\x73\x74\x72\x6f\ +\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x20\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3d\x27\x72\x6f\x75\ +\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\ +\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3d\x27\x31\ +\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\ +\x69\x64\x74\x68\x3d\x27\x31\x2e\x37\x35\x36\x35\x36\x27\x2f\x3e\ +\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\ +\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\ +\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\ +\x20\x36\x35\x2e\x36\x32\x38\x32\x20\x37\x34\x2e\x32\x30\x32\x29\ +\x27\x3e\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\x20\x2d\x38\ +\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x31\x2e\x31\x36\x36\x37\x4c\ +\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x30\x4c\x20\x2d\x38\ +\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x2e\x38\x35\x39\x36\x33\x65\ +\x2d\x31\x35\x43\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\ +\x2e\x32\x33\x39\x37\x35\x65\x2d\x31\x35\x20\x2d\x38\x2e\x33\x37\ +\x35\x30\x34\x20\x2d\x36\x2e\x31\x39\x38\x37\x37\x65\x2d\x31\x36\ +\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x30\x43\x20\x2d\x38\ +\x2e\x33\x37\x35\x30\x34\x20\x31\x2e\x35\x34\x31\x38\x20\x2d\x34\ +\x2e\x36\x32\x35\x34\x31\x20\x32\x2e\x37\x39\x31\x36\x38\x20\x30\ +\x20\x32\x2e\x37\x39\x31\x36\x38\x43\x20\x34\x2e\x36\x32\x35\x34\ +\x31\x20\x32\x2e\x37\x39\x31\x36\x38\x20\x38\x2e\x33\x37\x35\x30\ +\x34\x20\x31\x2e\x35\x34\x31\x38\x20\x38\x2e\x33\x37\x35\x30\x34\ +\x20\x30\x4c\x20\x38\x2e\x33\x37\x35\x30\x34\x20\x30\x4c\x20\x38\ +\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x31\x2e\x31\x36\x36\x37\x27\ +\x20\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\x27\x20\x73\x74\x72\ +\x6f\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3d\x27\x72\x6f\ +\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\ +\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3d\x27\ +\x31\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\ +\x77\x69\x64\x74\x68\x3d\x27\x31\x2e\x37\x35\x36\x35\x36\x27\x2f\ +\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\ +\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\ +\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\ +\x34\x20\x36\x35\x2e\x36\x32\x38\x32\x20\x37\x34\x2e\x32\x30\x32\ +\x29\x27\x3e\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\x20\x38\ +\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x31\x2e\x31\x36\x36\x37\x43\ +\x20\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x35\x2e\x37\x39\x32\ +\x31\x20\x34\x2e\x36\x32\x35\x34\x31\x20\x2d\x31\x39\x2e\x35\x34\ +\x31\x38\x20\x30\x20\x2d\x31\x39\x2e\x35\x34\x31\x38\x43\x20\x2d\ +\x34\x2e\x36\x32\x35\x34\x31\x20\x2d\x31\x39\x2e\x35\x34\x31\x38\ +\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x35\x2e\x37\x39\ +\x32\x31\x20\x2d\x38\x2e\x33\x37\x35\x30\x34\x20\x2d\x31\x31\x2e\ +\x31\x36\x36\x37\x27\x20\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\ +\x27\x20\x73\x74\x72\x6f\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\ +\x30\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\ +\x70\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\ +\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\ +\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\ +\x6d\x69\x74\x3d\x27\x31\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3d\x27\x31\x2e\x37\x35\ +\x36\x35\x36\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x3c\x2f\x67\x3e\x0a\ +\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x03\x26\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x3e\x0d\x0a\x09\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\x35\x36\x2c\x33\x38\ +\x38\x63\x2d\x37\x32\x2e\x35\x39\x37\x2c\x30\x2d\x31\x33\x32\x2d\ +\x35\x39\x2e\x34\x30\x35\x2d\x31\x33\x32\x2d\x31\x33\x32\x63\x30\ +\x2d\x37\x32\x2e\x36\x30\x31\x2c\x35\x39\x2e\x34\x30\x33\x2d\x31\ +\x33\x32\x2c\x31\x33\x32\x2d\x31\x33\x32\x63\x33\x36\x2e\x33\x2c\ +\x30\x2c\x36\x39\x2e\x32\x39\x39\x2c\x31\x35\x2e\x34\x2c\x39\x32\ +\x2e\x34\x30\x36\x2c\x33\x39\x2e\x36\x30\x31\x4c\x32\x37\x38\x2c\ +\x32\x33\x34\x68\x31\x35\x34\x56\x38\x30\x0d\x0a\x09\x09\x6c\x2d\ +\x35\x31\x2e\x36\x39\x38\x2c\x35\x31\x2e\x37\x30\x32\x43\x33\x34\ +\x38\x2e\x34\x30\x36\x2c\x39\x39\x2e\x37\x39\x38\x2c\x33\x30\x34\ +\x2e\x34\x30\x36\x2c\x38\x30\x2c\x32\x35\x36\x2c\x38\x30\x63\x2d\ +\x39\x36\x2e\x37\x39\x37\x2c\x30\x2d\x31\x37\x36\x2c\x37\x39\x2e\ +\x32\x30\x33\x2d\x31\x37\x36\x2c\x31\x37\x36\x73\x37\x38\x2e\x30\ +\x39\x34\x2c\x31\x37\x36\x2c\x31\x37\x36\x2c\x31\x37\x36\x0d\x0a\ +\x09\x09\x63\x38\x31\x2e\x30\x34\x35\x2c\x30\x2c\x31\x34\x38\x2e\ +\x32\x38\x37\x2d\x35\x34\x2e\x31\x33\x34\x2c\x31\x36\x39\x2e\x34\ +\x30\x31\x2d\x31\x32\x38\x48\x33\x37\x38\x2e\x38\x35\x43\x33\x36\ +\x30\x2e\x31\x30\x35\x2c\x33\x35\x33\x2e\x35\x36\x31\x2c\x33\x31\ +\x31\x2e\x37\x31\x32\x2c\x33\x38\x38\x2c\x32\x35\x36\x2c\x33\x38\ +\x38\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\ +\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\x0c\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x30\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x43\x68\x65\x63\x6b\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\ +\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\ +\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\x20\x79\ +\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x76\x69\x65\x77\x42\x6f\ +\x78\x3d\x22\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\x6e\ +\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3d\ +\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\ +\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\ +\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\ +\x4d\x38\x2e\x32\x39\x34\x2c\x31\x36\x2e\x39\x39\x38\x63\x2d\x30\ +\x2e\x34\x33\x35\x2c\x30\x2d\x30\x2e\x38\x34\x37\x2d\x30\x2e\x32\ +\x30\x33\x2d\x31\x2e\x31\x31\x31\x2d\x30\x2e\x35\x35\x33\x4c\x33\ +\x2e\x36\x31\x2c\x31\x31\x2e\x37\x32\x34\x63\x2d\x30\x2e\x34\x36\ +\x35\x2d\x30\x2e\x36\x31\x33\x2d\x30\x2e\x33\x34\x34\x2d\x31\x2e\ +\x34\x38\x36\x2c\x30\x2e\x32\x37\x2d\x31\x2e\x39\x35\x31\x0d\x0a\ +\x09\x63\x30\x2e\x36\x31\x35\x2d\x30\x2e\x34\x36\x37\x2c\x31\x2e\ +\x34\x38\x38\x2d\x30\x2e\x33\x34\x34\x2c\x31\x2e\x39\x35\x33\x2c\ +\x30\x2e\x32\x37\x6c\x32\x2e\x33\x35\x31\x2c\x33\x2e\x31\x30\x34\ +\x6c\x35\x2e\x39\x31\x31\x2d\x39\x2e\x34\x39\x32\x63\x30\x2e\x34\ +\x30\x37\x2d\x30\x2e\x36\x35\x32\x2c\x31\x2e\x32\x36\x37\x2d\x30\ +\x2e\x38\x35\x32\x2c\x31\x2e\x39\x32\x31\x2d\x30\x2e\x34\x34\x35\ +\x0d\x0a\x09\x63\x30\x2e\x36\x35\x33\x2c\x30\x2e\x34\x30\x36\x2c\ +\x30\x2e\x38\x35\x34\x2c\x31\x2e\x32\x36\x36\x2c\x30\x2e\x34\x34\ +\x36\x2c\x31\x2e\x39\x32\x4c\x39\x2e\x34\x37\x38\x2c\x31\x36\x2e\ +\x33\x34\x63\x2d\x30\x2e\x32\x34\x32\x2c\x30\x2e\x33\x39\x31\x2d\ +\x30\x2e\x36\x36\x31\x2c\x30\x2e\x36\x33\x35\x2d\x31\x2e\x31\x32\ +\x2c\x30\x2e\x36\x35\x36\x43\x38\x2e\x33\x33\x36\x2c\x31\x36\x2e\ +\x39\x39\x38\x2c\x38\x2e\x33\x31\x36\x2c\x31\x36\x2e\x39\x39\x38\ +\x2c\x38\x2e\x32\x39\x34\x2c\x31\x36\x2e\x39\x39\x38\x7a\x22\x2f\ +\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x02\xfc\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x32\x30\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\ +\x3e\x0d\x0a\x09\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\ +\x35\x36\x2c\x34\x38\x43\x31\x34\x31\x2e\x36\x30\x31\x2c\x34\x38\ +\x2c\x34\x38\x2c\x31\x34\x31\x2e\x36\x30\x31\x2c\x34\x38\x2c\x32\ +\x35\x36\x73\x39\x33\x2e\x36\x30\x31\x2c\x32\x30\x38\x2c\x32\x30\ +\x38\x2c\x32\x30\x38\x73\x32\x30\x38\x2d\x39\x33\x2e\x36\x30\x31\ +\x2c\x32\x30\x38\x2d\x32\x30\x38\x53\x33\x37\x30\x2e\x33\x39\x39\ +\x2c\x34\x38\x2c\x32\x35\x36\x2c\x34\x38\x7a\x20\x4d\x32\x35\x36\ +\x2c\x34\x32\x32\x2e\x33\x39\x39\x0d\x0a\x09\x09\x09\x63\x2d\x39\ +\x31\x2e\x35\x31\x38\x2c\x30\x2d\x31\x36\x36\x2e\x33\x39\x39\x2d\ +\x37\x34\x2e\x38\x38\x32\x2d\x31\x36\x36\x2e\x33\x39\x39\x2d\x31\ +\x36\x36\x2e\x33\x39\x39\x53\x31\x36\x34\x2e\x34\x38\x32\x2c\x38\ +\x39\x2e\x36\x2c\x32\x35\x36\x2c\x38\x39\x2e\x36\x53\x34\x32\x32\ +\x2e\x34\x2c\x31\x36\x34\x2e\x34\x38\x32\x2c\x34\x32\x32\x2e\x34\ +\x2c\x32\x35\x36\x53\x33\x34\x37\x2e\x35\x31\x38\x2c\x34\x32\x32\ +\x2e\x33\x39\x39\x2c\x32\x35\x36\x2c\x34\x32\x32\x2e\x33\x39\x39\ +\x7a\x22\x2f\x3e\x0d\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\ +\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\x4c\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x32\x31\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\ +\x3e\x0d\x0a\x09\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\ +\x35\x36\x2c\x31\x35\x32\x63\x2d\x35\x37\x2e\x32\x2c\x30\x2d\x31\ +\x30\x34\x2c\x34\x36\x2e\x38\x2d\x31\x30\x34\x2c\x31\x30\x34\x73\ +\x34\x36\x2e\x38\x2c\x31\x30\x34\x2c\x31\x30\x34\x2c\x31\x30\x34\ +\x73\x31\x30\x34\x2d\x34\x36\x2e\x38\x2c\x31\x30\x34\x2d\x31\x30\ +\x34\x53\x33\x31\x33\x2e\x32\x2c\x31\x35\x32\x2c\x32\x35\x36\x2c\ +\x31\x35\x32\x7a\x20\x4d\x32\x35\x36\x2c\x34\x38\x0d\x0a\x09\x09\ +\x09\x43\x31\x34\x31\x2e\x36\x30\x31\x2c\x34\x38\x2c\x34\x38\x2c\ +\x31\x34\x31\x2e\x36\x30\x31\x2c\x34\x38\x2c\x32\x35\x36\x73\x39\ +\x33\x2e\x36\x30\x31\x2c\x32\x30\x38\x2c\x32\x30\x38\x2c\x32\x30\ +\x38\x73\x32\x30\x38\x2d\x39\x33\x2e\x36\x30\x31\x2c\x32\x30\x38\ +\x2d\x32\x30\x38\x53\x33\x37\x30\x2e\x33\x39\x39\x2c\x34\x38\x2c\ +\x32\x35\x36\x2c\x34\x38\x7a\x20\x4d\x32\x35\x36\x2c\x34\x32\x32\ +\x2e\x34\x0d\x0a\x09\x09\x09\x63\x2d\x39\x31\x2e\x35\x31\x38\x2c\ +\x30\x2d\x31\x36\x36\x2e\x34\x2d\x37\x34\x2e\x38\x38\x33\x2d\x31\ +\x36\x36\x2e\x34\x2d\x31\x36\x36\x2e\x34\x53\x31\x36\x34\x2e\x34\ +\x38\x32\x2c\x38\x39\x2e\x36\x2c\x32\x35\x36\x2c\x38\x39\x2e\x36\ +\x53\x34\x32\x32\x2e\x34\x2c\x31\x36\x34\x2e\x34\x38\x32\x2c\x34\ +\x32\x32\x2e\x34\x2c\x32\x35\x36\x53\x33\x34\x37\x2e\x35\x31\x38\ +\x2c\x34\x32\x32\x2e\x34\x2c\x32\x35\x36\x2c\x34\x32\x32\x2e\x34\ +\x7a\x22\x2f\x3e\x0d\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\ +\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x04\x64\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x22\x3e\x0d\x0a\x09\x3c\x67\x3e\x0d\x0a\x09\ +\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\x35\x36\x2c\x31\ +\x37\x36\x63\x2d\x34\x34\x2e\x30\x30\x34\x2c\x30\x2d\x38\x30\x2e\ +\x30\x30\x31\x2c\x33\x36\x2d\x38\x30\x2e\x30\x30\x31\x2c\x38\x30\ +\x63\x30\x2c\x34\x34\x2e\x30\x30\x34\x2c\x33\x35\x2e\x39\x39\x37\ +\x2c\x38\x30\x2c\x38\x30\x2e\x30\x30\x31\x2c\x38\x30\x63\x34\x34\ +\x2e\x30\x30\x35\x2c\x30\x2c\x37\x39\x2e\x39\x39\x39\x2d\x33\x35\ +\x2e\x39\x39\x36\x2c\x37\x39\x2e\x39\x39\x39\x2d\x38\x30\x0d\x0a\ +\x09\x09\x09\x43\x33\x33\x35\x2e\x39\x39\x39\x2c\x32\x31\x32\x2c\ +\x33\x30\x30\x2e\x30\x30\x35\x2c\x31\x37\x36\x2c\x32\x35\x36\x2c\ +\x31\x37\x36\x7a\x20\x4d\x34\x34\x36\x2e\x39\x33\x38\x2c\x32\x33\ +\x34\x2e\x36\x36\x37\x63\x2d\x39\x2e\x36\x30\x35\x2d\x38\x38\x2e\ +\x35\x33\x31\x2d\x38\x31\x2e\x30\x37\x34\x2d\x31\x36\x30\x2d\x31\ +\x36\x39\x2e\x36\x30\x35\x2d\x31\x36\x39\x2e\x35\x39\x39\x56\x33\ +\x32\x68\x2d\x34\x32\x2e\x36\x36\x36\x76\x33\x33\x2e\x30\x36\x37\ +\x0d\x0a\x09\x09\x09\x63\x2d\x38\x38\x2e\x35\x33\x31\x2c\x39\x2e\ +\x35\x39\x39\x2d\x31\x36\x30\x2c\x38\x31\x2e\x30\x36\x38\x2d\x31\ +\x36\x39\x2e\x36\x30\x34\x2c\x31\x36\x39\x2e\x35\x39\x39\x48\x33\ +\x32\x76\x34\x32\x2e\x36\x36\x37\x68\x33\x33\x2e\x30\x36\x32\x63\ +\x39\x2e\x36\x30\x34\x2c\x38\x38\x2e\x35\x33\x31\x2c\x38\x31\x2e\ +\x30\x37\x32\x2c\x31\x36\x30\x2c\x31\x36\x39\x2e\x36\x30\x34\x2c\ +\x31\x36\x39\x2e\x36\x30\x34\x56\x34\x38\x30\x68\x34\x32\x2e\x36\ +\x36\x36\x76\x2d\x33\x33\x2e\x30\x36\x32\x0d\x0a\x09\x09\x09\x63\ +\x38\x38\x2e\x35\x33\x31\x2d\x39\x2e\x36\x30\x34\x2c\x31\x36\x30\ +\x2d\x38\x31\x2e\x30\x37\x33\x2c\x31\x36\x39\x2e\x36\x30\x35\x2d\ +\x31\x36\x39\x2e\x36\x30\x34\x48\x34\x38\x30\x76\x2d\x34\x32\x2e\ +\x36\x36\x37\x48\x34\x34\x36\x2e\x39\x33\x38\x7a\x20\x4d\x32\x35\ +\x36\x2c\x34\x30\x35\x2e\x33\x33\x33\x63\x2d\x38\x32\x2e\x31\x33\ +\x37\x2c\x30\x2d\x31\x34\x39\x2e\x33\x33\x34\x2d\x36\x37\x2e\x31\ +\x39\x38\x2d\x31\x34\x39\x2e\x33\x33\x34\x2d\x31\x34\x39\x2e\x33\ +\x33\x33\x0d\x0a\x09\x09\x09\x63\x30\x2d\x38\x32\x2e\x31\x33\x36\ +\x2c\x36\x37\x2e\x31\x39\x37\x2d\x31\x34\x39\x2e\x33\x33\x33\x2c\ +\x31\x34\x39\x2e\x33\x33\x34\x2d\x31\x34\x39\x2e\x33\x33\x33\x63\ +\x38\x32\x2e\x31\x33\x35\x2c\x30\x2c\x31\x34\x39\x2e\x33\x33\x32\ +\x2c\x36\x37\x2e\x31\x39\x38\x2c\x31\x34\x39\x2e\x33\x33\x32\x2c\ +\x31\x34\x39\x2e\x33\x33\x33\x43\x34\x30\x35\x2e\x33\x33\x32\x2c\ +\x33\x33\x38\x2e\x31\x33\x35\x2c\x33\x33\x38\x2e\x31\x33\x35\x2c\ +\x34\x30\x35\x2e\x33\x33\x33\x2c\x32\x35\x36\x2c\x34\x30\x35\x2e\ +\x33\x33\x33\x7a\x0d\x0a\x09\x09\x09\x22\x2f\x3e\x0d\x0a\x09\x3c\ +\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\ +\x3e\x0d\x0a\ +\x00\x00\x03\x36\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x3e\x0d\x0a\x09\ +\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\x6f\x69\x6e\x74\x73\x3d\ +\x22\x33\x39\x36\x2e\x37\x39\x35\x2c\x33\x39\x36\x2e\x38\x20\x33\ +\x32\x30\x2c\x33\x39\x36\x2e\x38\x20\x33\x32\x30\x2c\x34\x34\x38\ +\x20\x34\x34\x38\x2c\x34\x34\x38\x20\x34\x34\x38\x2c\x33\x32\x30\ +\x20\x33\x39\x36\x2e\x37\x39\x35\x2c\x33\x32\x30\x20\x09\x22\x2f\ +\x3e\x0d\x0a\x09\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\x6f\x69\ +\x6e\x74\x73\x3d\x22\x33\x39\x36\x2e\x38\x2c\x31\x31\x35\x2e\x32\ +\x30\x35\x20\x33\x39\x36\x2e\x38\x2c\x31\x39\x32\x20\x34\x34\x38\ +\x2c\x31\x39\x32\x20\x34\x34\x38\x2c\x36\x34\x20\x33\x32\x30\x2c\ +\x36\x34\x20\x33\x32\x30\x2c\x31\x31\x35\x2e\x32\x30\x35\x20\x09\ +\x22\x2f\x3e\x0d\x0a\x09\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\ +\x6f\x69\x6e\x74\x73\x3d\x22\x31\x31\x35\x2e\x32\x30\x35\x2c\x31\ +\x31\x35\x2e\x32\x20\x31\x39\x32\x2c\x31\x31\x35\x2e\x32\x20\x31\ +\x39\x32\x2c\x36\x34\x20\x36\x34\x2c\x36\x34\x20\x36\x34\x2c\x31\ +\x39\x32\x20\x31\x31\x35\x2e\x32\x30\x35\x2c\x31\x39\x32\x20\x09\ +\x22\x2f\x3e\x0d\x0a\x09\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\ +\x6f\x69\x6e\x74\x73\x3d\x22\x31\x31\x35\x2e\x32\x2c\x33\x39\x36\ +\x2e\x37\x39\x35\x20\x31\x31\x35\x2e\x32\x2c\x33\x32\x30\x20\x36\ +\x34\x2c\x33\x32\x30\x20\x36\x34\x2c\x34\x34\x38\x20\x31\x39\x32\ +\x2c\x34\x34\x38\x20\x31\x39\x32\x2c\x33\x39\x36\x2e\x37\x39\x35\ +\x20\x09\x22\x2f\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x73\ +\x76\x67\x3e\x0d\x0a\ +\x00\x00\x02\xf7\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\ +\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x6e\x65\x77\x20\x30\x20\x30\x20\ +\x35\x31\x32\x20\x35\x31\x32\x3b\x22\x20\x78\x6d\x6c\x3a\x73\x70\ +\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\ +\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x34\x31\x37\x2e\x34\ +\x2c\x32\x32\x34\x48\x32\x38\x38\x56\x39\x34\x2e\x36\x63\x30\x2d\ +\x31\x36\x2e\x39\x2d\x31\x34\x2e\x33\x2d\x33\x30\x2e\x36\x2d\x33\ +\x32\x2d\x33\x30\x2e\x36\x63\x2d\x31\x37\x2e\x37\x2c\x30\x2d\x33\ +\x32\x2c\x31\x33\x2e\x37\x2d\x33\x32\x2c\x33\x30\x2e\x36\x56\x32\ +\x32\x34\x48\x39\x34\x2e\x36\x43\x37\x37\x2e\x37\x2c\x32\x32\x34\ +\x2c\x36\x34\x2c\x32\x33\x38\x2e\x33\x2c\x36\x34\x2c\x32\x35\x36\ +\x0d\x0a\x09\x63\x30\x2c\x31\x37\x2e\x37\x2c\x31\x33\x2e\x37\x2c\ +\x33\x32\x2c\x33\x30\x2e\x36\x2c\x33\x32\x48\x32\x32\x34\x76\x31\ +\x32\x39\x2e\x34\x63\x30\x2c\x31\x36\x2e\x39\x2c\x31\x34\x2e\x33\ +\x2c\x33\x30\x2e\x36\x2c\x33\x32\x2c\x33\x30\x2e\x36\x63\x31\x37\ +\x2e\x37\x2c\x30\x2c\x33\x32\x2d\x31\x33\x2e\x37\x2c\x33\x32\x2d\ +\x33\x30\x2e\x36\x56\x32\x38\x38\x68\x31\x32\x39\x2e\x34\x63\x31\ +\x36\x2e\x39\x2c\x30\x2c\x33\x30\x2e\x36\x2d\x31\x34\x2e\x33\x2c\ +\x33\x30\x2e\x36\x2d\x33\x32\x0d\x0a\x09\x43\x34\x34\x38\x2c\x32\ +\x33\x38\x2e\x33\x2c\x34\x33\x34\x2e\x33\x2c\x32\x32\x34\x2c\x34\ +\x31\x37\x2e\x34\x2c\x32\x32\x34\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\ +\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\x52\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\ +\x64\x3d\x22\x4d\x34\x33\x37\x2e\x33\x33\x34\x2c\x31\x34\x34\x48\ +\x32\x35\x36\x2e\x30\x30\x36\x6c\x2d\x34\x32\x2e\x36\x36\x38\x2d\ +\x34\x38\x48\x37\x34\x2e\x36\x36\x36\x43\x35\x31\x2e\x31\x39\x37\ +\x2c\x39\x36\x2c\x33\x32\x2c\x31\x31\x35\x2e\x31\x39\x38\x2c\x33\ +\x32\x2c\x31\x33\x38\x2e\x36\x36\x37\x76\x32\x33\x34\x2e\x36\x36\ +\x36\x43\x33\x32\x2c\x33\x39\x36\x2e\x38\x30\x32\x2c\x35\x31\x2e\ +\x31\x39\x37\x2c\x34\x31\x36\x2c\x37\x34\x2e\x36\x36\x36\x2c\x34\ +\x31\x36\x68\x33\x36\x32\x2e\x36\x36\x38\x0d\x0a\x09\x43\x34\x36\ +\x30\x2e\x38\x30\x33\x2c\x34\x31\x36\x2c\x34\x38\x30\x2c\x33\x39\ +\x36\x2e\x38\x30\x32\x2c\x34\x38\x30\x2c\x33\x37\x33\x2e\x33\x33\ +\x33\x56\x31\x38\x36\x2e\x36\x36\x37\x43\x34\x38\x30\x2c\x31\x36\ +\x33\x2e\x31\x39\x38\x2c\x34\x36\x30\x2e\x38\x30\x33\x2c\x31\x34\ +\x34\x2c\x34\x33\x37\x2e\x33\x33\x34\x2c\x31\x34\x34\x7a\x20\x4d\ +\x34\x34\x38\x2c\x33\x37\x33\x2e\x33\x33\x33\x0d\x0a\x09\x63\x30\ +\x2c\x35\x2e\x37\x38\x32\x2d\x34\x2e\x38\x38\x35\x2c\x31\x30\x2e\ +\x36\x36\x37\x2d\x31\x30\x2e\x36\x36\x36\x2c\x31\x30\x2e\x36\x36\ +\x37\x48\x37\x34\x2e\x36\x36\x36\x43\x36\x38\x2e\x38\x38\x34\x2c\ +\x33\x38\x34\x2c\x36\x34\x2c\x33\x37\x39\x2e\x31\x31\x35\x2c\x36\ +\x34\x2c\x33\x37\x33\x2e\x33\x33\x33\x56\x31\x37\x36\x68\x33\x37\ +\x33\x2e\x33\x33\x34\x63\x35\x2e\x37\x38\x31\x2c\x30\x2c\x31\x30\ +\x2e\x36\x36\x36\x2c\x34\x2e\x38\x38\x35\x2c\x31\x30\x2e\x36\x36\ +\x36\x2c\x31\x30\x2e\x36\x36\x37\x0d\x0a\x09\x56\x33\x37\x33\x2e\ +\x33\x33\x33\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\ +\x0a\ +\x00\x00\x05\xca\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\ +\x64\x3d\x22\x4d\x34\x35\x30\x2e\x36\x37\x39\x2c\x32\x37\x33\x2e\ +\x35\x63\x2d\x31\x34\x2e\x35\x38\x35\x2d\x31\x34\x2e\x35\x37\x37\ +\x2d\x33\x36\x2e\x30\x35\x34\x2d\x31\x35\x2e\x38\x39\x2d\x35\x30\ +\x2e\x36\x33\x39\x2d\x31\x2e\x33\x31\x32\x6c\x2d\x34\x31\x2e\x36\ +\x38\x37\x2c\x34\x31\x2e\x36\x36\x34\x63\x2d\x31\x30\x2e\x38\x35\ +\x32\x2c\x31\x30\x2e\x38\x33\x36\x2d\x32\x33\x2e\x39\x33\x2c\x31\ +\x30\x2e\x38\x35\x39\x2d\x33\x31\x2e\x35\x36\x34\x2c\x31\x2e\x38\ +\x35\x32\x0d\x0a\x09\x63\x2d\x35\x2e\x30\x35\x37\x2d\x35\x2e\x39\ +\x36\x38\x2d\x33\x2e\x30\x36\x31\x2d\x32\x34\x2e\x33\x37\x34\x2d\ +\x31\x2e\x36\x34\x34\x2d\x33\x36\x2e\x30\x34\x39\x6c\x32\x30\x2e\ +\x39\x30\x37\x2d\x31\x37\x31\x2e\x38\x34\x39\x63\x31\x2e\x38\x36\ +\x37\x2d\x31\x35\x2e\x33\x35\x33\x2d\x39\x2e\x30\x37\x2d\x33\x30\ +\x2e\x31\x38\x35\x2d\x32\x34\x2e\x34\x33\x2d\x33\x32\x2e\x30\x35\ +\x31\x0d\x0a\x09\x63\x2d\x31\x35\x2e\x33\x35\x38\x2d\x31\x2e\x38\ +\x36\x37\x2d\x32\x39\x2e\x33\x32\x32\x2c\x39\x2e\x39\x33\x39\x2d\ +\x33\x31\x2e\x31\x39\x31\x2c\x32\x35\x2e\x32\x38\x39\x4c\x32\x36\ +\x37\x2e\x33\x37\x2c\x32\x33\x36\x2e\x30\x32\x31\x63\x2d\x31\x2e\ +\x32\x30\x35\x2c\x33\x2e\x33\x35\x38\x2d\x33\x2e\x37\x39\x2c\x33\ +\x2e\x39\x33\x38\x2d\x34\x2e\x30\x38\x31\x2d\x30\x2e\x35\x38\x32\ +\x4c\x32\x35\x35\x2e\x34\x34\x2c\x36\x30\x0d\x0a\x09\x63\x30\x2d\ +\x31\x35\x2e\x34\x36\x35\x2d\x31\x32\x2e\x35\x34\x32\x2d\x32\x38\ +\x2d\x32\x38\x2e\x30\x31\x34\x2d\x32\x38\x63\x2d\x31\x35\x2e\x34\ +\x37\x33\x2c\x30\x2d\x32\x38\x2e\x30\x31\x35\x2c\x31\x32\x2e\x35\ +\x33\x35\x2d\x32\x38\x2e\x30\x31\x35\x2c\x32\x38\x6c\x2d\x30\x2e\ +\x35\x35\x32\x2c\x31\x37\x36\x2e\x37\x35\x32\x63\x30\x2e\x31\x34\ +\x36\x2c\x32\x2e\x30\x34\x2d\x31\x2e\x36\x30\x34\x2c\x32\x2e\x36\ +\x32\x34\x2d\x31\x2e\x39\x32\x2c\x30\x2e\x32\x39\x34\x4c\x31\x37\ +\x32\x2e\x30\x31\x36\x2c\x39\x39\x2e\x30\x37\x37\x0d\x0a\x09\x63\ +\x2d\x32\x2e\x37\x35\x2d\x31\x35\x2e\x32\x31\x39\x2d\x31\x37\x2e\ +\x33\x32\x33\x2d\x32\x36\x2e\x32\x30\x33\x2d\x33\x32\x2e\x35\x34\ +\x38\x2d\x32\x33\x2e\x34\x35\x33\x63\x2d\x31\x35\x2e\x32\x32\x37\ +\x2c\x32\x2e\x37\x34\x38\x2d\x32\x35\x2e\x33\x33\x39\x2c\x31\x38\ +\x2e\x31\x38\x37\x2d\x32\x32\x2e\x35\x39\x31\x2c\x33\x33\x2e\x34\ +\x30\x33\x6c\x32\x32\x2e\x31\x39\x33\x2c\x31\x36\x31\x2e\x34\x35\ +\x35\x0d\x0a\x09\x63\x30\x2e\x30\x32\x33\x2c\x32\x2e\x38\x37\x32\ +\x2d\x30\x2e\x39\x34\x31\x2c\x34\x2e\x35\x31\x33\x2d\x32\x2e\x33\ +\x30\x38\x2c\x30\x2e\x38\x33\x31\x6c\x2d\x33\x33\x2e\x31\x30\x39\ +\x2d\x38\x38\x2e\x35\x31\x37\x63\x2d\x35\x2e\x31\x38\x2d\x31\x34\ +\x2e\x35\x37\x32\x2d\x32\x31\x2e\x31\x39\x36\x2d\x32\x33\x2e\x30\ +\x36\x35\x2d\x33\x35\x2e\x37\x37\x36\x2d\x31\x37\x2e\x38\x38\x39\ +\x0d\x0a\x09\x63\x2d\x31\x34\x2e\x35\x37\x39\x2c\x35\x2e\x31\x37\ +\x37\x2d\x32\x32\x2e\x32\x30\x31\x2c\x32\x32\x2e\x30\x36\x31\x2d\ +\x31\x37\x2e\x30\x32\x33\x2c\x33\x36\x2e\x36\x33\x31\x6c\x35\x38\ +\x2e\x30\x34\x32\x2c\x31\x38\x39\x2e\x36\x32\x35\x63\x30\x2e\x33\ +\x30\x33\x2c\x31\x2e\x30\x34\x36\x2c\x30\x2e\x36\x32\x34\x2c\x32\ +\x2e\x30\x38\x35\x2c\x30\x2e\x39\x35\x33\x2c\x33\x2e\x31\x31\x38\ +\x6c\x30\x2e\x31\x32\x31\x2c\x30\x2e\x33\x39\x0d\x0a\x09\x63\x30\ +\x2e\x30\x31\x31\x2c\x30\x2e\x30\x33\x31\x2c\x30\x2e\x30\x32\x35\ +\x2c\x30\x2e\x30\x35\x38\x2c\x30\x2e\x30\x33\x35\x2c\x30\x2e\x30\ +\x38\x38\x43\x31\x32\x36\x2e\x30\x37\x39\x2c\x34\x34\x34\x2e\x32\ +\x33\x33\x2c\x31\x37\x32\x2e\x35\x37\x2c\x34\x38\x30\x2c\x32\x32\ +\x37\x2e\x34\x32\x37\x2c\x34\x38\x30\x63\x33\x35\x2e\x31\x31\x36\ +\x2c\x30\x2c\x37\x31\x2e\x35\x39\x31\x2d\x31\x32\x2e\x33\x37\x38\ +\x2c\x39\x39\x2e\x33\x35\x37\x2d\x33\x33\x2e\x36\x37\x32\x0d\x0a\ +\x09\x63\x30\x2e\x30\x30\x31\x2c\x30\x2c\x30\x2e\x30\x30\x33\x2d\ +\x30\x2e\x30\x30\x32\x2c\x30\x2e\x30\x30\x33\x2d\x30\x2e\x30\x30\ +\x32\x63\x32\x39\x2e\x39\x39\x2d\x31\x38\x2e\x30\x35\x31\x2c\x31\ +\x32\x36\x2e\x30\x37\x31\x2d\x31\x32\x31\x2e\x33\x34\x37\x2c\x31\ +\x32\x36\x2e\x30\x37\x31\x2d\x31\x32\x31\x2e\x33\x34\x37\x43\x34\ +\x36\x37\x2e\x34\x34\x35\x2c\x33\x31\x30\x2e\x34\x30\x32\x2c\x34\ +\x36\x35\x2e\x32\x36\x36\x2c\x32\x38\x38\x2e\x30\x38\x2c\x34\x35\ +\x30\x2e\x36\x37\x39\x2c\x32\x37\x33\x2e\x35\x7a\x22\x2f\x3e\x0d\ +\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x04\xbd\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x32\x34\x70\x74\x27\x20\x76\x65\x72\x73\x69\x6f\x6e\ +\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x27\ +\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\x38\x33\x20\ +\x32\x34\x20\x32\x34\x27\x20\x77\x69\x64\x74\x68\x3d\x27\x32\x34\ +\x70\x74\x27\x20\x78\x6d\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x27\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\ +\x6e\x6b\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\ +\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\ +\x27\x3e\x0a\x3c\x67\x20\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\ +\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\ +\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\ +\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\ +\x37\x36\x35\x35\x20\x36\x39\x2e\x37\x36\x38\x38\x29\x27\x3e\x0a\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\x20\x2d\x39\x2e\x33\x39\ +\x31\x31\x38\x20\x2d\x30\x43\x20\x2d\x39\x2e\x39\x36\x38\x36\x31\ +\x20\x2d\x33\x2e\x38\x33\x30\x30\x34\x20\x2d\x38\x2e\x35\x35\x37\ +\x30\x36\x20\x2d\x37\x2e\x35\x31\x39\x34\x34\x20\x2d\x36\x2e\x36\ +\x34\x30\x35\x37\x20\x2d\x31\x30\x2e\x32\x31\x37\x31\x43\x20\x2d\ +\x30\x2e\x34\x39\x31\x30\x38\x39\x20\x2d\x31\x38\x2e\x38\x37\x33\ +\x33\x20\x38\x2e\x33\x35\x36\x37\x36\x20\x2d\x31\x35\x2e\x31\x39\ +\x30\x36\x20\x38\x2e\x36\x37\x36\x33\x32\x20\x2d\x35\x2e\x35\x32\ +\x39\x34\x38\x43\x20\x38\x2e\x38\x33\x36\x20\x2d\x30\x2e\x37\x30\ +\x32\x30\x30\x38\x20\x36\x2e\x32\x33\x39\x36\x38\x20\x32\x2e\x39\ +\x34\x32\x30\x37\x20\x33\x2e\x33\x32\x30\x32\x38\x20\x35\x2e\x31\ +\x30\x38\x35\x37\x43\x20\x2d\x31\x2e\x39\x36\x38\x39\x31\x20\x39\ +\x2e\x30\x33\x33\x37\x31\x20\x2d\x38\x2e\x32\x36\x38\x39\x35\x20\ +\x37\x2e\x34\x34\x33\x37\x20\x2d\x39\x2e\x33\x39\x31\x31\x38\x20\ +\x2d\x30\x5a\x27\x20\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\x27\ +\x20\x73\x74\x72\x6f\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\x30\ +\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\ +\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\x27\ +\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\ +\x69\x74\x3d\x27\x31\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\x72\ +\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3d\x27\x31\x2e\x37\x35\x36\ +\x35\x36\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\ +\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\ +\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\ +\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\x20\x36\x39\ +\x2e\x37\x36\x38\x38\x29\x27\x3e\x0a\x3c\x63\x69\x72\x63\x6c\x65\ +\x20\x63\x78\x3d\x27\x2d\x39\x2e\x33\x39\x31\x31\x38\x27\x20\x63\ +\x79\x3d\x27\x2d\x30\x27\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\ +\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\x30\ +\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\ +\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\ +\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\ +\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\x20\x36\x39\x2e\x37\ +\x36\x38\x38\x29\x27\x3e\x0a\x3c\x63\x69\x72\x63\x6c\x65\x20\x63\ +\x78\x3d\x27\x2d\x36\x2e\x36\x34\x30\x35\x37\x27\x20\x63\x79\x3d\ +\x27\x2d\x31\x30\x2e\x32\x31\x37\x31\x27\x20\x72\x3d\x27\x33\x2e\ +\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\x30\ +\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\ +\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\ +\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\ +\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\x20\ +\x36\x39\x2e\x37\x36\x38\x38\x29\x27\x3e\x0a\x3c\x63\x69\x72\x63\ +\x6c\x65\x20\x63\x78\x3d\x27\x38\x2e\x36\x37\x36\x33\x32\x27\x20\ +\x63\x79\x3d\x27\x2d\x35\x2e\x35\x32\x39\x34\x38\x27\x20\x72\x3d\ +\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\ +\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\ +\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\ +\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\ +\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\ +\x35\x35\x20\x36\x39\x2e\x37\x36\x38\x38\x29\x27\x3e\x0a\x3c\x63\ +\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x33\x2e\x33\x32\x30\x32\ +\x38\x27\x20\x63\x79\x3d\x27\x35\x2e\x31\x30\x38\x35\x37\x27\x20\ +\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\ +\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\ +\x3e\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x04\xca\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\x72\ +\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\x75\ +\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x39\x2e\x32\x2e\x31\x2c\x20\ +\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\x2d\ +\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\x6e\ +\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\x20\ +\x20\x2d\x2d\x3e\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x73\ +\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\ +\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\x2f\ +\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\ +\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\x2f\ +\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\x31\ +\x31\x2e\x64\x74\x64\x22\x3e\x0a\x3c\x73\x76\x67\x20\x76\x65\x72\ +\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x0a\x09\x20\x69\x64\x3d\ +\x22\x73\x76\x67\x34\x36\x31\x39\x22\x20\x69\x6e\x6b\x73\x63\x61\ +\x70\x65\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\x2e\x39\x31\ +\x2b\x64\x65\x76\x65\x6c\x2b\x6f\x73\x78\x6d\x65\x6e\x75\x20\x72\ +\x31\x32\x39\x31\x31\x22\x20\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x64\x6f\x63\x6e\x61\x6d\x65\x3d\x22\x74\x72\x69\x61\x6e\x67\x6c\ +\x65\x2d\x73\x74\x72\x6f\x6b\x65\x64\x2d\x31\x35\x2e\x73\x76\x67\ +\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x63\x72\x65\x61\x74\x69\x76\x65\x63\x6f\x6d\x6d\x6f\ +\x6e\x73\x2e\x6f\x72\x67\x2f\x6e\x73\x23\x22\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x64\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\ +\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\x6e\x74\ +\x73\x2f\x31\x2e\x31\x2f\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\ +\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x2f\ +\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x73\x2f\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\ +\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\x73\x23\x22\x20\x78\x6d\x6c\ +\x6e\x73\x3a\x73\x6f\x64\x69\x70\x6f\x64\x69\x3d\x22\x68\x74\x74\ +\x70\x3a\x2f\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2e\x73\x6f\x75\ +\x72\x63\x65\x66\x6f\x72\x67\x65\x2e\x6e\x65\x74\x2f\x44\x54\x44\ +\x2f\x73\x6f\x64\x69\x70\x6f\x64\x69\x2d\x30\x2e\x64\x74\x64\x22\ +\x20\x78\x6d\x6c\x6e\x73\x3a\x73\x76\x67\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\ +\x30\x30\x2f\x73\x76\x67\x22\x0a\x09\x20\x78\x6d\x6c\x6e\x73\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\ +\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\ +\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\ +\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\ +\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\x20\ +\x79\x3d\x22\x30\x70\x78\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x31\ +\x35\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x31\x35\x70\ +\x78\x22\x0a\x09\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\x20\ +\x30\x20\x31\x35\x20\x31\x35\x22\x20\x73\x74\x79\x6c\x65\x3d\x22\ +\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\ +\x64\x3a\x6e\x65\x77\x20\x30\x20\x30\x20\x31\x35\x20\x31\x35\x3b\ +\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\ +\x73\x65\x72\x76\x65\x22\x3e\x0a\x3c\x70\x61\x74\x68\x20\x69\x64\ +\x3d\x22\x72\x65\x63\x74\x33\x33\x33\x38\x22\x20\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x3a\x63\x6f\x6e\x6e\x65\x63\x74\x6f\x72\x2d\x63\ +\x75\x72\x76\x61\x74\x75\x72\x65\x3d\x22\x30\x22\x20\x73\x6f\x64\ +\x69\x70\x6f\x64\x69\x3a\x6e\x6f\x64\x65\x74\x79\x70\x65\x73\x3d\ +\x22\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x63\x22\x20\x64\ +\x3d\x22\x4d\x37\x2e\x35\x32\x34\x33\x2c\x31\x2e\x35\x30\x30\x34\ +\x0a\x09\x43\x37\x2e\x32\x34\x32\x39\x2c\x31\x2e\x34\x39\x31\x33\ +\x2c\x36\x2e\x39\x37\x38\x37\x2c\x31\x2e\x36\x34\x32\x33\x2c\x36\ +\x2e\x38\x33\x33\x36\x2c\x31\x2e\x38\x39\x35\x32\x6c\x2d\x35\x2e\ +\x35\x2c\x39\x2e\x38\x36\x39\x32\x43\x31\x2e\x30\x32\x31\x38\x2c\ +\x31\x32\x2e\x33\x30\x37\x38\x2c\x31\x2e\x33\x39\x35\x2c\x31\x32\ +\x2e\x39\x39\x39\x39\x2c\x32\x2c\x31\x33\x68\x31\x31\x0a\x09\x63\ +\x30\x2e\x36\x30\x35\x2d\x30\x2e\x30\x30\x30\x31\x2c\x30\x2e\x39\ +\x37\x38\x32\x2d\x30\x2e\x36\x39\x32\x32\x2c\x30\x2e\x36\x36\x36\ +\x34\x2d\x31\x2e\x32\x33\x35\x35\x6c\x2d\x35\x2e\x35\x2d\x39\x2e\ +\x38\x36\x39\x32\x43\x38\x2e\x30\x33\x30\x32\x2c\x31\x2e\x36\x35\ +\x37\x39\x2c\x37\x2e\x37\x38\x38\x34\x2c\x31\x2e\x35\x30\x39\x32\ +\x2c\x37\x2e\x35\x32\x34\x33\x2c\x31\x2e\x35\x30\x30\x34\x7a\x20\ +\x4d\x37\x2e\x35\x2c\x33\x2e\x38\x39\x39\x33\x6c\x34\x2e\x31\x32\ +\x36\x37\x2c\x37\x2e\x34\x37\x30\x34\x0a\x09\x48\x33\x2e\x33\x37\ +\x33\x33\x4c\x37\x2e\x35\x2c\x33\x2e\x38\x39\x39\x33\x7a\x22\x2f\ +\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ +\x00\x00\x04\x8d\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x32\x34\x70\x74\x27\x20\x76\x65\x72\x73\x69\x6f\x6e\ +\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x27\ +\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\x38\x33\x20\ +\x32\x34\x20\x32\x34\x27\x20\x77\x69\x64\x74\x68\x3d\x27\x32\x34\ +\x70\x74\x27\x20\x78\x6d\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x27\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\ +\x6e\x6b\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\ +\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\ +\x27\x3e\x0a\x3c\x67\x20\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\ +\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\ +\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\ +\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\ +\x37\x36\x35\x35\x20\x36\x39\x2e\x38\x31\x32\x33\x29\x27\x3e\x0a\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\x20\x2d\x39\x2e\x33\x39\ +\x31\x31\x38\x20\x2d\x30\x43\x20\x2d\x39\x2e\x33\x32\x32\x35\x37\ +\x20\x2d\x33\x2e\x37\x37\x37\x38\x33\x20\x2d\x38\x2e\x33\x34\x33\ +\x32\x34\x20\x2d\x37\x2e\x33\x38\x34\x34\x34\x20\x2d\x36\x2e\x36\ +\x34\x30\x35\x37\x20\x2d\x31\x30\x2e\x31\x32\x39\x38\x43\x20\x2d\ +\x31\x2e\x32\x36\x32\x31\x39\x20\x2d\x31\x38\x2e\x38\x30\x31\x37\ +\x20\x37\x2e\x36\x38\x39\x37\x31\x20\x2d\x31\x35\x2e\x35\x33\x35\ +\x31\x20\x38\x2e\x36\x37\x36\x33\x32\x20\x2d\x35\x2e\x34\x38\x32\ +\x32\x43\x20\x39\x2e\x31\x38\x36\x37\x39\x20\x2d\x30\x2e\x32\x38\ +\x30\x38\x37\x37\x20\x36\x2e\x37\x34\x39\x33\x37\x20\x34\x2e\x35\ +\x31\x38\x38\x37\x20\x33\x2e\x33\x32\x30\x32\x38\x20\x35\x2e\x30\ +\x36\x34\x38\x39\x27\x20\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\ +\x27\x20\x73\x74\x72\x6f\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\ +\x30\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\ +\x70\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\ +\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\ +\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\ +\x6d\x69\x74\x3d\x27\x31\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3d\x27\x31\x2e\x37\x35\ +\x36\x35\x36\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\ +\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\ +\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\ +\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\x20\x36\ +\x39\x2e\x38\x31\x32\x33\x29\x27\x3e\x0a\x3c\x63\x69\x72\x63\x6c\ +\x65\x20\x63\x78\x3d\x27\x2d\x39\x2e\x33\x39\x31\x31\x38\x27\x20\ +\x63\x79\x3d\x27\x2d\x30\x27\x20\x72\x3d\x27\x33\x2e\x30\x31\x31\ +\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\ +\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\ +\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\ +\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\ +\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\x20\x36\x39\x2e\ +\x38\x31\x32\x33\x29\x27\x3e\x0a\x3c\x63\x69\x72\x63\x6c\x65\x20\ +\x63\x78\x3d\x27\x2d\x36\x2e\x36\x34\x30\x35\x37\x27\x20\x63\x79\ +\x3d\x27\x2d\x31\x30\x2e\x31\x32\x39\x38\x27\x20\x72\x3d\x27\x33\ +\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\ +\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\ +\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\ +\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\ +\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\x35\x35\ +\x20\x36\x39\x2e\x38\x31\x32\x33\x29\x27\x3e\x0a\x3c\x63\x69\x72\ +\x63\x6c\x65\x20\x63\x78\x3d\x27\x38\x2e\x36\x37\x36\x33\x32\x27\ +\x20\x63\x79\x3d\x27\x2d\x35\x2e\x34\x38\x32\x32\x27\x20\x72\x3d\ +\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\x3d\x27\ +\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\ +\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\ +\x74\x72\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\ +\x30\x20\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x38\x2e\x37\x36\ +\x35\x35\x20\x36\x39\x2e\x38\x31\x32\x33\x29\x27\x3e\x0a\x3c\x63\ +\x69\x72\x63\x6c\x65\x20\x63\x78\x3d\x27\x33\x2e\x33\x32\x30\x32\ +\x38\x27\x20\x63\x79\x3d\x27\x35\x2e\x30\x36\x34\x38\x39\x27\x20\ +\x72\x3d\x27\x33\x2e\x30\x31\x31\x32\x35\x27\x20\x66\x69\x6c\x6c\ +\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\ +\x3e\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x03\xeb\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x73\ +\x74\x79\x6c\x65\x3d\x22\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\ +\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x6e\x65\x77\x20\x30\x20\x30\x20\ +\x35\x31\x32\x20\x35\x31\x32\x3b\x22\x20\x78\x6d\x6c\x3a\x73\x70\ +\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\ +\x0a\x3c\x67\x3e\x0d\x0a\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\ +\x4d\x32\x35\x36\x2c\x31\x32\x38\x63\x2d\x38\x31\x2e\x39\x2c\x30\ +\x2d\x31\x34\x35\x2e\x37\x2c\x34\x38\x2e\x38\x2d\x32\x32\x34\x2c\ +\x31\x32\x38\x63\x36\x37\x2e\x34\x2c\x36\x37\x2e\x37\x2c\x31\x32\ +\x34\x2c\x31\x32\x38\x2c\x32\x32\x34\x2c\x31\x32\x38\x63\x39\x39\ +\x2e\x39\x2c\x30\x2c\x31\x37\x33\x2e\x34\x2d\x37\x36\x2e\x34\x2c\ +\x32\x32\x34\x2d\x31\x32\x36\x2e\x36\x0d\x0a\x09\x09\x43\x34\x32\ +\x38\x2e\x32\x2c\x31\x39\x38\x2e\x36\x2c\x33\x35\x34\x2e\x38\x2c\ +\x31\x32\x38\x2c\x32\x35\x36\x2c\x31\x32\x38\x7a\x20\x4d\x32\x35\ +\x36\x2c\x33\x34\x37\x2e\x33\x63\x2d\x34\x39\x2e\x34\x2c\x30\x2d\ +\x38\x39\x2e\x36\x2d\x34\x31\x2d\x38\x39\x2e\x36\x2d\x39\x31\x2e\ +\x33\x63\x30\x2d\x35\x30\x2e\x34\x2c\x34\x30\x2e\x32\x2d\x39\x31\ +\x2e\x33\x2c\x38\x39\x2e\x36\x2d\x39\x31\x2e\x33\x73\x38\x39\x2e\ +\x36\x2c\x34\x31\x2c\x38\x39\x2e\x36\x2c\x39\x31\x2e\x33\x0d\x0a\ +\x09\x09\x43\x33\x34\x35\x2e\x36\x2c\x33\x30\x36\x2e\x34\x2c\x33\ +\x30\x35\x2e\x34\x2c\x33\x34\x37\x2e\x33\x2c\x32\x35\x36\x2c\x33\ +\x34\x37\x2e\x33\x7a\x22\x2f\x3e\x0d\x0a\x09\x3c\x67\x3e\x0d\x0a\ +\x09\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x32\x35\x36\x2c\ +\x32\x32\x34\x63\x30\x2d\x37\x2e\x39\x2c\x32\x2e\x39\x2d\x31\x35\ +\x2e\x31\x2c\x37\x2e\x36\x2d\x32\x30\x2e\x37\x63\x2d\x32\x2e\x35\ +\x2d\x30\x2e\x34\x2d\x35\x2d\x30\x2e\x36\x2d\x37\x2e\x36\x2d\x30\ +\x2e\x36\x63\x2d\x32\x38\x2e\x38\x2c\x30\x2d\x35\x32\x2e\x33\x2c\ +\x32\x33\x2e\x39\x2d\x35\x32\x2e\x33\x2c\x35\x33\x2e\x33\x63\x30\ +\x2c\x32\x39\x2e\x34\x2c\x32\x33\x2e\x35\x2c\x35\x33\x2e\x33\x2c\ +\x35\x32\x2e\x33\x2c\x35\x33\x2e\x33\x0d\x0a\x09\x09\x09\x73\x35\ +\x32\x2e\x33\x2d\x32\x33\x2e\x39\x2c\x35\x32\x2e\x33\x2d\x35\x33\ +\x2e\x33\x63\x30\x2d\x32\x2e\x33\x2d\x30\x2e\x32\x2d\x34\x2e\x36\ +\x2d\x30\x2e\x34\x2d\x36\x2e\x39\x63\x2d\x35\x2e\x35\x2c\x34\x2e\ +\x33\x2d\x31\x32\x2e\x33\x2c\x36\x2e\x39\x2d\x31\x39\x2e\x38\x2c\ +\x36\x2e\x39\x43\x32\x37\x30\x2e\x33\x2c\x32\x35\x36\x2c\x32\x35\ +\x36\x2c\x32\x34\x31\x2e\x37\x2c\x32\x35\x36\x2c\x32\x32\x34\x7a\ +\x22\x2f\x3e\x0d\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\x3e\ +\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\x6c\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x3e\x0d\x0a\x09\ +\x0d\x0a\x09\x09\x3c\x72\x65\x63\x74\x20\x78\x3d\x22\x31\x37\x38\ +\x2e\x38\x34\x36\x22\x20\x79\x3d\x22\x39\x32\x2e\x30\x38\x37\x22\ +\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\ +\x69\x78\x28\x2d\x30\x2e\x37\x30\x37\x31\x20\x2d\x30\x2e\x37\x30\ +\x37\x31\x20\x30\x2e\x37\x30\x37\x31\x20\x2d\x30\x2e\x37\x30\x37\ +\x31\x20\x32\x32\x34\x2e\x33\x34\x37\x36\x20\x36\x33\x31\x2e\x31\ +\x34\x39\x38\x29\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x31\x32\x38\ +\x2e\x30\x38\x35\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x33\x35\ +\x34\x2e\x30\x34\x39\x22\x2f\x3e\x0d\x0a\x09\x3c\x70\x61\x74\x68\ +\x20\x64\x3d\x22\x4d\x34\x37\x31\x2e\x37\x32\x33\x2c\x38\x38\x2e\ +\x33\x39\x33\x6c\x2d\x34\x38\x2e\x31\x31\x35\x2d\x34\x38\x2e\x31\ +\x31\x34\x63\x2d\x31\x31\x2e\x37\x32\x33\x2d\x31\x31\x2e\x37\x32\ +\x34\x2d\x33\x31\x2e\x35\x35\x38\x2d\x31\x30\x2e\x38\x39\x36\x2d\ +\x34\x34\x2e\x33\x30\x34\x2c\x31\x2e\x38\x35\x6c\x2d\x34\x35\x2e\ +\x32\x30\x32\x2c\x34\x35\x2e\x32\x30\x33\x6c\x39\x30\x2e\x35\x36\ +\x39\x2c\x39\x30\x2e\x35\x36\x38\x6c\x34\x35\x2e\x32\x30\x32\x2d\ +\x34\x35\x2e\x32\x30\x32\x0d\x0a\x09\x09\x43\x34\x38\x32\x2e\x36\ +\x31\x36\x2c\x31\x31\x39\x2e\x39\x35\x32\x2c\x34\x38\x33\x2e\x34\ +\x34\x35\x2c\x31\x30\x30\x2e\x31\x31\x36\x2c\x34\x37\x31\x2e\x37\ +\x32\x33\x2c\x38\x38\x2e\x33\x39\x33\x7a\x22\x2f\x3e\x0d\x0a\x09\ +\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\x6f\x69\x6e\x74\x73\x3d\ +\x22\x36\x34\x2e\x30\x32\x31\x2c\x33\x36\x33\x2e\x32\x35\x32\x20\ +\x33\x32\x2c\x34\x38\x30\x20\x31\x34\x38\x2e\x37\x33\x37\x2c\x34\ +\x34\x37\x2e\x39\x37\x39\x20\x09\x22\x2f\x3e\x0d\x0a\x3c\x2f\x67\ +\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x02\x7d\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x38\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\x3e\ +\x0d\x0a\x09\x09\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x34\x32\ +\x37\x2c\x32\x33\x34\x2e\x36\x32\x35\x48\x31\x36\x37\x2e\x32\x39\ +\x36\x6c\x31\x31\x39\x2e\x37\x30\x32\x2d\x31\x31\x39\x2e\x37\x30\ +\x32\x4c\x32\x35\x36\x2c\x38\x35\x4c\x38\x35\x2c\x32\x35\x36\x6c\ +\x31\x37\x31\x2c\x31\x37\x31\x6c\x32\x39\x2e\x39\x32\x32\x2d\x32\ +\x39\x2e\x39\x32\x34\x4c\x31\x36\x37\x2e\x32\x39\x36\x2c\x32\x37\ +\x37\x2e\x33\x37\x35\x48\x34\x32\x37\x56\x32\x33\x34\x2e\x36\x32\ +\x35\x7a\x22\x2f\x3e\x0d\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\ +\x67\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\xc4\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x43\x68\x65\x76\x72\x6f\x6e\x5f\x63\x69\x72\x63\x6c\x65\ +\x64\x5f\x6c\x65\x66\x74\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\ +\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\ +\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\ +\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\ +\x6c\x69\x6e\x6b\x22\x0d\x0a\x09\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\x6e\x61\ +\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3d\x22\ +\x6e\x65\x77\x20\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x78\ +\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\ +\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\ +\x31\x31\x2e\x33\x30\x32\x2c\x36\x2e\x37\x37\x36\x63\x2d\x30\x2e\ +\x31\x39\x36\x2d\x30\x2e\x31\x39\x37\x2d\x30\x2e\x35\x31\x35\x2d\ +\x30\x2e\x31\x39\x37\x2d\x30\x2e\x37\x31\x2c\x30\x4c\x37\x2e\x37\ +\x38\x35\x2c\x39\x2e\x36\x34\x31\x63\x2d\x30\x2e\x31\x39\x36\x2c\ +\x30\x2e\x31\x39\x39\x2d\x30\x2e\x31\x39\x36\x2c\x30\x2e\x35\x32\ +\x2c\x30\x2c\x30\x2e\x37\x31\x37\x6c\x32\x2e\x38\x30\x37\x2c\x32\ +\x2e\x38\x36\x34\x0d\x0a\x09\x63\x30\x2e\x31\x39\x35\x2c\x30\x2e\ +\x31\x39\x39\x2c\x30\x2e\x35\x31\x34\x2c\x30\x2e\x31\x39\x38\x2c\ +\x30\x2e\x37\x31\x2c\x30\x63\x30\x2e\x31\x39\x36\x2d\x30\x2e\x31\ +\x39\x37\x2c\x30\x2e\x31\x39\x36\x2d\x30\x2e\x35\x31\x38\x2c\x30\ +\x2d\x30\x2e\x37\x31\x37\x4c\x39\x2c\x31\x30\x6c\x32\x2e\x33\x30\ +\x32\x2d\x32\x2e\x35\x30\x36\x43\x31\x31\x2e\x34\x39\x38\x2c\x37\ +\x2e\x32\x39\x36\x2c\x31\x31\x2e\x34\x39\x38\x2c\x36\x2e\x39\x37\ +\x36\x2c\x31\x31\x2e\x33\x30\x32\x2c\x36\x2e\x37\x37\x36\x7a\x0d\ +\x0a\x09\x20\x4d\x31\x30\x2c\x30\x2e\x34\x63\x2d\x35\x2e\x33\x30\ +\x32\x2c\x30\x2d\x39\x2e\x36\x2c\x34\x2e\x32\x39\x38\x2d\x39\x2e\ +\x36\x2c\x39\x2e\x36\x63\x30\x2c\x35\x2e\x33\x30\x33\x2c\x34\x2e\ +\x32\x39\x38\x2c\x39\x2e\x36\x2c\x39\x2e\x36\x2c\x39\x2e\x36\x73\ +\x39\x2e\x36\x2d\x34\x2e\x32\x39\x37\x2c\x39\x2e\x36\x2d\x39\x2e\ +\x36\x43\x31\x39\x2e\x36\x2c\x34\x2e\x36\x39\x38\x2c\x31\x35\x2e\ +\x33\x30\x32\x2c\x30\x2e\x34\x2c\x31\x30\x2c\x30\x2e\x34\x7a\x20\ +\x4d\x31\x30\x2c\x31\x38\x2e\x33\x35\x34\x0d\x0a\x09\x63\x2d\x34\ +\x2e\x36\x31\x35\x2c\x30\x2d\x38\x2e\x33\x35\x34\x2d\x33\x2e\x37\ +\x34\x2d\x38\x2e\x33\x35\x34\x2d\x38\x2e\x33\x35\x34\x63\x30\x2d\ +\x34\x2e\x36\x31\x34\x2c\x33\x2e\x37\x33\x39\x2d\x38\x2e\x33\x35\ +\x34\x2c\x38\x2e\x33\x35\x34\x2d\x38\x2e\x33\x35\x34\x63\x34\x2e\ +\x36\x31\x33\x2c\x30\x2c\x38\x2e\x33\x35\x34\x2c\x33\x2e\x37\x34\ +\x2c\x38\x2e\x33\x35\x34\x2c\x38\x2e\x33\x35\x34\x0d\x0a\x09\x43\ +\x31\x38\x2e\x33\x35\x34\x2c\x31\x34\x2e\x36\x31\x34\x2c\x31\x34\ +\x2e\x36\x31\x33\x2c\x31\x38\x2e\x33\x35\x34\x2c\x31\x30\x2c\x31\ +\x38\x2e\x33\x35\x34\x7a\x22\x2f\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\ +\x3e\x0d\x0a\ +\x00\x00\x00\x6c\ +\x3c\ +\x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\ +\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x3e\x3c\x70\x61\x74\x68\ +\x20\x64\x3d\x22\x4d\x31\x38\x20\x31\x32\x76\x31\x48\x38\x76\x35\ +\x6c\x2d\x36\x2d\x36\x20\x36\x2d\x36\x76\x35\x68\x38\x56\x32\x68\ +\x32\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x02\xb7\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x36\x2e\x32\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x4c\x61\x79\x65\x72\x5f\x31\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ +\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ +\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\ +\x20\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x77\x69\x64\x74\ +\x68\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x68\x65\x69\x67\x68\x74\ +\x3d\x22\x35\x31\x32\x70\x78\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\x32\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x35\x31\x32\x20\x35\x31\ +\x32\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\ +\x65\x73\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x67\x20\x69\x64\x3d\ +\x22\x49\x63\x6f\x6e\x5f\x35\x5f\x22\x3e\x0d\x0a\x09\x3c\x67\x3e\ +\x0d\x0a\x09\x09\x3c\x70\x6f\x6c\x79\x67\x6f\x6e\x20\x70\x6f\x69\ +\x6e\x74\x73\x3d\x22\x34\x30\x35\x2c\x31\x33\x36\x2e\x37\x39\x38\ +\x20\x33\x37\x35\x2e\x32\x30\x32\x2c\x31\x30\x37\x20\x32\x35\x36\ +\x2c\x32\x32\x36\x2e\x32\x30\x32\x20\x31\x33\x36\x2e\x37\x39\x38\ +\x2c\x31\x30\x37\x20\x31\x30\x37\x2c\x31\x33\x36\x2e\x37\x39\x38\ +\x20\x32\x32\x36\x2e\x32\x30\x32\x2c\x32\x35\x36\x20\x31\x30\x37\ +\x2c\x33\x37\x35\x2e\x32\x30\x32\x20\x31\x33\x36\x2e\x37\x39\x38\ +\x2c\x34\x30\x35\x20\x32\x35\x36\x2c\x32\x38\x35\x2e\x37\x39\x38\ +\x20\x0d\x0a\x09\x09\x09\x33\x37\x35\x2e\x32\x30\x32\x2c\x34\x30\ +\x35\x20\x34\x30\x35\x2c\x33\x37\x35\x2e\x32\x30\x32\x20\x32\x38\ +\x35\x2e\x37\x39\x38\x2c\x32\x35\x36\x20\x09\x09\x22\x2f\x3e\x0d\ +\x0a\x09\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\x67\x3e\x0d\x0a\x3c\x2f\ +\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x01\xdd\ +\x00\ +\x00\x06\xe4\x78\x9c\xcd\x54\x4d\x6f\x9b\x40\x10\x3d\x13\x29\xff\ +\x61\xba\xb7\x4a\xec\x17\xc4\xd4\xa6\xa6\x51\xfd\x21\xcb\x52\x9a\ +\x58\x6a\xea\xaa\xa7\x0a\xc3\x16\x50\x28\x20\x58\xc0\xce\xaf\xef\ +\xee\x92\x54\x51\x95\xfa\x18\xfb\xb0\x33\xda\x79\x6f\xde\xbc\x65\ +\x24\xa6\xd7\xfb\xdf\x39\x74\xa2\x6e\xb2\xb2\x08\x10\x27\x0c\x81\ +\x28\xa2\x32\xce\x8a\x24\x40\xad\xfc\x85\xc7\xe8\xfa\xd3\xe5\xc5\ +\xf4\x1d\xc6\xb0\x12\x85\xa8\x43\x59\xd6\x3e\x7c\x8e\xcb\x9d\x80\ +\x75\x9e\xb7\x8d\x34\x25\xe0\x1e\x71\x08\xb7\xe1\xeb\x76\x05\xcb\ +\x7d\x55\xd6\x12\x36\x79\x9b\xe0\x75\x01\xc4\x14\xb7\xc3\x10\x1f\ +\x3c\xc2\x18\xcc\xda\x2c\x8f\x81\xbd\x07\xc0\xd8\xe8\x2f\xee\xe6\ +\xf7\x3f\x36\x4b\x68\xba\x04\x36\xdf\x66\x37\xeb\x39\x20\x4c\xe9\ +\x77\x77\x4e\xe9\xe2\x7e\x61\x24\x38\xe1\x94\x2e\x6f\x11\xa0\x54\ +\xca\xca\xa7\xb4\xef\x7b\xd2\xbb\xa4\xac\x13\xba\xaa\xc3\x2a\xcd\ +\xa2\x86\x2a\x22\xd5\x44\xd5\x44\x95\x18\xe7\x24\x96\x31\xd2\x33\ +\xb4\xf4\x8b\xa7\x72\x04\x59\x1c\xa0\x9b\xf0\x20\xea\x9f\xea\xa2\ +\xbe\x44\xd1\x04\xaf\x48\x3b\x8c\x31\x2d\xf5\x44\xf1\xf7\x79\x56\ +\x3c\xbc\x46\xe4\x93\xc9\x84\x1a\x54\x51\x03\xc4\xaa\x3d\x82\xc3\ +\x90\x2f\x2f\x2c\xe8\xb3\x58\xa6\x01\x1a\x71\x47\x03\xa9\xc8\x92\ +\x54\xfe\xbd\x76\x99\xe8\x67\xa5\xee\x02\x06\xaa\xa6\x0f\x82\x46\ +\x1e\x72\x11\x20\x51\x84\xbb\x5c\xe0\x5d\x18\x3d\x24\x75\xd9\x16\ +\xb1\x5f\x88\x1e\x5e\x30\x3f\x1a\x6f\x7e\x53\x85\x91\xa2\x57\xb5\ +\x68\x44\xdd\x09\xf3\xea\x44\x05\x6b\x88\xd6\xb4\x0a\x65\x0a\xea\ +\xd1\x5f\xb8\xc7\x6c\x3e\x72\x89\x1b\x31\xdb\x25\x1f\xb0\x6b\x7b\ +\x2a\xaa\xa3\x73\x8a\x47\x8c\x8c\x22\xac\x00\x9b\xe9\x22\x76\xf1\ +\x13\xdc\x0d\x10\x33\x98\xfb\xdc\xa0\x73\x6a\x00\xd3\xa2\x4b\xb6\ +\x51\xd4\x67\x6b\xe6\x3c\xea\x4f\x60\x59\xf4\x1f\x1f\xce\x78\x7c\ +\x16\x3e\xae\xb8\x77\x3a\x1f\x53\xfa\xff\x25\x39\x63\xfe\x26\xa6\ +\xcc\x9c\x23\x4b\x3a\x07\x1f\x7a\x49\x27\xf3\x71\x6c\x49\x57\x6c\ +\xf2\x26\xa6\xcc\x9c\x23\x4b\x3a\x07\x1f\x7a\x49\x27\xf3\x31\x2c\ +\xe9\x39\xaa\x7f\xb6\xca\x7f\x00\x99\x18\x96\x19\ +\x00\x00\x02\x20\ +\x00\ +\x00\x07\x7f\x78\x9c\xdd\x55\x3b\x6f\xdb\x30\x10\xde\xfb\x2b\xae\ +\xe8\xc0\x76\x20\x45\x52\x24\x45\x06\x51\x02\xd4\x40\xa7\x74\x4b\ +\x96\x6e\x6a\xcc\x48\x6c\x6c\xc9\x90\x54\xcb\xfd\xf7\x3d\xc5\xd6\ +\xc3\x56\xd0\x29\x43\x11\x69\xd0\x3d\x78\x77\xdf\x7d\xc7\x83\xae\ +\x6f\x0f\xdb\x0d\xec\x7d\xdd\x84\xaa\x4c\x89\x60\x9c\x80\x2f\x1f\ +\xab\x75\x28\xf3\x94\x3c\xdc\x7f\xa3\x96\xdc\xde\x7c\xb8\xfe\x48\ +\x29\xdc\x17\xa1\x81\xa7\xb0\xf1\xd0\x65\x0d\xe4\xbe\xf4\x75\xd6\ +\xfa\x35\xfc\xfc\x03\xeb\x7d\x68\xf6\xf9\x16\x24\x53\x40\x29\x9e\ +\x47\x0d\x0a\x1f\xf2\xa2\x4d\x89\x54\xbb\x96\xcc\x6b\x08\xd4\x82\ +\xef\xbe\x56\x87\x94\x68\xc3\x14\x77\x0a\x74\xcc\xac\xb6\x31\x08\ +\xcb\x54\x9c\x68\x90\x8a\x40\x17\xd6\x6d\x81\x01\x47\x53\x9f\x04\ +\xc1\x96\x4d\x4a\x8a\xb6\xdd\x5d\x45\x51\xd7\x75\xac\x8b\x59\x55\ +\xe7\x91\xe4\x9c\x47\x58\xf4\x74\xe4\xea\xb0\x09\xe5\xf3\x6b\x07\ +\x85\x73\x2e\x7a\xf1\x12\x84\x99\x43\x58\xa7\x64\x97\xe5\x5e\x1c\ +\xd5\xb6\xce\xca\xe6\xa9\xaa\xb7\x29\xd9\x66\x6d\x1d\x0e\x9f\x39\ +\x73\xce\x48\xa3\x80\xf7\xef\xa0\x18\xcd\x8c\xb4\x12\x12\xc5\x24\ +\x97\x5f\xfa\xe0\x5d\xd6\x16\x80\xd9\xbe\x03\xb5\x0c\xf1\x72\xa4\ +\x42\x33\x2e\x35\x97\x77\x33\x13\x9f\x2b\x02\x9b\x76\x26\xf6\x54\ +\xe8\xd5\x99\x59\xc6\x2e\xd1\xbd\x79\x66\x35\x4c\x38\x9b\x24\x68\ +\x35\xf3\x7c\xb3\x40\xc1\xb4\x12\x16\xa8\x42\x70\x28\xe1\x38\x12\ +\x27\x8c\x45\xe0\x27\x69\x05\x97\xae\x8b\xd0\x41\x45\x94\xaf\x88\ +\x53\x3f\x13\x54\xce\x38\x62\x9f\x74\x4c\xe4\x30\xd1\x50\x87\x0a\ +\xc9\xac\x12\x09\x62\x18\xc4\xd5\x04\x70\xf4\xd2\x45\x02\x7a\x5e\ +\xe2\x6e\xc9\xea\x0f\xd2\xdf\xc6\x4d\x4a\x3e\x59\xde\xbf\x24\xc2\ +\x29\x44\xf9\xdb\xcd\x71\x8e\x49\x18\x93\xcc\xdb\x44\xfe\xb8\xd5\ +\xb3\x36\x63\xe6\xb4\x55\x2f\x6d\x1e\xc5\xb3\x36\x4f\x5e\xba\x48\ +\x40\x97\x45\x26\x93\xc3\x78\xe5\xe4\x2c\xd1\x38\x94\x51\x9c\x46\ +\x3a\x3a\x17\xf1\x97\x35\x46\xe6\xca\xaa\xf4\x04\x9a\xb6\xae\x9e\ +\x3d\xf2\xc8\x5f\x9e\xc1\x40\x71\x4b\xfc\x63\xb6\x4b\x49\x5d\xfd\ +\x2e\xd7\x67\xe6\x5f\x55\x28\x2f\xed\xdb\xd0\xfa\x7a\x13\xf0\x83\ +\x3b\x8b\x33\xc3\x9a\xa3\x6f\x58\x65\x96\x68\xa3\xcd\x9b\x4f\x6a\ +\xc1\xe2\xbb\xd9\xb8\x53\x3f\xef\x6d\x60\xff\x5a\x2d\x8d\x44\x49\ +\x31\x5b\x2d\x77\x64\x8a\x8f\xe2\xd9\x6a\x9d\xbc\x74\x91\x60\x71\ +\x29\xfe\x13\x12\x8f\x44\xf6\x7f\xab\x9b\xbf\x3f\x45\xd8\x3d\ +\x00\x00\x01\x20\ +\x3c\ +\x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ +\x30\x2f\x73\x76\x67\x22\x20\x76\x69\x65\x77\x42\x6f\x78\x3d\x22\ +\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x3e\x3c\x70\x61\x74\x68\ +\x20\x64\x3d\x22\x4d\x34\x2e\x33\x34\x20\x31\x35\x2e\x36\x36\x41\ +\x37\x2e\x39\x37\x20\x37\x2e\x39\x37\x20\x30\x20\x30\x20\x30\x20\ +\x39\x20\x31\x37\x2e\x39\x34\x56\x31\x30\x48\x35\x56\x38\x68\x34\ +\x56\x35\x2e\x38\x33\x61\x33\x20\x33\x20\x30\x20\x31\x20\x31\x20\ +\x32\x20\x30\x56\x38\x68\x34\x76\x32\x68\x2d\x34\x76\x37\x2e\x39\ +\x34\x61\x37\x2e\x39\x37\x20\x37\x2e\x39\x37\x20\x30\x20\x30\x20\ +\x30\x20\x34\x2e\x36\x36\x2d\x32\x2e\x32\x38\x6c\x2d\x31\x2e\x34\ +\x32\x2d\x31\x2e\x34\x32\x68\x35\x2e\x36\x36\x6c\x2d\x32\x2e\x38\ +\x33\x20\x32\x2e\x38\x33\x61\x31\x30\x20\x31\x30\x20\x30\x20\x30\ +\x20\x31\x2d\x31\x34\x2e\x31\x34\x20\x30\x4c\x2e\x31\x20\x31\x34\ +\x2e\x32\x34\x68\x35\x2e\x36\x36\x6c\x2d\x31\x2e\x34\x32\x20\x31\ +\x2e\x34\x32\x7a\x4d\x31\x30\x20\x34\x61\x31\x20\x31\x20\x30\x20\ +\x31\x20\x30\x20\x30\x2d\x32\x20\x31\x20\x31\x20\x30\x20\x30\x20\ +\x30\x20\x30\x20\x32\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\ +\x00\x00\x02\x6c\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x31\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x53\x61\x76\x65\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\ +\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\ +\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\x73\ +\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\ +\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x78\ +\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\x20\x79\x3d\ +\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x76\x69\x65\x77\x42\x6f\x78\ +\x3d\x22\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\x6e\x61\ +\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3d\x22\ +\x6e\x65\x77\x20\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x78\ +\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\ +\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\ +\x31\x35\x2e\x31\x37\x33\x2c\x32\x48\x34\x43\x32\x2e\x38\x39\x39\ +\x2c\x32\x2c\x32\x2c\x32\x2e\x39\x2c\x32\x2c\x34\x76\x31\x32\x63\ +\x30\x2c\x31\x2e\x31\x2c\x30\x2e\x38\x39\x39\x2c\x32\x2c\x32\x2c\ +\x32\x68\x31\x32\x63\x31\x2e\x31\x30\x31\x2c\x30\x2c\x32\x2d\x30\ +\x2e\x39\x2c\x32\x2d\x32\x56\x35\x2e\x31\x32\x37\x4c\x31\x35\x2e\ +\x31\x37\x33\x2c\x32\x7a\x20\x4d\x31\x34\x2c\x38\x63\x30\x2c\x30\ +\x2e\x35\x34\x39\x2d\x30\x2e\x34\x35\x2c\x31\x2d\x31\x2c\x31\x48\ +\x37\x0d\x0a\x09\x43\x36\x2e\x34\x35\x2c\x39\x2c\x36\x2c\x38\x2e\ +\x35\x34\x39\x2c\x36\x2c\x38\x56\x33\x68\x38\x56\x38\x7a\x20\x4d\ +\x31\x33\x2c\x34\x68\x2d\x32\x76\x34\x68\x32\x56\x34\x7a\x22\x2f\ +\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x02\xbd\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x75\x74\x66\ +\x2d\x38\x22\x3f\x3e\x0d\x0a\x3c\x21\x2d\x2d\x20\x47\x65\x6e\x65\ +\x72\x61\x74\x6f\x72\x3a\x20\x41\x64\x6f\x62\x65\x20\x49\x6c\x6c\ +\x75\x73\x74\x72\x61\x74\x6f\x72\x20\x31\x38\x2e\x31\x2e\x30\x2c\ +\x20\x53\x56\x47\x20\x45\x78\x70\x6f\x72\x74\x20\x50\x6c\x75\x67\ +\x2d\x49\x6e\x20\x2e\x20\x53\x56\x47\x20\x56\x65\x72\x73\x69\x6f\ +\x6e\x3a\x20\x36\x2e\x30\x30\x20\x42\x75\x69\x6c\x64\x20\x30\x29\ +\x20\x20\x2d\x2d\x3e\x0d\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\ +\x20\x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\ +\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\ +\x2f\x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\ +\x73\x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\ +\x67\x31\x31\x2e\x64\x74\x64\x22\x3e\x0d\x0a\x3c\x73\x76\x67\x20\ +\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x69\x64\ +\x3d\x22\x43\x69\x72\x63\x6c\x65\x22\x20\x78\x6d\x6c\x6e\x73\x3d\ +\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\ +\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\x6c\ +\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\x2f\ +\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\ +\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x3d\x22\x30\x70\x78\x22\x20\ +\x79\x3d\x22\x30\x70\x78\x22\x0d\x0a\x09\x20\x76\x69\x65\x77\x42\ +\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\x20\x65\ +\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\ +\x3d\x22\x6e\x65\x77\x20\x30\x20\x30\x20\x32\x30\x20\x32\x30\x22\ +\x20\x78\x6d\x6c\x3a\x73\x70\x61\x63\x65\x3d\x22\x70\x72\x65\x73\ +\x65\x72\x76\x65\x22\x3e\x0d\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\ +\x22\x4d\x31\x30\x2c\x30\x2e\x34\x43\x34\x2e\x36\x39\x38\x2c\x30\ +\x2e\x34\x2c\x30\x2e\x34\x2c\x34\x2e\x36\x39\x38\x2c\x30\x2e\x34\ +\x2c\x31\x30\x43\x30\x2e\x34\x2c\x31\x35\x2e\x33\x30\x32\x2c\x34\ +\x2e\x36\x39\x38\x2c\x31\x39\x2e\x36\x2c\x31\x30\x2c\x31\x39\x2e\ +\x36\x63\x35\x2e\x33\x30\x31\x2c\x30\x2c\x39\x2e\x36\x2d\x34\x2e\ +\x32\x39\x38\x2c\x39\x2e\x36\x2d\x39\x2e\x36\x30\x31\x0d\x0a\x09\ +\x43\x31\x39\x2e\x36\x2c\x34\x2e\x36\x39\x38\x2c\x31\x35\x2e\x33\ +\x30\x31\x2c\x30\x2e\x34\x2c\x31\x30\x2c\x30\x2e\x34\x7a\x20\x4d\ +\x31\x30\x2c\x31\x37\x2e\x35\x39\x39\x63\x2d\x34\x2e\x31\x39\x37\ +\x2c\x30\x2d\x37\x2e\x36\x2d\x33\x2e\x34\x30\x32\x2d\x37\x2e\x36\ +\x2d\x37\x2e\x36\x53\x35\x2e\x38\x30\x32\x2c\x32\x2e\x34\x2c\x31\ +\x30\x2c\x32\x2e\x34\x63\x34\x2e\x31\x39\x37\x2c\x30\x2c\x37\x2e\ +\x36\x30\x31\x2c\x33\x2e\x34\x30\x32\x2c\x37\x2e\x36\x30\x31\x2c\ +\x37\x2e\x36\x0d\x0a\x09\x53\x31\x34\x2e\x31\x39\x37\x2c\x31\x37\ +\x2e\x35\x39\x39\x2c\x31\x30\x2c\x31\x37\x2e\x35\x39\x39\x7a\x22\ +\x2f\x3e\x0d\x0a\x3c\x2f\x73\x76\x67\x3e\x0d\x0a\ +\x00\x00\x03\x65\ +\x3c\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x27\x31\x2e\ +\x30\x27\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x27\x55\x54\x46\ +\x2d\x38\x27\x3f\x3e\x0a\x3c\x21\x2d\x2d\x20\x54\x68\x69\x73\x20\ +\x66\x69\x6c\x65\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74\ +\x65\x64\x20\x62\x79\x20\x64\x76\x69\x73\x76\x67\x6d\x20\x32\x2e\ +\x34\x20\x2d\x2d\x3e\x0a\x3c\x73\x76\x67\x20\x68\x65\x69\x67\x68\ +\x74\x3d\x27\x31\x38\x2e\x37\x35\x70\x74\x27\x20\x76\x65\x72\x73\ +\x69\x6f\x6e\x3d\x27\x31\x2e\x31\x27\x20\x76\x69\x65\x77\x42\x6f\ +\x78\x3d\x27\x35\x36\x2e\x34\x30\x39\x34\x20\x35\x33\x2e\x38\x35\ +\x38\x33\x20\x31\x38\x2e\x37\x35\x20\x31\x38\x2e\x37\x35\x27\x20\ +\x77\x69\x64\x74\x68\x3d\x27\x31\x38\x2e\x37\x35\x70\x74\x27\x20\ +\x78\x6d\x6c\x6e\x73\x3d\x27\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\ +\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\ +\x67\x27\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x27\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x31\x39\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x27\x3e\x0a\x3c\ +\x67\x20\x69\x64\x3d\x27\x70\x61\x67\x65\x31\x27\x3e\x0a\x3c\x67\ +\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\ +\x69\x78\x28\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\ +\x30\x2e\x39\x39\x36\x32\x36\x34\x20\x36\x35\x2e\x37\x38\x34\x34\ +\x20\x36\x33\x2e\x32\x33\x33\x33\x29\x27\x3e\x0a\x3c\x70\x61\x74\ +\x68\x20\x64\x3d\x27\x4d\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x20\ +\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x38\x2e\x36\x35\x37\x33\x34\ +\x20\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x38\x2e\x36\x35\x37\x33\ +\x34\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x2d\x38\x2e\x36\ +\x35\x37\x33\x34\x20\x2d\x38\x2e\x36\x35\x37\x33\x34\x4c\x20\x2d\ +\x38\x2e\x36\x35\x37\x33\x34\x20\x38\x2e\x36\x35\x37\x33\x34\x5a\ +\x27\x20\x66\x69\x6c\x6c\x3d\x27\x6e\x6f\x6e\x65\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x3d\x27\x23\x30\x30\x30\x30\x30\x30\x27\x20\x73\ +\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3d\x27\x72\ +\x6f\x75\x6e\x64\x27\x20\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\ +\x65\x6a\x6f\x69\x6e\x3d\x27\x72\x6f\x75\x6e\x64\x27\x20\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3d\ +\x27\x31\x30\x2e\x30\x33\x37\x35\x27\x20\x73\x74\x72\x6f\x6b\x65\ +\x2d\x77\x69\x64\x74\x68\x3d\x27\x31\x2e\x35\x30\x35\x36\x32\x27\ +\x2f\x3e\x0a\x3c\x2f\x67\x3e\x0a\x3c\x67\x20\x74\x72\x61\x6e\x73\ +\x66\x6f\x72\x6d\x3d\x27\x6d\x61\x74\x72\x69\x78\x28\x30\x2e\x39\ +\x39\x36\x32\x36\x34\x20\x30\x20\x30\x20\x30\x2e\x39\x39\x36\x32\ +\x36\x34\x20\x36\x35\x2e\x37\x38\x34\x34\x20\x36\x33\x2e\x32\x33\ +\x33\x33\x29\x27\x3e\x0a\x3c\x70\x61\x74\x68\x20\x64\x3d\x27\x4d\ +\x20\x34\x2e\x33\x32\x38\x36\x37\x20\x30\x43\x20\x34\x2e\x33\x32\ +\x38\x36\x37\x20\x2d\x32\x2e\x33\x39\x30\x36\x36\x20\x32\x2e\x33\ +\x39\x30\x36\x36\x20\x2d\x34\x2e\x33\x32\x38\x36\x37\x20\x30\x20\ +\x2d\x34\x2e\x33\x32\x38\x36\x37\x43\x20\x2d\x32\x2e\x33\x39\x30\ +\x36\x36\x20\x2d\x34\x2e\x33\x32\x38\x36\x37\x20\x2d\x34\x2e\x33\ +\x32\x38\x36\x37\x20\x2d\x32\x2e\x33\x39\x30\x36\x36\x20\x2d\x34\ +\x2e\x33\x32\x38\x36\x37\x20\x2d\x30\x43\x20\x2d\x34\x2e\x33\x32\ +\x38\x36\x37\x20\x32\x2e\x33\x39\x30\x36\x36\x20\x2d\x32\x2e\x33\ +\x39\x30\x36\x36\x20\x34\x2e\x33\x32\x38\x36\x37\x20\x30\x20\x34\ +\x2e\x33\x32\x38\x36\x37\x43\x20\x32\x2e\x33\x39\x30\x36\x36\x20\ +\x34\x2e\x33\x32\x38\x36\x37\x20\x34\x2e\x33\x32\x38\x36\x37\x20\ +\x32\x2e\x33\x39\x30\x36\x36\x20\x34\x2e\x33\x32\x38\x36\x37\x20\ +\x30\x5a\x27\x20\x66\x69\x6c\x6c\x3d\x27\x23\x30\x30\x30\x30\x30\ +\x30\x27\x2f\x3e\x0a\x3c\x2f\x67\x3e\x3c\x2f\x67\x3e\x0a\x3c\x2f\ +\x73\x76\x67\x3e\ +" + +qt_resource_name = b"\ +\x00\x05\ +\x00\x6f\xa6\x53\ +\x00\x69\ +\x00\x63\x00\x6f\x00\x6e\x00\x73\ +\x00\x10\ +\x08\x89\xfa\x47\ +\x00\x63\ +\x00\x65\x00\x6e\x00\x74\x00\x65\x00\x72\x00\x6f\x00\x72\x00\x69\x00\x67\x00\x69\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0e\ +\x05\xed\x38\x67\ +\x00\x61\ +\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2d\x00\x6d\x00\x6f\x00\x76\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x14\ +\x0f\xa5\xe0\xc7\ +\x00\x6d\ +\x00\x61\x00\x67\x00\x6e\x00\x69\x00\x66\x00\x79\x00\x69\x00\x6e\x00\x67\x00\x2d\x00\x67\x00\x6c\x00\x61\x00\x73\x00\x73\x00\x2e\ +\x00\x73\x00\x76\x00\x67\ +\x00\x0f\ +\x09\x76\x60\xc7\ +\x00\x63\ +\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x2d\x00\x72\x00\x6f\x00\x75\x00\x6e\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x08\ +\x0c\xf7\x55\x87\ +\x00\x74\ +\x00\x65\x00\x78\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x10\ +\x0c\x57\x65\x47\ +\x00\x61\ +\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2d\x00\x72\x00\x65\x00\x73\x00\x69\x00\x7a\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0f\ +\x07\x0e\xc4\x87\ +\x00\x6f\ +\x00\x70\x00\x65\x00\x6e\x00\x70\x00\x6f\x00\x6c\x00\x79\x00\x67\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x1d\ +\x06\xec\xf4\xc7\ +\x00\x63\ +\x00\x68\x00\x65\x00\x76\x00\x72\x00\x6f\x00\x6e\x00\x2d\x00\x77\x00\x69\x00\x74\x00\x68\x00\x2d\x00\x63\x00\x69\x00\x72\x00\x63\ +\x00\x6c\x00\x65\x00\x2d\x00\x72\x00\x69\x00\x67\x00\x68\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x10\ +\x08\xe4\xaf\x47\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x64\x00\x6f\x00\x6e\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x08\ +\x05\xa8\x57\x87\ +\x00\x63\ +\x00\x6f\x00\x64\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x11\ +\x0c\xa7\xc7\x47\ +\x00\x63\ +\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x70\x00\x6f\x00\x6c\x00\x79\x00\x67\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\ +\x00\x11\ +\x01\x60\xbc\x47\ +\x00\x73\ +\x00\x6f\x00\x63\x00\x69\x00\x61\x00\x6c\x00\x2d\x00\x70\x00\x79\x00\x74\x00\x68\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\ +\x00\x19\ +\x0f\xef\x7b\xe7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x63\x00\x6f\x00\x6c\x00\x6f\x00\x72\x00\x2d\x00\x70\x00\x61\x00\x6c\ +\x00\x65\x00\x74\x00\x74\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x12\ +\x08\x55\xef\xc7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x64\x00\x65\x00\x6c\x00\x65\x00\x74\x00\x65\x00\x2e\x00\x73\x00\x76\ +\x00\x67\ +\x00\x12\ +\x08\x79\x97\xe7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x63\x00\x61\x00\x6d\x00\x65\x00\x72\x00\x61\x00\x2e\x00\x73\x00\x76\ +\x00\x67\ +\x00\x19\ +\x0a\x43\x45\xc7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2d\x00\x66\x00\x6f\x00\x72\ +\x00\x77\x00\x61\x00\x72\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0a\ +\x01\xca\x6d\x87\ +\x00\x62\ +\x00\x75\x00\x63\x00\x6b\x00\x65\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x13\ +\x03\x24\x75\x47\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x72\x00\x65\x00\x66\x00\x72\x00\x65\x00\x73\x00\x68\x00\x2e\x00\x73\ +\x00\x76\x00\x67\ +\x00\x09\ +\x0b\x9e\x89\x07\ +\x00\x63\ +\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x1c\ +\x08\x8a\x79\x07\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x72\x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x62\x00\x75\x00\x74\ +\x00\x74\x00\x6f\x00\x6e\x00\x2d\x00\x6f\x00\x66\x00\x66\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x1b\ +\x0e\xb5\x68\xe7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x72\x00\x61\x00\x64\x00\x69\x00\x6f\x00\x2d\x00\x62\x00\x75\x00\x74\ +\x00\x74\x00\x6f\x00\x6e\x00\x2d\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x12\ +\x0c\x5e\xd4\xa7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x6c\x00\x6f\x00\x63\x00\x61\x00\x74\x00\x65\x00\x2e\x00\x73\x00\x76\ +\x00\x67\ +\x00\x12\ +\x04\xb2\x21\x47\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x65\x00\x78\x00\x70\x00\x61\x00\x6e\x00\x64\x00\x2e\x00\x73\x00\x76\ +\x00\x67\ +\x00\x0e\ +\x0f\xcb\xd5\xc7\ +\x00\x70\ +\x00\x6c\x00\x75\x00\x73\x00\x2d\x00\x72\x00\x6f\x00\x75\x00\x6e\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x07\x87\x48\x27\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x66\x00\x6f\x00\x6c\x00\x64\x00\x65\x00\x72\x00\x2d\x00\x6f\x00\x70\ +\x00\x65\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x10\ +\x06\xe3\xaf\xe7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x68\x00\x61\x00\x6e\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0f\ +\x04\xf2\xa7\x87\ +\x00\x63\ +\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x63\x00\x75\x00\x72\x00\x76\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x17\ +\x06\xc6\x02\xa7\ +\x00\x74\ +\x00\x72\x00\x69\x00\x61\x00\x6e\x00\x67\x00\x6c\x00\x65\x00\x2d\x00\x73\x00\x74\x00\x72\x00\x6f\x00\x6b\x00\x65\x00\x64\x00\x2d\ +\x00\x31\x00\x35\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0d\ +\x05\x20\xce\x87\ +\x00\x6f\ +\x00\x70\x00\x65\x00\x6e\x00\x63\x00\x75\x00\x72\x00\x76\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x07\ +\x0c\xf8\x5a\x07\ +\x00\x65\ +\x00\x79\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x08\ +\x0b\x07\x57\xa7\ +\x00\x65\ +\x00\x64\x00\x69\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x16\ +\x01\xfb\x76\x27\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2d\x00\x62\x00\x61\x00\x63\ +\x00\x6b\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x1c\ +\x04\x66\xe1\x67\ +\x00\x63\ +\x00\x68\x00\x65\x00\x76\x00\x72\x00\x6f\x00\x6e\x00\x2d\x00\x77\x00\x69\x00\x74\x00\x68\x00\x2d\x00\x63\x00\x69\x00\x72\x00\x63\ +\x00\x6c\x00\x65\x00\x2d\x00\x6c\x00\x65\x00\x66\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x15\ +\x0f\xc4\x59\xe7\ +\x00\x73\ +\x00\x75\x00\x62\x00\x64\x00\x69\x00\x72\x00\x65\x00\x63\x00\x74\x00\x6f\x00\x72\x00\x79\x00\x2d\x00\x6c\x00\x65\x00\x66\x00\x74\ +\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x11\ +\x0c\xdb\x38\xe7\ +\x00\x61\ +\x00\x6e\x00\x64\x00\x72\x00\x6f\x00\x69\x00\x64\x00\x2d\x00\x63\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\ +\x00\x08\ +\x08\xf7\x57\x07\ +\x00\x67\ +\x00\x72\x00\x69\x00\x64\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x10\ +\x04\xa9\x22\xc7\ +\x00\x66\ +\x00\x69\x00\x6c\x00\x6c\x00\x65\x00\x64\x00\x62\x00\x75\x00\x63\x00\x6b\x00\x65\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0a\ +\x0f\x68\x53\xe7\ +\x00\x61\ +\x00\x6e\x00\x63\x00\x68\x00\x6f\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x08\ +\x08\xc8\x55\xe7\ +\x00\x73\ +\x00\x61\x00\x76\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0a\ +\x0a\x2d\x1b\xc7\ +\x00\x63\ +\x00\x69\x00\x72\x00\x63\x00\x6c\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x0a\ +\x0a\xc8\x62\x67\ +\x00\x63\ +\x00\x65\x00\x6e\x00\x74\x00\x65\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\ +" + +qt_resource_struct_v1 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x29\x00\x00\x00\x02\ +\x00\x00\x01\xae\x00\x00\x00\x00\x00\x01\x00\x00\x24\x33\ +\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x39\x2f\ +\x00\x00\x04\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x74\xb4\ +\x00\x00\x02\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x3e\xea\ +\x00\x00\x05\x16\x00\x00\x00\x00\x00\x01\x00\x00\x77\x35\ +\x00\x00\x05\xc2\x00\x01\x00\x00\x00\x01\x00\x00\x80\x09\ +\x00\x00\x03\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x4f\xdc\ +\x00\x00\x04\x42\x00\x00\x00\x00\x00\x01\x00\x00\x5f\x35\ +\x00\x00\x04\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x68\xc4\ +\x00\x00\x01\x70\x00\x00\x00\x00\x00\x01\x00\x00\x1b\x82\ +\x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x04\x14\ +\x00\x00\x04\x66\x00\x00\x00\x00\x00\x01\x00\x00\x63\xf6\ +\x00\x00\x04\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x59\x67\ +\x00\x00\x01\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x15\x4c\ +\x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x11\x26\ +\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x56\x11\ +\x00\x00\x02\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x30\xe2\ +\x00\x00\x02\x38\x00\x00\x00\x00\x00\x01\x00\x00\x33\x88\ +\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x02\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x45\x24\ +\x00\x00\x06\x02\x00\x00\x00\x00\x00\x01\x00\x00\x83\x51\ +\x00\x00\x01\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x19\x05\ +\x00\x00\x05\xac\x00\x01\x00\x00\x00\x01\x00\x00\x7e\x28\ +\x00\x00\x00\x86\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x78\ +\x00\x00\x06\x18\x00\x00\x00\x00\x00\x01\x00\x00\x85\xc1\ +\x00\x00\x02\x62\x00\x00\x00\x00\x00\x01\x00\x00\x36\xae\ +\x00\x00\x06\x32\x00\x00\x00\x00\x00\x01\x00\x00\x88\x82\ +\x00\x00\x04\xce\x00\x00\x00\x00\x00\x01\x00\x00\x71\x44\ +\x00\x00\x02\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x42\x14\ +\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x0e\xa3\ +\x00\x00\x03\x72\x00\x00\x00\x00\x00\x01\x00\x00\x4b\x74\ +\x00\x00\x01\x86\x00\x00\x00\x00\x00\x01\x00\x00\x1f\xff\ +\x00\x00\x05\x84\x00\x00\x00\x00\x00\x01\x00\x00\x7b\x6d\ +\x00\x00\x00\xaa\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xc2\ +\x00\x00\x04\xba\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x55\ +\x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\x48\x24\ +\x00\x00\x05\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x82\x2d\ +\x00\x00\x00\x58\x00\x00\x00\x00\x00\x01\x00\x00\x06\xe1\ +\x00\x00\x05\x54\x00\x00\x00\x00\x00\x01\x00\x00\x7a\xfd\ +\x00\x00\x03\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x53\x16\ +\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x2b\xb7\ +" + +qt_resource_struct_v2 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x29\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x01\xae\x00\x00\x00\x00\x00\x01\x00\x00\x24\x33\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x39\x2f\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x04\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x74\xb4\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x02\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x3e\xea\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x05\x16\x00\x00\x00\x00\x00\x01\x00\x00\x77\x35\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x05\xc2\x00\x01\x00\x00\x00\x01\x00\x00\x80\x09\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x03\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x4f\xdc\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x04\x42\x00\x00\x00\x00\x00\x01\x00\x00\x5f\x35\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x04\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x68\xc4\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x01\x70\x00\x00\x00\x00\x00\x01\x00\x00\x1b\x82\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x04\x14\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x04\x66\x00\x00\x00\x00\x00\x01\x00\x00\x63\xf6\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x04\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x59\x67\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x01\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x15\x4c\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x11\x26\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x56\x11\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x02\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x30\xe2\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x02\x38\x00\x00\x00\x00\x00\x01\x00\x00\x33\x88\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x02\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x45\x24\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x06\x02\x00\x00\x00\x00\x00\x01\x00\x00\x83\x51\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x01\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x19\x05\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x05\xac\x00\x01\x00\x00\x00\x01\x00\x00\x7e\x28\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x00\x86\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x78\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x06\x18\x00\x00\x00\x00\x00\x01\x00\x00\x85\xc1\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x02\x62\x00\x00\x00\x00\x00\x01\x00\x00\x36\xae\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x06\x32\x00\x00\x00\x00\x00\x01\x00\x00\x88\x82\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x04\xce\x00\x00\x00\x00\x00\x01\x00\x00\x71\x44\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x02\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x42\x14\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x01\x00\x00\x0e\xa3\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x03\x72\x00\x00\x00\x00\x00\x01\x00\x00\x4b\x74\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x01\x86\x00\x00\x00\x00\x00\x01\x00\x00\x1f\xff\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x05\x84\x00\x00\x00\x00\x00\x01\x00\x00\x7b\x6d\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x00\xaa\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xc2\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x04\xba\x00\x00\x00\x00\x00\x01\x00\x00\x6d\x55\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\x48\x24\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x05\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x82\x2d\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +\x00\x00\x00\x58\x00\x00\x00\x00\x00\x01\x00\x00\x06\xe1\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x05\x54\x00\x00\x00\x00\x00\x01\x00\x00\x7a\xfd\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x03\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x53\x16\ +\x00\x00\x01\x64\xed\x16\x26\xd7\ +\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x2b\xb7\ +\x00\x00\x01\x64\xed\x16\x26\xd6\ +" + +qt_version = QtCore.qVersion().split('.') +if qt_version < ['5', '8', '0']: + rcc_version = 1 + qt_resource_struct = qt_resource_struct_v1 +else: + rcc_version = 2 + qt_resource_struct = qt_resource_struct_v2 + +def qInitResources(): + QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/Master/texmf-dist/asymptote/GUI/labelEditor.py b/Master/texmf-dist/asymptote/GUI/labelEditor.py new file mode 100755 index 00000000000..76bc8d7d0a1 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/labelEditor.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 + +from pyUIClass.labelTextEditor import Ui_Dialog +import PyQt5.QtWidgets as Qw +import PyQt5.QtSvg as Qs +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +import xasyArgs as xa +import xasy2asy as x2a +import subprocess +import xasyOptions as xo +import xasyUtils as xu +import tempfile +import uuid +import os +import io + + +class labelEditor(Qw.QDialog): + def __init__(self, text=''): + super().__init__() + self.ui = Ui_Dialog() + self.ui.setupUi(self) + + self.ui.btnAccept.clicked.connect(self.accept) + self.ui.btnCancel.clicked.connect(self.reject) + self.ui.chkMathMode.stateChanged.connect(self.chkMathModeChecked) + self.ui.btnPreview.clicked.connect(self.btnPreviewOnClick) + self.ui.btnGetText.clicked.connect(self.btnGetTextOnClick) + + self.svgPreview = None + self.initializeText(text) + + def initializeText(self, text: str): + if text[0] == '$' and text[-1] == '$': + self.ui.chkMathMode.setChecked(True) + text = text.strip('$') + + if text.startswith('\\displaystyle{'): + self.ui.cmbMathStyle.setCurrentText('Display Style') + text = text.rstrip('}') + text = text.replace('\\displaystyle{', '', 1) + elif text.startswith('\\scriptstyle{'): + self.ui.cmbMathStyle.setCurrentText('Script Style') + text = text.rstrip('}') + text = text.replace('\\scriptstyle{', '', 1) + + self.ui.txtLabelEdit.setPlainText(text) + + def chkMathModeChecked(self, checked): + self.ui.cmbMathStyle.setEnabled(checked) + + def getText(self): + rawText = self.ui.txtLabelEdit.toPlainText() + rawText.replace('\n', ' ') + if self.ui.chkMathMode.isChecked(): + prefix = '' + suffix = '' + if self.ui.cmbMathStyle.currentText() == 'Display Style': + prefix = '\\displaystyle{' + suffix = '}' + elif self.ui.cmbMathStyle.currentText() == 'Script Style': + prefix = '\\scriptstyle{' + suffix = '}' + return '${0}{1}{2}$'.format(prefix, rawText, suffix) + else: + return rawText + + def btnPreviewOnClick(self): + path = xa.getArgs().asypath + if path is None: + opt = xo.xasyOptions().load() + path = opt['asyPath'] + + asyInput = """ + frame f; + label(f, "{0}"); + write(min(f), newl); + write(max(f), newl); + shipout(f); + """ + + self.svgPreview = Qs.QSvgRenderer() + with tempfile.TemporaryDirectory(prefix='xasylbl_') as tmpdir: + id = str(uuid.uuid4()) + tmpFile = os.path.join(tmpdir, 'lbl-{0}.svg'.format(id)) + + with subprocess.Popen(args=[path, '-fsvg', '-o', tmpFile, '-'], encoding='utf-8', stdin=subprocess.PIPE, + stdout=subprocess.PIPE) as asy: + asy.stdin.write(asyInput.format(self.getText())) + asy.stdin.close() + out = asy.stdout.read() + + raw_array = out.splitlines() + + bounds_1, bounds_2 = [val.strip() for val in raw_array] + + min_bounds = xu.listize(bounds_1, (float, float)) + max_bounds = xu.listize(bounds_2, (float, float)) + + new_rect = self.processBounds(min_bounds, max_bounds) + self.svgPreview.load(tmpFile) + + + + self.drawPreview(new_rect) + + def drawPreview(self, naturalBounds): + img = Qg.QPixmap(self.ui.lblLabelPreview.size()) + img.fill(Qg.QColor.fromRgbF(1, 1, 1, 1)) + if self.svgPreview is None: + pass + else: + with Qg.QPainter(img) as pnt: + scale_ratio = self.getIdealScaleRatio(naturalBounds, self.ui.lblLabelPreview.rect()) + + pnt.translate(self.ui.lblLabelPreview.rect().center()) + pnt.scale(scale_ratio, scale_ratio) + self.svgPreview.render(pnt, naturalBounds) + self.ui.lblLabelPreview.setPixmap(img) + + + def getIdealScaleRatio(self, rect, boundsRect): + assert isinstance(rect, (Qc.QRect, Qc.QRectF)) + assert isinstance(rect, (Qc.QRect, Qc.QRectF)) + + magic_ratio = 0.50 + idealRatioHeight = (magic_ratio * boundsRect.height()) / rect.height() + magicRatioWidth = 0.50 + + if idealRatioHeight * rect.width() > magicRatioWidth * boundsRect.width(): + idealRatioWidth = (magicRatioWidth * boundsRect.width()) / rect.width() + idealRatio = min(idealRatioHeight, idealRatioWidth) + else: + idealRatio = idealRatioHeight + return idealRatio + + def processBounds(self, minPt, maxPt): + p1x, p1y = minPt + p2x, p2y = maxPt + + minPt = Qc.QPointF(p1x, p1y) + maxPt = Qc.QPointF(p2x, p2y) + + newRect = Qc.QRectF(minPt, maxPt) + return newRect + + + def btnGetTextOnClick(self): + msgbox = Qw.QMessageBox() + msgbox.setText('Text Preview:\n' + self.getText()) + msgbox.setWindowTitle('Text preview') + msgbox.show() + return msgbox.exec_() diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/custMatTransform.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/custMatTransform.py new file mode 100644 index 00000000000..71dc6be2d21 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/custMatTransform.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/custMatTransform.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(500, 320) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) + Dialog.setSizePolicy(sizePolicy) + Dialog.setMinimumSize(QtCore.QSize(500, 320)) + Dialog.setMaximumSize(QtCore.QSize(500, 320)) + Dialog.setMouseTracking(False) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/android-expand.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + Dialog.setWindowIcon(icon) + Dialog.setSizeGripEnabled(True) + Dialog.setModal(False) + self.centralFrame = QtWidgets.QFrame(Dialog) + self.centralFrame.setGeometry(QtCore.QRect(20, 20, 461, 271)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralFrame.sizePolicy().hasHeightForWidth()) + self.centralFrame.setSizePolicy(sizePolicy) + self.centralFrame.setBaseSize(QtCore.QSize(0, 0)) + self.centralFrame.setObjectName("centralFrame") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.centralFrame) + self.verticalLayout_3.setSpacing(4) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label = QtWidgets.QLabel(self.centralFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) + self.label.setSizePolicy(sizePolicy) + self.label.setObjectName("label") + self.verticalLayout.addWidget(self.label) + self.gridFrame = QtWidgets.QFrame(self.centralFrame) + self.gridFrame.setFrameShape(QtWidgets.QFrame.Box) + self.gridFrame.setObjectName("gridFrame") + self.gridLayout = QtWidgets.QGridLayout(self.gridFrame) + self.gridLayout.setObjectName("gridLayout") + self.lineMat00 = QtWidgets.QLineEdit(self.gridFrame) + self.lineMat00.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineMat00.setObjectName("lineMat00") + self.gridLayout.addWidget(self.lineMat00, 1, 0, 1, 1) + self.lineMat11 = QtWidgets.QLineEdit(self.gridFrame) + self.lineMat11.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineMat11.setObjectName("lineMat11") + self.gridLayout.addWidget(self.lineMat11, 2, 1, 1, 1) + self.lineMat10 = QtWidgets.QLineEdit(self.gridFrame) + self.lineMat10.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineMat10.setObjectName("lineMat10") + self.gridLayout.addWidget(self.lineMat10, 2, 0, 1, 1) + self.lineMat01 = QtWidgets.QLineEdit(self.gridFrame) + self.lineMat01.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineMat01.setObjectName("lineMat01") + self.gridLayout.addWidget(self.lineMat01, 1, 1, 1, 1) + self.verticalLayout.addWidget(self.gridFrame) + self.horizontalLayout_3.addLayout(self.verticalLayout) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_3.addItem(spacerItem) + self.verticalLayout_4 = QtWidgets.QVBoxLayout() + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.label_3 = QtWidgets.QLabel(self.centralFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) + self.label_3.setSizePolicy(sizePolicy) + self.label_3.setObjectName("label_3") + self.verticalLayout_4.addWidget(self.label_3) + self.gridFrame_2 = QtWidgets.QFrame(self.centralFrame) + self.gridFrame_2.setFrameShape(QtWidgets.QFrame.Box) + self.gridFrame_2.setObjectName("gridFrame_2") + self.gridLayout_2 = QtWidgets.QGridLayout(self.gridFrame_2) + self.gridLayout_2.setObjectName("gridLayout_2") + self.lineMatTy = QtWidgets.QLineEdit(self.gridFrame_2) + self.lineMatTy.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineMatTy.setObjectName("lineMatTy") + self.gridLayout_2.addWidget(self.lineMatTy, 2, 1, 1, 1) + self.lineMatTx = QtWidgets.QLineEdit(self.gridFrame_2) + self.lineMatTx.setMaximumSize(QtCore.QSize(70, 16777215)) + self.lineMatTx.setObjectName("lineMatTx") + self.gridLayout_2.addWidget(self.lineMatTx, 1, 1, 1, 1) + self.verticalLayout_4.addWidget(self.gridFrame_2) + self.horizontalLayout_3.addLayout(self.verticalLayout_4) + self.horizontalLayout.addLayout(self.horizontalLayout_3) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem1) + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_2 = QtWidgets.QLabel(self.centralFrame) + self.label_2.setObjectName("label_2") + self.verticalLayout_2.addWidget(self.label_2) + self.imgPreview = QtWidgets.QLabel(self.centralFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.imgPreview.sizePolicy().hasHeightForWidth()) + self.imgPreview.setSizePolicy(sizePolicy) + self.imgPreview.setMinimumSize(QtCore.QSize(150, 150)) + self.imgPreview.setBaseSize(QtCore.QSize(300, 300)) + self.imgPreview.setFrameShape(QtWidgets.QFrame.Box) + self.imgPreview.setText("") + self.imgPreview.setObjectName("imgPreview") + self.verticalLayout_2.addWidget(self.imgPreview) + self.horizontalLayout.addLayout(self.verticalLayout_2) + self.verticalLayout_3.addLayout(self.horizontalLayout) + self.lblAnchor = QtWidgets.QLabel(self.centralFrame) + self.lblAnchor.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lblAnchor.setObjectName("lblAnchor") + self.verticalLayout_3.addWidget(self.lblAnchor) + self.lblCoordsMode = QtWidgets.QLabel(self.centralFrame) + self.lblCoordsMode.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lblCoordsMode.setObjectName("lblCoordsMode") + self.verticalLayout_3.addWidget(self.lblCoordsMode) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_3.addItem(spacerItem2) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem3) + self.btnReset = QtWidgets.QPushButton(self.centralFrame) + self.btnReset.setObjectName("btnReset") + self.horizontalLayout_2.addWidget(self.btnReset) + self.btnCancel = QtWidgets.QPushButton(self.centralFrame) + self.btnCancel.setObjectName("btnCancel") + self.horizontalLayout_2.addWidget(self.btnCancel) + self.btnAccept = QtWidgets.QPushButton(self.centralFrame) + self.btnAccept.setObjectName("btnAccept") + self.horizontalLayout_2.addWidget(self.btnAccept) + self.verticalLayout_3.addLayout(self.horizontalLayout_2) + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Set Custom Transformation")) + self.label.setText(_translate("Dialog", "Transformation Matrix")) + self.lineMat00.setText(_translate("Dialog", "1")) + self.lineMat11.setText(_translate("Dialog", "1")) + self.lineMat10.setText(_translate("Dialog", "0")) + self.lineMat01.setText(_translate("Dialog", "0")) + self.label_3.setText(_translate("Dialog", "Translation")) + self.lineMatTy.setText(_translate("Dialog", "0")) + self.lineMatTx.setText(_translate("Dialog", "0")) + self.label_2.setText(_translate("Dialog", "Preview:")) + self.imgPreview.setToolTip(_translate("Dialog", "Shows a red square if transformation determinant is negative.")) + self.lblAnchor.setText(_translate("Dialog", "Anchor: Top Left")) + self.lblCoordsMode.setText(_translate("Dialog", "Coordinates: Global")) + self.btnReset.setText(_translate("Dialog", "Reset")) + self.btnCancel.setText(_translate("Dialog", "Cancel")) + self.btnAccept.setText(_translate("Dialog", "Accept")) + +import icons_rc diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/labelTextEditor.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/labelTextEditor.py new file mode 100644 index 00000000000..25bfc30cc13 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/labelTextEditor.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/labelTextEditor.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(473, 424) + self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.frame = QtWidgets.QFrame(Dialog) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame.setObjectName("frame") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setContentsMargins(-1, 0, -1, -1) + self.gridLayout.setObjectName("gridLayout") + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout.addItem(spacerItem, 0, 2, 1, 1) + self.chkMathMode = QtWidgets.QCheckBox(self.frame) + self.chkMathMode.setObjectName("chkMathMode") + self.gridLayout.addWidget(self.chkMathMode, 0, 0, 1, 1) + self.cmbMathStyle = QtWidgets.QComboBox(self.frame) + self.cmbMathStyle.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.cmbMathStyle.sizePolicy().hasHeightForWidth()) + self.cmbMathStyle.setSizePolicy(sizePolicy) + self.cmbMathStyle.setMinimumSize(QtCore.QSize(100, 0)) + self.cmbMathStyle.setObjectName("cmbMathStyle") + self.cmbMathStyle.addItem("") + self.cmbMathStyle.addItem("") + self.cmbMathStyle.addItem("") + self.gridLayout.addWidget(self.cmbMathStyle, 0, 1, 1, 1) + self.verticalLayout_3.addLayout(self.gridLayout) + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.txtLabelEdit = QtWidgets.QPlainTextEdit(self.frame) + self.txtLabelEdit.setObjectName("txtLabelEdit") + self.verticalLayout_2.addWidget(self.txtLabelEdit) + self.verticalLayout_3.addLayout(self.verticalLayout_2) + self.verticalLayout_4 = QtWidgets.QVBoxLayout() + self.verticalLayout_4.setContentsMargins(-1, 0, -1, -1) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.label = QtWidgets.QLabel(self.frame) + self.label.setObjectName("label") + self.verticalLayout_4.addWidget(self.label) + self.lblLabelPreview = QtWidgets.QLabel(self.frame) + self.lblLabelPreview.setMinimumSize(QtCore.QSize(0, 100)) + self.lblLabelPreview.setFrameShape(QtWidgets.QFrame.Box) + self.lblLabelPreview.setText("") + self.lblLabelPreview.setObjectName("lblLabelPreview") + self.verticalLayout_4.addWidget(self.lblLabelPreview) + self.verticalLayout_3.addLayout(self.verticalLayout_4) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem1) + self.btnGetText = QtWidgets.QPushButton(self.frame) + self.btnGetText.setMaximumSize(QtCore.QSize(32, 32)) + self.btnGetText.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/text.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnGetText.setIcon(icon) + self.btnGetText.setFlat(True) + self.btnGetText.setObjectName("btnGetText") + self.horizontalLayout.addWidget(self.btnGetText) + self.btnPreview = QtWidgets.QPushButton(self.frame) + self.btnPreview.setMaximumSize(QtCore.QSize(32, 32)) + self.btnPreview.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/icons/eye.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnPreview.setIcon(icon1) + self.btnPreview.setFlat(True) + self.btnPreview.setObjectName("btnPreview") + self.horizontalLayout.addWidget(self.btnPreview) + self.btnCancel = QtWidgets.QPushButton(self.frame) + self.btnCancel.setMaximumSize(QtCore.QSize(32, 32)) + self.btnCancel.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(":/icons/android-close.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnCancel.setIcon(icon2) + self.btnCancel.setFlat(True) + self.btnCancel.setObjectName("btnCancel") + self.horizontalLayout.addWidget(self.btnCancel) + self.btnAccept = QtWidgets.QPushButton(self.frame) + self.btnAccept.setMaximumSize(QtCore.QSize(32, 32)) + self.btnAccept.setText("") + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(":/icons/android-done.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnAccept.setIcon(icon3) + self.btnAccept.setFlat(True) + self.btnAccept.setObjectName("btnAccept") + self.horizontalLayout.addWidget(self.btnAccept) + self.verticalLayout_3.addLayout(self.horizontalLayout) + self.verticalLayout.addWidget(self.frame) + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + self.chkMathMode.setText(_translate("Dialog", "Math Mode")) + self.cmbMathStyle.setItemText(0, _translate("Dialog", "Inline Style")) + self.cmbMathStyle.setItemText(1, _translate("Dialog", "Display Style")) + self.cmbMathStyle.setItemText(2, _translate("Dialog", "Script Style")) + self.label.setText(_translate("Dialog", "Preview")) + +import icons_rc diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/setCustomAnchor.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/setCustomAnchor.py new file mode 100644 index 00000000000..e1637173fc3 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/setCustomAnchor.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/setCustomAnchor.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(245, 161) + self.verticalLayoutWidget = QtWidgets.QWidget(Dialog) + self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 20, 201, 121)) + self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.formLayout = QtWidgets.QFormLayout() + self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow) + self.formLayout.setObjectName("formLayout") + self.label = QtWidgets.QLabel(self.verticalLayoutWidget) + self.label.setObjectName("label") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label) + self.lineEditX = QtWidgets.QLineEdit(self.verticalLayoutWidget) + self.lineEditX.setObjectName("lineEditX") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEditX) + self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget) + self.label_2.setObjectName("label_2") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2) + self.lineEditY = QtWidgets.QLineEdit(self.verticalLayoutWidget) + self.lineEditY.setObjectName("lineEditY") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.lineEditY) + self.verticalLayout.addLayout(self.formLayout) + self.buttonBox = QtWidgets.QDialogButtonBox(self.verticalLayoutWidget) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Reset) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) + self.buttonBox.rejected.connect(Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Set Custom Anchor")) + self.label.setText(_translate("Dialog", "X:")) + self.lineEditX.setText(_translate("Dialog", "0")) + self.label_2.setText(_translate("Dialog", "Y:")) + self.lineEditY.setText(_translate("Dialog", "0")) + diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_addLabel.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_addLabel.py new file mode 100644 index 00000000000..f7c7a0c5ae5 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_addLabel.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/widg_addLabel.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.setWindowModality(QtCore.Qt.NonModal) + Form.resize(599, 35) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) + Form.setSizePolicy(sizePolicy) + Form.setMinimumSize(QtCore.QSize(0, 35)) + Form.setMaximumSize(QtCore.QSize(16777215, 35)) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setSpacing(0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.txtLabelText = QtWidgets.QLineEdit(Form) + self.txtLabelText.setObjectName("txtLabelText") + self.horizontalLayout.addWidget(self.txtLabelText) + self.btnAdvancedEdit = QtWidgets.QPushButton(Form) + self.btnAdvancedEdit.setMaximumSize(QtCore.QSize(25, 25)) + self.btnAdvancedEdit.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/edit.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnAdvancedEdit.setIcon(icon) + self.btnAdvancedEdit.setFlat(True) + self.btnAdvancedEdit.setObjectName("btnAdvancedEdit") + self.horizontalLayout.addWidget(self.btnAdvancedEdit) + self.label = QtWidgets.QLabel(Form) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.cmbAlign = QtWidgets.QComboBox(Form) + self.cmbAlign.setObjectName("cmbAlign") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.cmbAlign.addItem("") + self.horizontalLayout.addWidget(self.cmbAlign) + self.label_3 = QtWidgets.QLabel(Form) + self.label_3.setObjectName("label_3") + self.horizontalLayout.addWidget(self.label_3) + self.cmbFontSize = QtWidgets.QComboBox(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.cmbFontSize.sizePolicy().hasHeightForWidth()) + self.cmbFontSize.setSizePolicy(sizePolicy) + self.cmbFontSize.setEditable(True) + self.cmbFontSize.setObjectName("cmbFontSize") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.cmbFontSize.addItem("") + self.horizontalLayout.addWidget(self.cmbFontSize) + self.label_2 = QtWidgets.QLabel(Form) + self.label_2.setObjectName("label_2") + self.horizontalLayout.addWidget(self.label_2) + self.txtShiftX = QtWidgets.QLineEdit(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.txtShiftX.sizePolicy().hasHeightForWidth()) + self.txtShiftX.setSizePolicy(sizePolicy) + self.txtShiftX.setMaximumSize(QtCore.QSize(50, 16777215)) + self.txtShiftX.setObjectName("txtShiftX") + self.horizontalLayout.addWidget(self.txtShiftX) + self.txtShiftY = QtWidgets.QLineEdit(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.txtShiftY.sizePolicy().hasHeightForWidth()) + self.txtShiftY.setSizePolicy(sizePolicy) + self.txtShiftY.setMaximumSize(QtCore.QSize(50, 16777215)) + self.txtShiftY.setObjectName("txtShiftY") + self.horizontalLayout.addWidget(self.txtShiftY) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.horizontalLayout_2.addLayout(self.horizontalLayout) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.txtLabelText.setToolTip(_translate("Form", "Number of Sides")) + self.txtLabelText.setPlaceholderText(_translate("Form", "Text")) + self.label.setText(_translate("Form", "Align")) + self.cmbAlign.setItemText(0, _translate("Form", "Center")) + self.cmbAlign.setItemText(1, _translate("Form", "N")) + self.cmbAlign.setItemText(2, _translate("Form", "E")) + self.cmbAlign.setItemText(3, _translate("Form", "W")) + self.cmbAlign.setItemText(4, _translate("Form", "S")) + self.cmbAlign.setItemText(5, _translate("Form", "NW")) + self.cmbAlign.setItemText(6, _translate("Form", "NE")) + self.cmbAlign.setItemText(7, _translate("Form", "SW")) + self.cmbAlign.setItemText(8, _translate("Form", "SE")) + self.cmbAlign.setItemText(9, _translate("Form", "Custom")) + self.label_3.setText(_translate("Form", "Font Size")) + self.cmbFontSize.setItemText(0, _translate("Form", "-")) + self.cmbFontSize.setItemText(1, _translate("Form", "8")) + self.cmbFontSize.setItemText(2, _translate("Form", "9")) + self.cmbFontSize.setItemText(3, _translate("Form", "10")) + self.cmbFontSize.setItemText(4, _translate("Form", "11")) + self.cmbFontSize.setItemText(5, _translate("Form", "12")) + self.cmbFontSize.setItemText(6, _translate("Form", "14")) + self.cmbFontSize.setItemText(7, _translate("Form", "18")) + self.cmbFontSize.setItemText(8, _translate("Form", "24")) + self.cmbFontSize.setItemText(9, _translate("Form", "48")) + self.cmbFontSize.setItemText(10, _translate("Form", "72")) + self.label_2.setText(_translate("Form", "Custom Align")) + self.txtShiftX.setPlaceholderText(_translate("Form", "Shift X")) + self.txtShiftY.setPlaceholderText(_translate("Form", "Shift Y")) + +import icons_rc diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_addPolyOpt.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_addPolyOpt.py new file mode 100644 index 00000000000..a9cc20ba106 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_addPolyOpt.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/widg_addPolyOpt.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.setWindowModality(QtCore.Qt.NonModal) + Form.resize(326, 35) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) + Form.setSizePolicy(sizePolicy) + Form.setMinimumSize(QtCore.QSize(0, 35)) + Form.setMaximumSize(QtCore.QSize(16777215, 35)) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setSpacing(0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.chkInscribed = QtWidgets.QCheckBox(Form) + self.chkInscribed.setObjectName("chkInscribed") + self.horizontalLayout.addWidget(self.chkInscribed) + self.txtSides = QtWidgets.QLineEdit(Form) + self.txtSides.setObjectName("txtSides") + self.horizontalLayout.addWidget(self.txtSides) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.horizontalLayout_2.addLayout(self.horizontalLayout) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.chkInscribed.setText(_translate("Form", "Start at Vertex")) + self.txtSides.setToolTip(_translate("Form", "Number of Sides")) + self.txtSides.setPlaceholderText(_translate("Form", "Sides")) + diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_editBezier.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_editBezier.py new file mode 100644 index 00000000000..1379e68ab70 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/widg_editBezier.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/widg_editBezier.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.setWindowModality(QtCore.Qt.NonModal) + Form.resize(692, 35) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) + Form.setSizePolicy(sizePolicy) + Form.setMinimumSize(QtCore.QSize(0, 35)) + Form.setMaximumSize(QtCore.QSize(16777215, 35)) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setSpacing(0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.cmbLockMode = QtWidgets.QComboBox(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.cmbLockMode.sizePolicy().hasHeightForWidth()) + self.cmbLockMode.setSizePolicy(sizePolicy) + self.cmbLockMode.setObjectName("cmbLockMode") + self.cmbLockMode.addItem("") + self.cmbLockMode.addItem("") + self.cmbLockMode.addItem("") + self.horizontalLayout.addWidget(self.cmbLockMode) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.chkRecompute = QtWidgets.QCheckBox(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.chkRecompute.sizePolicy().hasHeightForWidth()) + self.chkRecompute.setSizePolicy(sizePolicy) + self.chkRecompute.setObjectName("chkRecompute") + self.horizontalLayout.addWidget(self.chkRecompute) + self.btnForceRecompute = QtWidgets.QPushButton(Form) + self.btnForceRecompute.setObjectName("btnForceRecompute") + self.horizontalLayout.addWidget(self.btnForceRecompute) + self.btnOk = QtWidgets.QPushButton(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnOk.sizePolicy().hasHeightForWidth()) + self.btnOk.setSizePolicy(sizePolicy) + self.btnOk.setMaximumSize(QtCore.QSize(25, 25)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/check.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnOk.setIcon(icon) + self.btnOk.setFlat(True) + self.btnOk.setObjectName("btnOk") + self.horizontalLayout.addWidget(self.btnOk) + self.btnCancel = QtWidgets.QPushButton(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnCancel.sizePolicy().hasHeightForWidth()) + self.btnCancel.setSizePolicy(sizePolicy) + self.btnCancel.setMaximumSize(QtCore.QSize(25, 25)) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/icons/close-round.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnCancel.setIcon(icon1) + self.btnCancel.setFlat(True) + self.btnCancel.setObjectName("btnCancel") + self.horizontalLayout.addWidget(self.btnCancel) + self.horizontalLayout_2.addLayout(self.horizontalLayout) + + self.retranslateUi(Form) + self.cmbLockMode.setCurrentIndex(1) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.cmbLockMode.setItemText(0, _translate("Form", "No Lock")) + self.cmbLockMode.setItemText(1, _translate("Form", "Lock Angle")) + self.cmbLockMode.setItemText(2, _translate("Form", "Lock Angle & Scale")) + self.chkRecompute.setText(_translate("Form", "Recompute Path")) + self.btnForceRecompute.setText(_translate("Form", "Recompute Once")) + +import icons_rc diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/widgetPointEditor.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/widgetPointEditor.py new file mode 100644 index 00000000000..5e2ffc0b531 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/widgetPointEditor.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/widgetPointEditor.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(324, 67) + self.verticalLayout = QtWidgets.QVBoxLayout(Form) + self.verticalLayout.setObjectName("verticalLayout") + self.nameLabel = QtWidgets.QLabel(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.nameLabel.sizePolicy().hasHeightForWidth()) + self.nameLabel.setSizePolicy(sizePolicy) + self.nameLabel.setObjectName("nameLabel") + self.verticalLayout.addWidget(self.nameLabel) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.lineXorA = QtWidgets.QLineEdit(Form) + self.lineXorA.setEnabled(False) + self.lineXorA.setReadOnly(False) + self.lineXorA.setObjectName("lineXorA") + self.horizontalLayout.addWidget(self.lineXorA) + self.lineYorM = QtWidgets.QLineEdit(Form) + self.lineYorM.setEnabled(False) + self.lineYorM.setAutoFillBackground(False) + self.lineYorM.setReadOnly(False) + self.lineYorM.setObjectName("lineYorM") + self.horizontalLayout.addWidget(self.lineYorM) + self.btnRelative = QtWidgets.QPushButton(Form) + self.btnRelative.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnRelative.sizePolicy().hasHeightForWidth()) + self.btnRelative.setSizePolicy(sizePolicy) + self.btnRelative.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/android-locate.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnRelative.setIcon(icon) + self.btnRelative.setCheckable(True) + self.btnRelative.setFlat(False) + self.btnRelative.setObjectName("btnRelative") + self.horizontalLayout.addWidget(self.btnRelative) + self.btnPolar = QtWidgets.QPushButton(Form) + self.btnPolar.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnPolar.sizePolicy().hasHeightForWidth()) + self.btnPolar.setSizePolicy(sizePolicy) + self.btnPolar.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/icons/android-radio-button-off.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnPolar.setIcon(icon1) + self.btnPolar.setCheckable(True) + self.btnPolar.setFlat(False) + self.btnPolar.setObjectName("btnPolar") + self.horizontalLayout.addWidget(self.btnPolar) + self.btnManualAdj = QtWidgets.QPushButton(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnManualAdj.sizePolicy().hasHeightForWidth()) + self.btnManualAdj.setSizePolicy(sizePolicy) + self.btnManualAdj.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(":/icons/edit.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnManualAdj.setIcon(icon2) + self.btnManualAdj.setCheckable(True) + self.btnManualAdj.setFlat(False) + self.btnManualAdj.setObjectName("btnManualAdj") + self.horizontalLayout.addWidget(self.btnManualAdj) + self.verticalLayout.addLayout(self.horizontalLayout) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.nameLabel.setText(_translate("Form", "Left Control Point")) + self.lineXorA.setToolTip(_translate("Form", "X")) + self.lineXorA.setPlaceholderText(_translate("Form", "X")) + self.lineYorM.setToolTip(_translate("Form", "X")) + self.lineYorM.setPlaceholderText(_translate("Form", "Y")) + +import icons_rc diff --git a/Master/texmf-dist/asymptote/GUI/pyUIClass/window1.py b/Master/texmf-dist/asymptote/GUI/pyUIClass/window1.py new file mode 100644 index 00000000000..24f30dce850 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/pyUIClass/window1.py @@ -0,0 +1,930 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'GUI/windows/window1.ui' +# +# Created by: PyQt5 UI code generator 5.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(1030, 768) + self.centralwidget = QtWidgets.QWidget(MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) + self.centralwidget.setSizePolicy(sizePolicy) + self.centralwidget.setMouseTracking(True) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setSpacing(0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.mainWidget = QtWidgets.QWidget(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.mainWidget.sizePolicy().hasHeightForWidth()) + self.mainWidget.setSizePolicy(sizePolicy) + self.mainWidget.setMouseTracking(True) + self.mainWidget.setObjectName("mainWidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.mainWidget) + self.verticalLayout.setContentsMargins(2, 2, 2, 2) + self.verticalLayout.setSpacing(4) + self.verticalLayout.setObjectName("verticalLayout") + self.menuFrame = QtWidgets.QFrame(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.menuFrame.sizePolicy().hasHeightForWidth()) + self.menuFrame.setSizePolicy(sizePolicy) + self.menuFrame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.menuFrame.setObjectName("menuFrame") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.menuFrame) + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setSpacing(4) + self.horizontalLayout.setObjectName("horizontalLayout") + self.btnUndo = QtWidgets.QPushButton(self.menuFrame) + self.btnUndo.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnUndo.sizePolicy().hasHeightForWidth()) + self.btnUndo.setSizePolicy(sizePolicy) + self.btnUndo.setMaximumSize(QtCore.QSize(25, 25)) + self.btnUndo.setBaseSize(QtCore.QSize(32, 32)) + self.btnUndo.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icons/android-arrow-back.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnUndo.setIcon(icon) + self.btnUndo.setIconSize(QtCore.QSize(16, 16)) + self.btnUndo.setFlat(True) + self.btnUndo.setObjectName("btnUndo") + self.horizontalLayout.addWidget(self.btnUndo) + self.btnRedo = QtWidgets.QPushButton(self.menuFrame) + self.btnRedo.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnRedo.sizePolicy().hasHeightForWidth()) + self.btnRedo.setSizePolicy(sizePolicy) + self.btnRedo.setMaximumSize(QtCore.QSize(25, 25)) + self.btnRedo.setBaseSize(QtCore.QSize(32, 32)) + self.btnRedo.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/icons/android-arrow-forward.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnRedo.setIcon(icon1) + self.btnRedo.setFlat(True) + self.btnRedo.setObjectName("btnRedo") + self.horizontalLayout.addWidget(self.btnRedo) + self.btnLoadFile = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnLoadFile.sizePolicy().hasHeightForWidth()) + self.btnLoadFile.setSizePolicy(sizePolicy) + self.btnLoadFile.setMaximumSize(QtCore.QSize(25, 25)) + self.btnLoadFile.setBaseSize(QtCore.QSize(32, 32)) + self.btnLoadFile.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(":/icons/android-folder-open.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnLoadFile.setIcon(icon2) + self.btnLoadFile.setFlat(True) + self.btnLoadFile.setObjectName("btnLoadFile") + self.horizontalLayout.addWidget(self.btnLoadFile) + self.btnSave = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnSave.sizePolicy().hasHeightForWidth()) + self.btnSave.setSizePolicy(sizePolicy) + self.btnSave.setMaximumSize(QtCore.QSize(25, 25)) + self.btnSave.setBaseSize(QtCore.QSize(32, 32)) + self.btnSave.setText("") + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(":/icons/save.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnSave.setIcon(icon3) + self.btnSave.setFlat(True) + self.btnSave.setObjectName("btnSave") + self.horizontalLayout.addWidget(self.btnSave) + self.btnViewCode = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnViewCode.sizePolicy().hasHeightForWidth()) + self.btnViewCode.setSizePolicy(sizePolicy) + self.btnViewCode.setMaximumSize(QtCore.QSize(25, 25)) + self.btnViewCode.setBaseSize(QtCore.QSize(32, 32)) + self.btnViewCode.setText("") + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap(":/icons/code.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnViewCode.setIcon(icon4) + self.btnViewCode.setFlat(True) + self.btnViewCode.setObjectName("btnViewCode") + self.horizontalLayout.addWidget(self.btnViewCode) + self.btnQuickScreenshot = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnQuickScreenshot.sizePolicy().hasHeightForWidth()) + self.btnQuickScreenshot.setSizePolicy(sizePolicy) + self.btnQuickScreenshot.setMaximumSize(QtCore.QSize(25, 25)) + self.btnQuickScreenshot.setBaseSize(QtCore.QSize(32, 32)) + self.btnQuickScreenshot.setText("") + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap(":/icons/android-camera.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnQuickScreenshot.setIcon(icon5) + self.btnQuickScreenshot.setFlat(True) + self.btnQuickScreenshot.setObjectName("btnQuickScreenshot") + self.horizontalLayout.addWidget(self.btnQuickScreenshot) + spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.btnDrawAxes = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnDrawAxes.sizePolicy().hasHeightForWidth()) + self.btnDrawAxes.setSizePolicy(sizePolicy) + self.btnDrawAxes.setMaximumSize(QtCore.QSize(25, 25)) + self.btnDrawAxes.setBaseSize(QtCore.QSize(32, 32)) + font = QtGui.QFont() + font.setFamily("Roboto") + font.setBold(True) + font.setWeight(75) + self.btnDrawAxes.setFont(font) + self.btnDrawAxes.setText("") + icon6 = QtGui.QIcon() + icon6.addPixmap(QtGui.QPixmap(":/icons/plus-round.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnDrawAxes.setIcon(icon6) + self.btnDrawAxes.setCheckable(True) + self.btnDrawAxes.setChecked(True) + self.btnDrawAxes.setFlat(True) + self.btnDrawAxes.setObjectName("btnDrawAxes") + self.horizontalLayout.addWidget(self.btnDrawAxes) + self.btnDrawGrid = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnDrawGrid.sizePolicy().hasHeightForWidth()) + self.btnDrawGrid.setSizePolicy(sizePolicy) + self.btnDrawGrid.setMaximumSize(QtCore.QSize(25, 25)) + self.btnDrawGrid.setBaseSize(QtCore.QSize(32, 32)) + font = QtGui.QFont() + font.setFamily("Roboto") + font.setBold(True) + font.setWeight(75) + self.btnDrawGrid.setFont(font) + self.btnDrawGrid.setText("") + icon7 = QtGui.QIcon() + icon7.addPixmap(QtGui.QPixmap(":/icons/grid.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnDrawGrid.setIcon(icon7) + self.btnDrawGrid.setCheckable(True) + self.btnDrawGrid.setChecked(False) + self.btnDrawGrid.setFlat(True) + self.btnDrawGrid.setObjectName("btnDrawGrid") + self.horizontalLayout.addWidget(self.btnDrawGrid) + self.btnSetZoom = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnSetZoom.sizePolicy().hasHeightForWidth()) + self.btnSetZoom.setSizePolicy(sizePolicy) + self.btnSetZoom.setMaximumSize(QtCore.QSize(25, 25)) + self.btnSetZoom.setBaseSize(QtCore.QSize(32, 32)) + self.btnSetZoom.setText("") + icon8 = QtGui.QIcon() + icon8.addPixmap(QtGui.QPixmap(":/icons/magnifying-glass.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnSetZoom.setIcon(icon8) + self.btnSetZoom.setFlat(True) + self.btnSetZoom.setObjectName("btnSetZoom") + self.horizontalLayout.addWidget(self.btnSetZoom) + self.btnPanCenter = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnPanCenter.sizePolicy().hasHeightForWidth()) + self.btnPanCenter.setSizePolicy(sizePolicy) + self.btnPanCenter.setMaximumSize(QtCore.QSize(25, 25)) + self.btnPanCenter.setBaseSize(QtCore.QSize(32, 32)) + self.btnPanCenter.setText("") + icon9 = QtGui.QIcon() + icon9.addPixmap(QtGui.QPixmap(":/icons/center.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnPanCenter.setIcon(icon9) + self.btnPanCenter.setFlat(True) + self.btnPanCenter.setObjectName("btnPanCenter") + self.horizontalLayout.addWidget(self.btnPanCenter) + self.btnResetPan = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnResetPan.sizePolicy().hasHeightForWidth()) + self.btnResetPan.setSizePolicy(sizePolicy) + self.btnResetPan.setMaximumSize(QtCore.QSize(25, 25)) + self.btnResetPan.setBaseSize(QtCore.QSize(32, 32)) + self.btnResetPan.setText("") + icon10 = QtGui.QIcon() + icon10.addPixmap(QtGui.QPixmap(":/icons/centerorigin.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnResetPan.setIcon(icon10) + self.btnResetPan.setFlat(True) + self.btnResetPan.setObjectName("btnResetPan") + self.horizontalLayout.addWidget(self.btnResetPan) + self.btnAlignX = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnAlignX.sizePolicy().hasHeightForWidth()) + self.btnAlignX.setSizePolicy(sizePolicy) + self.btnAlignX.setMaximumSize(QtCore.QSize(25, 25)) + self.btnAlignX.setBaseSize(QtCore.QSize(32, 32)) + font = QtGui.QFont() + font.setFamily("Roboto") + font.setBold(True) + font.setWeight(75) + self.btnAlignX.setFont(font) + self.btnAlignX.setCheckable(True) + self.btnAlignX.setFlat(True) + self.btnAlignX.setObjectName("btnAlignX") + self.horizontalLayout.addWidget(self.btnAlignX) + self.btnAlignY = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnAlignY.sizePolicy().hasHeightForWidth()) + self.btnAlignY.setSizePolicy(sizePolicy) + self.btnAlignY.setMaximumSize(QtCore.QSize(25, 25)) + self.btnAlignY.setBaseSize(QtCore.QSize(32, 32)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.btnAlignY.setFont(font) + self.btnAlignY.setCheckable(True) + self.btnAlignY.setFlat(True) + self.btnAlignY.setObjectName("btnAlignY") + self.horizontalLayout.addWidget(self.btnAlignY) + spacerItem1 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem1) + self.btnSelectEdit = QtWidgets.QPushButton(self.menuFrame) + self.btnSelectEdit.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnSelectEdit.sizePolicy().hasHeightForWidth()) + self.btnSelectEdit.setSizePolicy(sizePolicy) + self.btnSelectEdit.setMaximumSize(QtCore.QSize(25, 25)) + self.btnSelectEdit.setText("") + icon11 = QtGui.QIcon() + icon11.addPixmap(QtGui.QPixmap(":/icons/edit.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnSelectEdit.setIcon(icon11) + self.btnSelectEdit.setFlat(True) + self.btnSelectEdit.setObjectName("btnSelectEdit") + self.horizontalLayout.addWidget(self.btnSelectEdit) + self.btnDeleteMode = QtWidgets.QPushButton(self.menuFrame) + self.btnDeleteMode.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnDeleteMode.sizePolicy().hasHeightForWidth()) + self.btnDeleteMode.setSizePolicy(sizePolicy) + self.btnDeleteMode.setMaximumSize(QtCore.QSize(25, 25)) + self.btnDeleteMode.setBaseSize(QtCore.QSize(32, 32)) + self.btnDeleteMode.setText("") + icon12 = QtGui.QIcon() + icon12.addPixmap(QtGui.QPixmap(":/icons/android-delete.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnDeleteMode.setIcon(icon12) + self.btnDeleteMode.setCheckable(False) + self.btnDeleteMode.setFlat(True) + self.btnDeleteMode.setObjectName("btnDeleteMode") + self.horizontalLayout.addWidget(self.btnDeleteMode) + spacerItem2 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem2) + self.btnPan = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnPan.sizePolicy().hasHeightForWidth()) + self.btnPan.setSizePolicy(sizePolicy) + self.btnPan.setMaximumSize(QtCore.QSize(25, 25)) + self.btnPan.setBaseSize(QtCore.QSize(32, 32)) + self.btnPan.setText("") + icon13 = QtGui.QIcon() + icon13.addPixmap(QtGui.QPixmap(":/icons/android-hand.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnPan.setIcon(icon13) + self.btnPan.setCheckable(True) + self.btnPan.setFlat(True) + self.btnPan.setObjectName("btnPan") + self.horizontalLayout.addWidget(self.btnPan) + self.btnTranslate = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnTranslate.sizePolicy().hasHeightForWidth()) + self.btnTranslate.setSizePolicy(sizePolicy) + self.btnTranslate.setMaximumSize(QtCore.QSize(25, 25)) + self.btnTranslate.setBaseSize(QtCore.QSize(32, 32)) + self.btnTranslate.setText("") + icon14 = QtGui.QIcon() + icon14.addPixmap(QtGui.QPixmap(":/icons/arrow-move.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnTranslate.setIcon(icon14) + self.btnTranslate.setCheckable(True) + self.btnTranslate.setChecked(True) + self.btnTranslate.setFlat(True) + self.btnTranslate.setObjectName("btnTranslate") + self.horizontalLayout.addWidget(self.btnTranslate) + self.btnScale = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnScale.sizePolicy().hasHeightForWidth()) + self.btnScale.setSizePolicy(sizePolicy) + self.btnScale.setMaximumSize(QtCore.QSize(25, 25)) + self.btnScale.setBaseSize(QtCore.QSize(32, 32)) + self.btnScale.setText("") + icon15 = QtGui.QIcon() + icon15.addPixmap(QtGui.QPixmap(":/icons/arrow-resize.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnScale.setIcon(icon15) + self.btnScale.setCheckable(True) + self.btnScale.setFlat(True) + self.btnScale.setObjectName("btnScale") + self.horizontalLayout.addWidget(self.btnScale) + self.btnRotate = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnRotate.sizePolicy().hasHeightForWidth()) + self.btnRotate.setSizePolicy(sizePolicy) + self.btnRotate.setMaximumSize(QtCore.QSize(25, 25)) + self.btnRotate.setBaseSize(QtCore.QSize(32, 32)) + self.btnRotate.setText("") + icon16 = QtGui.QIcon() + icon16.addPixmap(QtGui.QPixmap(":/icons/android-refresh.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnRotate.setIcon(icon16) + self.btnRotate.setCheckable(True) + self.btnRotate.setFlat(True) + self.btnRotate.setObjectName("btnRotate") + self.horizontalLayout.addWidget(self.btnRotate) + spacerItem3 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem3) + self.btnAnchor = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnAnchor.sizePolicy().hasHeightForWidth()) + self.btnAnchor.setSizePolicy(sizePolicy) + self.btnAnchor.setMaximumSize(QtCore.QSize(25, 25)) + self.btnAnchor.setBaseSize(QtCore.QSize(32, 32)) + self.btnAnchor.setText("") + icon17 = QtGui.QIcon() + icon17.addPixmap(QtGui.QPixmap(":/icons/anchor.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnAnchor.setIcon(icon17) + self.btnAnchor.setCheckable(True) + self.btnAnchor.setChecked(False) + self.btnAnchor.setFlat(True) + self.btnAnchor.setObjectName("btnAnchor") + self.horizontalLayout.addWidget(self.btnAnchor) + self.comboAnchor = QtWidgets.QComboBox(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.comboAnchor.sizePolicy().hasHeightForWidth()) + self.comboAnchor.setSizePolicy(sizePolicy) + self.comboAnchor.setMinimumSize(QtCore.QSize(127, 0)) + self.comboAnchor.setMaximumSize(QtCore.QSize(127, 25)) + self.comboAnchor.setLayoutDirection(QtCore.Qt.LeftToRight) + self.comboAnchor.setEditable(False) + self.comboAnchor.setInsertPolicy(QtWidgets.QComboBox.InsertAtCurrent) + self.comboAnchor.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContentsOnFirstShow) + self.comboAnchor.setIconSize(QtCore.QSize(0, 0)) + self.comboAnchor.setDuplicatesEnabled(False) + self.comboAnchor.setFrame(False) + self.comboAnchor.setModelColumn(0) + self.comboAnchor.setObjectName("comboAnchor") + self.comboAnchor.addItem("") + self.comboAnchor.addItem("") + self.comboAnchor.addItem("") + self.comboAnchor.addItem("") + self.comboAnchor.addItem("") + self.comboAnchor.addItem("") + self.comboAnchor.addItem("") + self.horizontalLayout.addWidget(self.comboAnchor) + self.btnToggleVisible = QtWidgets.QPushButton(self.menuFrame) + self.btnToggleVisible.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnToggleVisible.sizePolicy().hasHeightForWidth()) + self.btnToggleVisible.setSizePolicy(sizePolicy) + self.btnToggleVisible.setMaximumSize(QtCore.QSize(25, 25)) + self.btnToggleVisible.setBaseSize(QtCore.QSize(32, 32)) + self.btnToggleVisible.setText("") + icon18 = QtGui.QIcon() + icon18.addPixmap(QtGui.QPixmap(":/icons/eye.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnToggleVisible.setIcon(icon18) + self.btnToggleVisible.setCheckable(False) + self.btnToggleVisible.setFlat(True) + self.btnToggleVisible.setObjectName("btnToggleVisible") + self.horizontalLayout.addWidget(self.btnToggleVisible) + self.btnCustTransform = QtWidgets.QPushButton(self.menuFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnCustTransform.sizePolicy().hasHeightForWidth()) + self.btnCustTransform.setSizePolicy(sizePolicy) + self.btnCustTransform.setMaximumSize(QtCore.QSize(25, 25)) + self.btnCustTransform.setBaseSize(QtCore.QSize(32, 32)) + self.btnCustTransform.setText("") + icon19 = QtGui.QIcon() + icon19.addPixmap(QtGui.QPixmap(":/icons/android-expand.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnCustTransform.setIcon(icon19) + self.btnCustTransform.setFlat(True) + self.btnCustTransform.setObjectName("btnCustTransform") + self.horizontalLayout.addWidget(self.btnCustTransform) + self.btnSendBackwards = QtWidgets.QPushButton(self.menuFrame) + self.btnSendBackwards.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnSendBackwards.sizePolicy().hasHeightForWidth()) + self.btnSendBackwards.setSizePolicy(sizePolicy) + self.btnSendBackwards.setMaximumSize(QtCore.QSize(25, 25)) + self.btnSendBackwards.setBaseSize(QtCore.QSize(32, 32)) + self.btnSendBackwards.setText("") + icon20 = QtGui.QIcon() + icon20.addPixmap(QtGui.QPixmap(":/icons/chevron-with-circle-left.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnSendBackwards.setIcon(icon20) + self.btnSendBackwards.setFlat(True) + self.btnSendBackwards.setObjectName("btnSendBackwards") + self.horizontalLayout.addWidget(self.btnSendBackwards) + self.btnSendForwards = QtWidgets.QPushButton(self.menuFrame) + self.btnSendForwards.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnSendForwards.sizePolicy().hasHeightForWidth()) + self.btnSendForwards.setSizePolicy(sizePolicy) + self.btnSendForwards.setMaximumSize(QtCore.QSize(25, 25)) + self.btnSendForwards.setBaseSize(QtCore.QSize(32, 32)) + self.btnSendForwards.setText("") + icon21 = QtGui.QIcon() + icon21.addPixmap(QtGui.QPixmap(":/icons/chevron-with-circle-right.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnSendForwards.setIcon(icon21) + self.btnSendForwards.setFlat(True) + self.btnSendForwards.setObjectName("btnSendForwards") + self.horizontalLayout.addWidget(self.btnSendForwards) + spacerItem4 = QtWidgets.QSpacerItem(40, 25, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem4) + self.btnUndo.raise_() + self.btnRedo.raise_() + self.btnLoadFile.raise_() + self.btnSave.raise_() + self.btnViewCode.raise_() + self.btnQuickScreenshot.raise_() + self.btnDrawAxes.raise_() + self.btnDrawGrid.raise_() + self.btnSetZoom.raise_() + self.btnPanCenter.raise_() + self.btnResetPan.raise_() + self.btnAlignX.raise_() + self.btnAlignY.raise_() + self.btnPan.raise_() + self.btnTranslate.raise_() + self.btnScale.raise_() + self.btnCustTransform.raise_() + self.btnSendBackwards.raise_() + self.btnSendForwards.raise_() + self.comboAnchor.raise_() + self.btnToggleVisible.raise_() + self.btnAnchor.raise_() + self.btnRotate.raise_() + self.btnSelectEdit.raise_() + self.btnDeleteMode.raise_() + self.verticalLayout.addWidget(self.menuFrame) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setSpacing(4) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setSpacing(3) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.addOption = QtWidgets.QHBoxLayout() + self.addOption.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) + self.addOption.setContentsMargins(6, -1, -1, 0) + self.addOption.setSpacing(6) + self.addOption.setObjectName("addOption") + self.btnFill = QtWidgets.QPushButton(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnFill.sizePolicy().hasHeightForWidth()) + self.btnFill.setSizePolicy(sizePolicy) + self.btnFill.setMaximumSize(QtCore.QSize(32, 32)) + self.btnFill.setBaseSize(QtCore.QSize(32, 32)) + self.btnFill.setAutoFillBackground(False) + self.btnFill.setStyleSheet("") + self.btnFill.setText("") + icon22 = QtGui.QIcon() + icon22.addPixmap(QtGui.QPixmap(":/icons/bucket.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon22.addPixmap(QtGui.QPixmap(":/icons/filledbucket.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.btnFill.setIcon(icon22) + self.btnFill.setCheckable(True) + self.btnFill.setDefault(False) + self.btnFill.setFlat(True) + self.btnFill.setObjectName("btnFill") + self.addOption.addWidget(self.btnFill) + self.addOptionLayout = QtWidgets.QGridLayout() + self.addOptionLayout.setSpacing(6) + self.addOptionLayout.setObjectName("addOptionLayout") + self.addOption.addLayout(self.addOptionLayout) + spacerItem5 = QtWidgets.QSpacerItem(40, 35, QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) + self.addOption.addItem(spacerItem5) + self.label = QtWidgets.QLabel(self.mainWidget) + self.label.setObjectName("label") + self.addOption.addWidget(self.label) + self.txtLineWidth = QtWidgets.QLineEdit(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.txtLineWidth.sizePolicy().hasHeightForWidth()) + self.txtLineWidth.setSizePolicy(sizePolicy) + self.txtLineWidth.setMaximumSize(QtCore.QSize(75, 16777215)) + self.txtLineWidth.setObjectName("txtLineWidth") + self.addOption.addWidget(self.txtLineWidth) + self.frameCurrColor = QtWidgets.QFrame(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frameCurrColor.sizePolicy().hasHeightForWidth()) + self.frameCurrColor.setSizePolicy(sizePolicy) + self.frameCurrColor.setMinimumSize(QtCore.QSize(15, 15)) + self.frameCurrColor.setAutoFillBackground(False) + self.frameCurrColor.setStyleSheet("QFrame{ \n" +"padding: 4.0;\n" +"border-radius: 3.0; \n" +"background: rgb(0, 0, 0)\n" +"}") + self.frameCurrColor.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frameCurrColor.setFrameShadow(QtWidgets.QFrame.Sunken) + self.frameCurrColor.setObjectName("frameCurrColor") + self.addOption.addWidget(self.frameCurrColor) + self.btnSelectColor = QtWidgets.QPushButton(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnSelectColor.sizePolicy().hasHeightForWidth()) + self.btnSelectColor.setSizePolicy(sizePolicy) + self.btnSelectColor.setMaximumSize(QtCore.QSize(25, 25)) + self.btnSelectColor.setBaseSize(QtCore.QSize(32, 32)) + self.btnSelectColor.setAutoFillBackground(False) + self.btnSelectColor.setStyleSheet("") + self.btnSelectColor.setText("") + icon23 = QtGui.QIcon() + icon23.addPixmap(QtGui.QPixmap(":/icons/android-color-palette.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnSelectColor.setIcon(icon23) + self.btnSelectColor.setDefault(False) + self.btnSelectColor.setFlat(True) + self.btnSelectColor.setObjectName("btnSelectColor") + self.addOption.addWidget(self.btnSelectColor) + self.verticalLayout_2.addLayout(self.addOption) + self.horizontalLayout_7 = QtWidgets.QHBoxLayout() + self.horizontalLayout_7.setContentsMargins(-1, 6, -1, -1) + self.horizontalLayout_7.setSpacing(6) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.formFrame = QtWidgets.QFrame(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.formFrame.sizePolicy().hasHeightForWidth()) + self.formFrame.setSizePolicy(sizePolicy) + self.formFrame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.formFrame.setFrameShadow(QtWidgets.QFrame.Plain) + self.formFrame.setLineWidth(0) + self.formFrame.setObjectName("formFrame") + self.formLayout = QtWidgets.QFormLayout(self.formFrame) + self.formLayout.setContentsMargins(0, 0, 0, 0) + self.formLayout.setSpacing(0) + self.formLayout.setObjectName("formLayout") + self.btnOpenPoly = QtWidgets.QPushButton(self.formFrame) + self.btnOpenPoly.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnOpenPoly.sizePolicy().hasHeightForWidth()) + self.btnOpenPoly.setSizePolicy(sizePolicy) + self.btnOpenPoly.setMaximumSize(QtCore.QSize(32, 32)) + self.btnOpenPoly.setText("") + icon24 = QtGui.QIcon() + icon24.addPixmap(QtGui.QPixmap(":/icons/openpolygon.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnOpenPoly.setIcon(icon24) + self.btnOpenPoly.setFlat(True) + self.btnOpenPoly.setObjectName("btnOpenPoly") + self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.btnOpenPoly) + self.btnClosedPoly = QtWidgets.QPushButton(self.formFrame) + self.btnClosedPoly.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnClosedPoly.sizePolicy().hasHeightForWidth()) + self.btnClosedPoly.setSizePolicy(sizePolicy) + self.btnClosedPoly.setMaximumSize(QtCore.QSize(32, 32)) + self.btnClosedPoly.setText("") + icon25 = QtGui.QIcon() + icon25.addPixmap(QtGui.QPixmap(":/icons/closedpolygon.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnClosedPoly.setIcon(icon25) + self.btnClosedPoly.setFlat(True) + self.btnClosedPoly.setObjectName("btnClosedPoly") + self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.btnClosedPoly) + self.btnOpenCurve = QtWidgets.QPushButton(self.formFrame) + self.btnOpenCurve.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnOpenCurve.sizePolicy().hasHeightForWidth()) + self.btnOpenCurve.setSizePolicy(sizePolicy) + self.btnOpenCurve.setMaximumSize(QtCore.QSize(32, 32)) + self.btnOpenCurve.setText("") + icon26 = QtGui.QIcon() + icon26.addPixmap(QtGui.QPixmap(":/icons/opencurve.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnOpenCurve.setIcon(icon26) + self.btnOpenCurve.setFlat(True) + self.btnOpenCurve.setObjectName("btnOpenCurve") + self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.btnOpenCurve) + self.btnClosedCurve = QtWidgets.QPushButton(self.formFrame) + self.btnClosedCurve.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnClosedCurve.sizePolicy().hasHeightForWidth()) + self.btnClosedCurve.setSizePolicy(sizePolicy) + self.btnClosedCurve.setMaximumSize(QtCore.QSize(32, 32)) + self.btnClosedCurve.setText("") + icon27 = QtGui.QIcon() + icon27.addPixmap(QtGui.QPixmap(":/icons/closedcurve.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnClosedCurve.setIcon(icon27) + self.btnClosedCurve.setFlat(True) + self.btnClosedCurve.setObjectName("btnClosedCurve") + self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.btnClosedCurve) + self.btnAddPoly = QtWidgets.QPushButton(self.formFrame) + self.btnAddPoly.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnAddPoly.sizePolicy().hasHeightForWidth()) + self.btnAddPoly.setSizePolicy(sizePolicy) + self.btnAddPoly.setMaximumSize(QtCore.QSize(32, 32)) + self.btnAddPoly.setText("") + icon28 = QtGui.QIcon() + icon28.addPixmap(QtGui.QPixmap(":/icons/triangle-stroked-15.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnAddPoly.setIcon(icon28) + self.btnAddPoly.setFlat(True) + self.btnAddPoly.setObjectName("btnAddPoly") + self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.btnAddPoly) + self.btnAddCircle = QtWidgets.QPushButton(self.formFrame) + self.btnAddCircle.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnAddCircle.sizePolicy().hasHeightForWidth()) + self.btnAddCircle.setSizePolicy(sizePolicy) + self.btnAddCircle.setMaximumSize(QtCore.QSize(32, 32)) + self.btnAddCircle.setText("") + icon29 = QtGui.QIcon() + icon29.addPixmap(QtGui.QPixmap(":/icons/circle.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnAddCircle.setIcon(icon29) + self.btnAddCircle.setFlat(True) + self.btnAddCircle.setObjectName("btnAddCircle") + self.formLayout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.btnAddCircle) + self.btnAddLabel = QtWidgets.QPushButton(self.formFrame) + self.btnAddLabel.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnAddLabel.sizePolicy().hasHeightForWidth()) + self.btnAddLabel.setSizePolicy(sizePolicy) + self.btnAddLabel.setMaximumSize(QtCore.QSize(32, 32)) + self.btnAddLabel.setText("") + icon30 = QtGui.QIcon() + icon30.addPixmap(QtGui.QPixmap(":/icons/text.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnAddLabel.setIcon(icon30) + self.btnAddLabel.setFlat(True) + self.btnAddLabel.setObjectName("btnAddLabel") + self.formLayout.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.btnAddLabel) + self.horizontalLayout_7.addWidget(self.formFrame) + self.imgFrame = QtWidgets.QFrame(self.mainWidget) + self.imgFrame.setMinimumSize(QtCore.QSize(0, 6)) + self.imgFrame.setMouseTracking(True) + self.imgFrame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.imgFrame.setFrameShadow(QtWidgets.QFrame.Raised) + self.imgFrame.setObjectName("imgFrame") + self.gridLayout = QtWidgets.QGridLayout(self.imgFrame) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setSpacing(0) + self.gridLayout.setObjectName("gridLayout") + self.imgLabel = QtWidgets.QLabel(self.imgFrame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.imgLabel.sizePolicy().hasHeightForWidth()) + self.imgLabel.setSizePolicy(sizePolicy) + self.imgLabel.setMouseTracking(True) + self.imgLabel.setFrameShape(QtWidgets.QFrame.Panel) + self.imgLabel.setText("") + self.imgLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) + self.imgLabel.setObjectName("imgLabel") + self.gridLayout.addWidget(self.imgLabel, 0, 1, 1, 1) + self.horizontalLayout_7.addWidget(self.imgFrame) + self.verticalLayout_2.addLayout(self.horizontalLayout_7) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) + self.horizontalLayout_3.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout_3.setSpacing(0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.btnTogglePython = QtWidgets.QPushButton(self.mainWidget) + self.btnTogglePython.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnTogglePython.sizePolicy().hasHeightForWidth()) + self.btnTogglePython.setSizePolicy(sizePolicy) + self.btnTogglePython.setText("") + icon31 = QtGui.QIcon() + icon31.addPixmap(QtGui.QPixmap(":/icons/social-python.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnTogglePython.setIcon(icon31) + self.btnTogglePython.setIconSize(QtCore.QSize(16, 16)) + self.btnTogglePython.setCheckable(True) + self.btnTogglePython.setFlat(True) + self.btnTogglePython.setObjectName("btnTogglePython") + self.horizontalLayout_3.addWidget(self.btnTogglePython) + self.txtTerminalPrompt = QtWidgets.QLineEdit(self.mainWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.txtTerminalPrompt.sizePolicy().hasHeightForWidth()) + self.txtTerminalPrompt.setSizePolicy(sizePolicy) + self.txtTerminalPrompt.setObjectName("txtTerminalPrompt") + self.horizontalLayout_3.addWidget(self.txtTerminalPrompt) + self.btnEnterCommand = QtWidgets.QPushButton(self.mainWidget) + self.btnEnterCommand.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.btnEnterCommand.sizePolicy().hasHeightForWidth()) + self.btnEnterCommand.setSizePolicy(sizePolicy) + self.btnEnterCommand.setText("") + icon32 = QtGui.QIcon() + icon32.addPixmap(QtGui.QPixmap(":/icons/subdirectory-left.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.btnEnterCommand.setIcon(icon32) + self.btnEnterCommand.setFlat(True) + self.btnEnterCommand.setObjectName("btnEnterCommand") + self.horizontalLayout_3.addWidget(self.btnEnterCommand) + self.verticalLayout_2.addLayout(self.horizontalLayout_3) + self.horizontalLayout_2.addLayout(self.verticalLayout_2) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.horizontalLayout_4.addWidget(self.mainWidget) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1030, 28)) + self.menubar.setObjectName("menubar") + self.menuFIle = QtWidgets.QMenu(self.menubar) + self.menuFIle.setObjectName("menuFIle") + self.menuEdit = QtWidgets.QMenu(self.menubar) + self.menuEdit.setObjectName("menuEdit") + self.menuOptions = QtWidgets.QMenu(self.menubar) + self.menuOptions.setObjectName("menuOptions") + self.menuHelp = QtWidgets.QMenu(self.menubar) + self.menuHelp.setObjectName("menuHelp") + self.menuTools = QtWidgets.QMenu(self.menubar) + self.menuTools.setObjectName("menuTools") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setSizeGripEnabled(False) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.actionAbout = QtWidgets.QAction(MainWindow) + self.actionAbout.setObjectName("actionAbout") + self.actionManual = QtWidgets.QAction(MainWindow) + self.actionManual.setObjectName("actionManual") + self.actionSettings = QtWidgets.QAction(MainWindow) + self.actionSettings.setObjectName("actionSettings") + self.actionPause = QtWidgets.QAction(MainWindow) + self.actionPause.setObjectName("actionPause") + self.actionSaveAs = QtWidgets.QAction(MainWindow) + self.actionSaveAs.setObjectName("actionSaveAs") + self.actionEnterCommand = QtWidgets.QAction(MainWindow) + self.actionEnterCommand.setObjectName("actionEnterCommand") + self.actionQuit = QtWidgets.QAction(MainWindow) + self.actionQuit.setObjectName("actionQuit") + self.actionUndo = QtWidgets.QAction(MainWindow) + self.actionUndo.setEnabled(False) + self.actionUndo.setObjectName("actionUndo") + self.actionRedo = QtWidgets.QAction(MainWindow) + self.actionRedo.setEnabled(False) + self.actionRedo.setObjectName("actionRedo") + self.actionShow_Grid = QtWidgets.QAction(MainWindow) + self.actionShow_Grid.setObjectName("actionShow_Grid") + self.actionShow_Local_Grid = QtWidgets.QAction(MainWindow) + self.actionShow_Local_Grid.setObjectName("actionShow_Local_Grid") + self.actionTransform = QtWidgets.QAction(MainWindow) + self.actionTransform.setObjectName("actionTransform") + self.actionExportAsymptote = QtWidgets.QAction(MainWindow) + self.actionExportAsymptote.setObjectName("actionExportAsymptote") + self.actionSave = QtWidgets.QAction(MainWindow) + self.actionSave.setObjectName("actionSave") + self.actionOpen = QtWidgets.QAction(MainWindow) + self.actionOpen.setObjectName("actionOpen") + self.menuFIle.addAction(self.actionOpen) + self.menuFIle.addAction(self.actionSave) + self.menuFIle.addAction(self.actionSaveAs) + self.menuFIle.addAction(self.actionExportAsymptote) + self.menuFIle.addSeparator() + self.menuFIle.addAction(self.actionQuit) + self.menuEdit.addAction(self.actionUndo) + self.menuEdit.addAction(self.actionRedo) + self.menuEdit.addSeparator() + self.menuOptions.addAction(self.actionSettings) + self.menuHelp.addAction(self.actionManual) + self.menuHelp.addAction(self.actionAbout) + self.menuTools.addAction(self.actionEnterCommand) + self.menubar.addAction(self.menuFIle.menuAction()) + self.menubar.addAction(self.menuEdit.menuAction()) + self.menubar.addAction(self.menuOptions.menuAction()) + self.menubar.addAction(self.menuTools.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) + + self.retranslateUi(MainWindow) + self.comboAnchor.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "xasy")) + self.btnUndo.setToolTip(_translate("MainWindow", "Undo")) + self.btnRedo.setToolTip(_translate("MainWindow", "Redo")) + self.btnLoadFile.setToolTip(_translate("MainWindow", "<html><head/><body><p>Open file</p></body></html>")) + self.btnSave.setToolTip(_translate("MainWindow", "<html><head/><body><p>Save file</p></body></html>")) + self.btnViewCode.setToolTip(_translate("MainWindow", "<html><head/><body><p>Edit code</p></body></html>")) + self.btnQuickScreenshot.setToolTip(_translate("MainWindow", "<html><head/><body><p>Screenshot</p></body></html>")) + self.btnDrawAxes.setToolTip(_translate("MainWindow", "<html><head/><body><p>Toggle display axes</p></body></html>")) + self.btnDrawGrid.setToolTip(_translate("MainWindow", "<html><head/><body><p>Toggle grid</p></body></html>")) + self.btnSetZoom.setToolTip(_translate("MainWindow", "Synchronize with Asymptote")) + self.btnPanCenter.setToolTip(_translate("MainWindow", "<html><head/><body><p>Center</p></body></html>")) + self.btnResetPan.setToolTip(_translate("MainWindow", "<html><head/><body><p>Center about origin</p></body></html>")) + self.btnAlignX.setToolTip(_translate("MainWindow", "<html><head/><body><p>Lock transform to X axis</p></body></html>")) + self.btnAlignX.setText(_translate("MainWindow", "X")) + self.btnAlignY.setToolTip(_translate("MainWindow", "<html><head/><body><p>Lock transform to Y axis</p></body></html>")) + self.btnAlignY.setText(_translate("MainWindow", "Y")) + self.btnSelectEdit.setToolTip(_translate("MainWindow", "<html><head/><body><p>Bézier editor</p></body></html>")) + self.btnDeleteMode.setToolTip(_translate("MainWindow", "<html><head/><body><p>Delete</p></body></html>")) + self.btnPan.setToolTip(_translate("MainWindow", "Pan")) + self.btnTranslate.setToolTip(_translate("MainWindow", "Translate")) + self.btnScale.setToolTip(_translate("MainWindow", "Scale")) + self.btnRotate.setToolTip(_translate("MainWindow", "Rotate")) + self.btnAnchor.setToolTip(_translate("MainWindow", "<html><head/><body><p>Set custom anchor</p></body></html>")) + self.comboAnchor.setToolTip(_translate("MainWindow", "<html><head/><body><p>Anchor</p></body></html>")) + self.comboAnchor.setCurrentText(_translate("MainWindow", "Origin")) + self.comboAnchor.setItemText(0, _translate("MainWindow", "Origin")) + self.comboAnchor.setItemText(1, _translate("MainWindow", "Center")) + self.comboAnchor.setItemText(2, _translate("MainWindow", "Top Left")) + self.comboAnchor.setItemText(3, _translate("MainWindow", "Top Right")) + self.comboAnchor.setItemText(4, _translate("MainWindow", "Bottom Right")) + self.comboAnchor.setItemText(5, _translate("MainWindow", "Bottom Left")) + self.comboAnchor.setItemText(6, _translate("MainWindow", "Custom")) + self.btnCustTransform.setToolTip(_translate("MainWindow", "<html><head/><body><p><br/></p></body></html>")) + self.btnSendBackwards.setToolTip(_translate("MainWindow", "<html><head/><body><p><br/></p></body></html>")) + self.btnSendForwards.setToolTip(_translate("MainWindow", "Translate")) + self.btnFill.setToolTip(_translate("MainWindow", "<html><head/><body><p>Toggle fill/outline</p></body></html>")) + self.label.setText(_translate("MainWindow", "Line Width:")) + self.txtLineWidth.setToolTip(_translate("MainWindow", "<html><head/><body><p>Current pen width</p></body></html>")) + self.frameCurrColor.setToolTip(_translate("MainWindow", "<html><head/><body><p>Current pen color</p></body></html>")) + self.btnSelectColor.setToolTip(_translate("MainWindow", "<html><head/><body><p>Set color</p></body></html>")) + self.btnOpenPoly.setToolTip(_translate("MainWindow", "<html><head/><body><p>Open polygon</p></body></html>")) + self.btnClosedPoly.setToolTip(_translate("MainWindow", "<html><head/><body><p>Closed polygon</p></body></html>")) + self.btnOpenCurve.setToolTip(_translate("MainWindow", "<html><head/><body><p>Open Bézier curve</p></body></html>")) + self.btnClosedCurve.setToolTip(_translate("MainWindow", "<html><head/><body><p>Closed Bézier curve</p></body></html>")) + self.btnAddPoly.setToolTip(_translate("MainWindow", "<html><head/><body><p>Regular polygon</p></body></html>")) + self.btnAddCircle.setToolTip(_translate("MainWindow", "<html><head/><body><p>Circle</p></body></html>")) + self.btnAddLabel.setToolTip(_translate("MainWindow", "<html><head/><body><p>Text</p></body></html>")) + self.menuFIle.setTitle(_translate("MainWindow", "&File")) + self.menuEdit.setTitle(_translate("MainWindow", "&Edit")) + self.menuOptions.setTitle(_translate("MainWindow", "Optio&ns")) + self.menuHelp.setTitle(_translate("MainWindow", "&Help")) + self.menuTools.setTitle(_translate("MainWindow", "&Tools")) + self.actionAbout.setText(_translate("MainWindow", "&About")) + self.actionManual.setText(_translate("MainWindow", "&Manual")) + self.actionSettings.setText(_translate("MainWindow", "&Settings")) + self.actionPause.setText(_translate("MainWindow", "Pause ")) + self.actionSaveAs.setText(_translate("MainWindow", "&Save As")) + self.actionEnterCommand.setText(_translate("MainWindow", "&Enter Command")) + self.actionQuit.setText(_translate("MainWindow", "&Quit")) + self.actionUndo.setText(_translate("MainWindow", "&Undo")) + self.actionRedo.setText(_translate("MainWindow", "&Redo")) + self.actionShow_Grid.setText(_translate("MainWindow", "&Show Grid")) + self.actionShow_Local_Grid.setText(_translate("MainWindow", "Show &Local Grid")) + self.actionTransform.setText(_translate("MainWindow", "&Transform")) + self.actionExportAsymptote.setText(_translate("MainWindow", "Export")) + self.actionSave.setText(_translate("MainWindow", "Save")) + self.actionOpen.setText(_translate("MainWindow", "Open")) + +import icons_rc diff --git a/Master/texmf-dist/asymptote/GUI/res/icons.qrc b/Master/texmf-dist/asymptote/GUI/res/icons.qrc new file mode 100644 index 00000000000..50928ef9b74 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons.qrc @@ -0,0 +1,45 @@ +<RCC> + <qresource prefix=""> + <file>icons/android-arrow-back.svg</file> + <file>icons/android-arrow-forward.svg</file> + <file>icons/android-folder-open.svg</file> + <file>icons/save.svg</file> + <file>icons/code.svg</file> + <file>icons/android-camera.svg</file> + <file>icons/plus-round.svg</file> + <file>icons/grid.svg</file> + <file>icons/magnifying-glass.svg</file> + <file>icons/center.svg</file> + <file>icons/centerorigin.svg</file> + <file>icons/edit.svg</file> + <file>icons/android-delete.svg</file> + <file>icons/android-hand.svg</file> + <file>icons/arrow-move.svg</file> + <file>icons/arrow-resize.svg</file> + <file>icons/android-refresh.svg</file> + <file>icons/anchor.svg</file> + <file>icons/eye.svg</file> + <file>icons/android-expand.svg</file> + <file>icons/chevron-with-circle-left.svg</file> + <file>icons/chevron-with-circle-right.svg</file> + <file>icons/bucket.svg</file> + <file>icons/filledbucket.svg</file> + <file>icons/android-color-palette.svg</file> + <file>icons/openpolygon.svg</file> + <file>icons/closedpolygon.svg</file> + <file>icons/opencurve.svg</file> + <file>icons/closedcurve.svg</file> + <file>icons/triangle-stroked-15.svg</file> + <file>icons/circle.svg</file> + <file>icons/text.svg</file> + <file>icons/social-python.svg</file> + <file>icons/subdirectory-left.svg</file> + <file>icons/android-done.svg</file> + <file>icons/android-close.svg</file> + <file>icons/check.svg</file> + <file>icons/android-radio-button-on.svg</file> + <file>icons/android-radio-button-off.svg</file> + <file>icons/android-locate.svg</file> + <file>icons/close-round.svg</file> + </qresource> +</RCC> diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/anchor.svg b/Master/texmf-dist/asymptote/GUI/res/icons/anchor.svg new file mode 100644 index 00000000000..886aadebf94 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/anchor.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M4.34 15.66A7.97 7.97 0 0 0 9 17.94V10H5V8h4V5.83a3 3 0 1 1 2 0V8h4v2h-4v7.94a7.97 7.97 0 0 0 4.66-2.28l-1.42-1.42h5.66l-2.83 2.83a10 10 0 0 1-14.14 0L.1 14.24h5.66l-1.42 1.42zM10 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-arrow-back.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-arrow-back.svg new file mode 100644 index 00000000000..79cfe814a1b --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-arrow-back.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_8_">
+ <g>
+ <path d="M427,234.625H167.296l119.702-119.702L256,85L85,256l171,171l29.922-29.924L167.296,277.375H427V234.625z"/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-arrow-forward.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-arrow-forward.svg new file mode 100644 index 00000000000..d63794dcc0a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-arrow-forward.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_8_">
+ <g>
+ <path d="M85,277.375h259.704L225.002,397.077L256,427l171-171L256,85l-29.922,29.924l118.626,119.701H85V277.375z"/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-camera.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-camera.svg new file mode 100644 index 00000000000..73aeaabe49c --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-camera.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+ <circle cx="256" cy="280" r="63"/>
+ <path d="M440,96h-88l-32-32H192l-32,32H72c-22.092,0-40,17.908-40,40v272c0,22.092,17.908,40,40,40h368c22.092,0,40-17.908,40-40
+ V136C480,113.908,462.092,96,440,96z M256,392c-61.855,0-112-50.145-112-112s50.145-112,112-112s112,50.145,112,112
+ S317.855,392,256,392z"/>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-close.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-close.svg new file mode 100644 index 00000000000..aee0f9f630e --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-close.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_5_">
+ <g>
+ <polygon points="405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798
+ 375.202,405 405,375.202 285.798,256 "/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-color-palette.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-color-palette.svg new file mode 100644 index 00000000000..d700fe6b9d2 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-color-palette.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_12_">
+ <g>
+ <path d="M256,64C150.401,64,64,150.401,64,256c0,105.604,86.401,192,192,192c18.136,0,32-13.864,32-32
+ c0-8.531-3.198-16-8.531-21.333c-5.333-5.334-8.531-12.803-8.531-21.334c0-18.135,13.864-32,32-32h38.396
+ c58.667,0,106.667-48,106.667-106.666C448,140.802,361.604,64,256,64z M138.667,256c-18.136,0-32-13.864-32-32s13.864-32,32-32
+ c18.135,0,32,13.864,32,32S156.802,256,138.667,256z M202.667,170.667c-18.136,0-32-13.865-32-32c0-18.136,13.864-32,32-32
+ c18.135,0,32,13.864,32,32C234.667,156.802,220.802,170.667,202.667,170.667z M309.333,170.667c-18.135,0-32-13.865-32-32
+ c0-18.136,13.865-32,32-32c18.136,0,32,13.864,32,32C341.333,156.802,327.469,170.667,309.333,170.667z M373.333,256
+ c-18.135,0-32-13.864-32-32s13.865-32,32-32c18.136,0,32,13.864,32,32S391.469,256,373.333,256z"/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-delete.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-delete.svg new file mode 100644 index 00000000000..49cfa5ade43 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-delete.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+ <path d="M128,405.429C128,428.846,147.198,448,170.667,448h170.667C364.802,448,384,428.846,384,405.429V160H128V405.429z M416,96
+ h-80l-26.785-32H202.786L176,96H96v32h320V96z"/>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-done.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-done.svg new file mode 100644 index 00000000000..4bcb946f058 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-done.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_1_">
+ <g>
+ <g>
+ <polygon points="186.301,339.893 96,249.461 64,279.968 186.301,402 448,140.506 416,110 "/>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-expand.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-expand.svg new file mode 100644 index 00000000000..51be003c07a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-expand.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+ <polygon points="396.795,396.8 320,396.8 320,448 448,448 448,320 396.795,320 "/>
+ <polygon points="396.8,115.205 396.8,192 448,192 448,64 320,64 320,115.205 "/>
+ <polygon points="115.205,115.2 192,115.2 192,64 64,64 64,192 115.205,192 "/>
+ <polygon points="115.2,396.795 115.2,320 64,320 64,448 192,448 192,396.795 "/>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-folder-open.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-folder-open.svg new file mode 100644 index 00000000000..fa77fea2154 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-folder-open.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<path d="M437.334,144H256.006l-42.668-48H74.666C51.197,96,32,115.198,32,138.667v234.666C32,396.802,51.197,416,74.666,416h362.668
+ C460.803,416,480,396.802,480,373.333V186.667C480,163.198,460.803,144,437.334,144z M448,373.333
+ c0,5.782-4.885,10.667-10.666,10.667H74.666C68.884,384,64,379.115,64,373.333V176h373.334c5.781,0,10.666,4.885,10.666,10.667
+ V373.333z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-hand.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-hand.svg new file mode 100644 index 00000000000..5035d875be1 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-hand.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<path d="M450.679,273.5c-14.585-14.577-36.054-15.89-50.639-1.312l-41.687,41.664c-10.852,10.836-23.93,10.859-31.564,1.852
+ c-5.057-5.968-3.061-24.374-1.644-36.049l20.907-171.849c1.867-15.353-9.07-30.185-24.43-32.051
+ c-15.358-1.867-29.322,9.939-31.191,25.289L267.37,236.021c-1.205,3.358-3.79,3.938-4.081-0.582L255.44,60
+ c0-15.465-12.542-28-28.014-28c-15.473,0-28.015,12.535-28.015,28l-0.552,176.752c0.146,2.04-1.604,2.624-1.92,0.294L172.016,99.077
+ c-2.75-15.219-17.323-26.203-32.548-23.453c-15.227,2.748-25.339,18.187-22.591,33.403l22.193,161.455
+ c0.023,2.872-0.941,4.513-2.308,0.831l-33.109-88.517c-5.18-14.572-21.196-23.065-35.776-17.889
+ c-14.579,5.177-22.201,22.061-17.023,36.631l58.042,189.625c0.303,1.046,0.624,2.085,0.953,3.118l0.121,0.39
+ c0.011,0.031,0.025,0.058,0.035,0.088C126.079,444.233,172.57,480,227.427,480c35.116,0,71.591-12.378,99.357-33.672
+ c0.001,0,0.003-0.002,0.003-0.002c29.99-18.051,126.071-121.347,126.071-121.347C467.445,310.402,465.266,288.08,450.679,273.5z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-locate.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-locate.svg new file mode 100644 index 00000000000..e2dd098cbd3 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-locate.svg @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon">
+ <g>
+ <path d="M256,176c-44.004,0-80.001,36-80.001,80c0,44.004,35.997,80,80.001,80c44.005,0,79.999-35.996,79.999-80
+ C335.999,212,300.005,176,256,176z M446.938,234.667c-9.605-88.531-81.074-160-169.605-169.599V32h-42.666v33.067
+ c-88.531,9.599-160,81.068-169.604,169.599H32v42.667h33.062c9.604,88.531,81.072,160,169.604,169.604V480h42.666v-33.062
+ c88.531-9.604,160-81.073,169.605-169.604H480v-42.667H446.938z M256,405.333c-82.137,0-149.334-67.198-149.334-149.333
+ c0-82.136,67.197-149.333,149.334-149.333c82.135,0,149.332,67.198,149.332,149.333C405.332,338.135,338.135,405.333,256,405.333z
+ "/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-radio-button-off.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-radio-button-off.svg new file mode 100644 index 00000000000..d36fa657c03 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-radio-button-off.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_20_">
+ <g>
+ <path d="M256,48C141.601,48,48,141.601,48,256s93.601,208,208,208s208-93.601,208-208S370.399,48,256,48z M256,422.399
+ c-91.518,0-166.399-74.882-166.399-166.399S164.482,89.6,256,89.6S422.4,164.482,422.4,256S347.518,422.399,256,422.399z"/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-radio-button-on.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-radio-button-on.svg new file mode 100644 index 00000000000..1f7f54c0f24 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-radio-button-on.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g id="Icon_21_">
+ <g>
+ <path d="M256,152c-57.2,0-104,46.8-104,104s46.8,104,104,104s104-46.8,104-104S313.2,152,256,152z M256,48
+ C141.601,48,48,141.601,48,256s93.601,208,208,208s208-93.601,208-208S370.399,48,256,48z M256,422.4
+ c-91.518,0-166.4-74.883-166.4-166.4S164.482,89.6,256,89.6S422.4,164.482,422.4,256S347.518,422.4,256,422.4z"/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/android-refresh.svg b/Master/texmf-dist/asymptote/GUI/res/icons/android-refresh.svg new file mode 100644 index 00000000000..066f7d22c6b --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/android-refresh.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+ <path d="M256,388c-72.597,0-132-59.405-132-132c0-72.601,59.403-132,132-132c36.3,0,69.299,15.4,92.406,39.601L278,234h154V80
+ l-51.698,51.702C348.406,99.798,304.406,80,256,80c-96.797,0-176,79.203-176,176s78.094,176,176,176
+ c81.045,0,148.287-54.134,169.401-128H378.85C360.105,353.561,311.712,388,256,388z"/>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/arrow-move.svg b/Master/texmf-dist/asymptote/GUI/res/icons/arrow-move.svg new file mode 100644 index 00000000000..1d4263f45a2 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/arrow-move.svg @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<polygon points="480,256 384,160 384,236 276,236 276,128 352,128 256,32 160,128 236,128 236,236 128,236 128,160 32,256 128,352
+ 128,276 236,276 236,384 160,384 256,480 352,384 275.8,384 275.4,275.5 384,275.8 384,352 "/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/arrow-resize.svg b/Master/texmf-dist/asymptote/GUI/res/icons/arrow-resize.svg new file mode 100644 index 00000000000..a9c3b3cc140 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/arrow-resize.svg @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<polygon points="288,96 337.9,145.9 274,209.7 274,209.7 145.9,337.9 96,288 96,416 224,416 174.1,366.1 357.4,182.9 366.1,174.1
+ 416,224 416,96 "/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/bucket.svg b/Master/texmf-dist/asymptote/GUI/res/icons/bucket.svg new file mode 100644 index 00000000000..8898aba6091 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/bucket.svg @@ -0,0 +1,14 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='24pt' version='1.1' viewBox='56.4094 53.8583 18.4375 24' width='18.4375pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M 8.37504 -11.1667C 8.37504 -12.7085 4.62541 -13.9584 0 -13.9584C -4.62541 -13.9584 -8.37504 -12.7085 -8.37504 -11.1667C -8.37504 -9.62492 -4.62541 -8.37504 0 -8.37504C 4.62541 -8.37504 8.37504 -9.62492 8.37504 -11.1667Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M -8.37504 -11.1667L -8.37504 -0L -8.37504 -1.85963e-15C -8.37504 -1.23975e-15 -8.37504 -6.19877e-16 -8.37504 -0C -8.37504 1.5418 -4.62541 2.79168 0 2.79168C 4.62541 2.79168 8.37504 1.5418 8.37504 0L 8.37504 0L 8.37504 -11.1667' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M 8.37504 -11.1667C 8.37504 -15.7921 4.62541 -19.5418 0 -19.5418C -4.62541 -19.5418 -8.37504 -15.7921 -8.37504 -11.1667' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/center.svg b/Master/texmf-dist/asymptote/GUI/res/icons/center.svg new file mode 100644 index 00000000000..15b77f5a644 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/center.svg @@ -0,0 +1,11 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='18.75pt' version='1.1' viewBox='56.4094 53.8583 18.75 18.75' width='18.75pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 65.7844 63.2333)'> +<path d='M -8.65734 8.65734L 8.65734 8.65734L 8.65734 -8.65734L -8.65734 -8.65734L -8.65734 8.65734Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.7844 63.2333)'> +<path d='M 4.32867 0C 4.32867 -2.39066 2.39066 -4.32867 0 -4.32867C -2.39066 -4.32867 -4.32867 -2.39066 -4.32867 -0C -4.32867 2.39066 -2.39066 4.32867 0 4.32867C 2.39066 4.32867 4.32867 2.39066 4.32867 0Z' fill='#000000'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/centerorigin.svg b/Master/texmf-dist/asymptote/GUI/res/icons/centerorigin.svg new file mode 100644 index 00000000000..253ba7789f3 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/centerorigin.svg @@ -0,0 +1,14 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='18.75pt' version='1.1' viewBox='56.4094 53.8583 18.75 18.75' width='18.75pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 65.7844 63.2333)'> +<path d='M -8.65734 8.65734L 8.65734 8.65734L 8.65734 -8.65734L -8.65734 -8.65734L -8.65734 8.65734Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.7844 63.2333)'> +<path d='M -8.65734 -0L 8.65734 0' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.7844 63.2333)'> +<path d='M 0 8.65734L 0 -8.65734' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.50562'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/check.svg b/Master/texmf-dist/asymptote/GUI/res/icons/check.svg new file mode 100644 index 00000000000..08ee56d87de --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/check.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Check" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M8.294,16.998c-0.435,0-0.847-0.203-1.111-0.553L3.61,11.724c-0.465-0.613-0.344-1.486,0.27-1.951
+ c0.615-0.467,1.488-0.344,1.953,0.27l2.351,3.104l5.911-9.492c0.407-0.652,1.267-0.852,1.921-0.445
+ c0.653,0.406,0.854,1.266,0.446,1.92L9.478,16.34c-0.242,0.391-0.661,0.635-1.12,0.656C8.336,16.998,8.316,16.998,8.294,16.998z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/chevron-with-circle-left.svg b/Master/texmf-dist/asymptote/GUI/res/icons/chevron-with-circle-left.svg new file mode 100644 index 00000000000..57d5c5b2574 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/chevron-with-circle-left.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Chevron_circled_left" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M11.302,6.776c-0.196-0.197-0.515-0.197-0.71,0L7.785,9.641c-0.196,0.199-0.196,0.52,0,0.717l2.807,2.864
+ c0.195,0.199,0.514,0.198,0.71,0c0.196-0.197,0.196-0.518,0-0.717L9,10l2.302-2.506C11.498,7.296,11.498,6.976,11.302,6.776z
+ M10,0.4c-5.302,0-9.6,4.298-9.6,9.6c0,5.303,4.298,9.6,9.6,9.6s9.6-4.297,9.6-9.6C19.6,4.698,15.302,0.4,10,0.4z M10,18.354
+ c-4.615,0-8.354-3.74-8.354-8.354c0-4.614,3.739-8.354,8.354-8.354c4.613,0,8.354,3.74,8.354,8.354
+ C18.354,14.614,14.613,18.354,10,18.354z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/chevron-with-circle-right.svg b/Master/texmf-dist/asymptote/GUI/res/icons/chevron-with-circle-right.svg new file mode 100644 index 00000000000..a48f0376a06 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/chevron-with-circle-right.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Chevron_circled_right" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M11,10L8.698,7.494c-0.196-0.198-0.196-0.519,0-0.718c0.196-0.197,0.515-0.197,0.71,0l2.807,2.864
+ c0.196,0.199,0.196,0.52,0,0.717l-2.807,2.864c-0.195,0.199-0.514,0.198-0.71,0c-0.196-0.197-0.196-0.518,0-0.717L11,10z M10,0.4
+ c5.302,0,9.6,4.298,9.6,9.6c0,5.303-4.298,9.6-9.6,9.6S0.4,15.303,0.4,10C0.4,4.698,4.698,0.4,10,0.4z M10,18.354
+ c4.613,0,8.354-3.74,8.354-8.354c0-4.614-3.741-8.354-8.354-8.354c-4.615,0-8.354,3.74-8.354,8.354
+ C1.645,14.614,5.385,18.354,10,18.354z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/circle.svg b/Master/texmf-dist/asymptote/GUI/res/icons/circle.svg new file mode 100644 index 00000000000..b841c238db5 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/circle.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Circle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M10,0.4C4.698,0.4,0.4,4.698,0.4,10C0.4,15.302,4.698,19.6,10,19.6c5.301,0,9.6-4.298,9.6-9.601
+ C19.6,4.698,15.301,0.4,10,0.4z M10,17.599c-4.197,0-7.6-3.402-7.6-7.6S5.802,2.4,10,2.4c4.197,0,7.601,3.402,7.601,7.6
+ S14.197,17.599,10,17.599z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/close-round.svg b/Master/texmf-dist/asymptote/GUI/res/icons/close-round.svg new file mode 100644 index 00000000000..d8b5554c5c1 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/close-round.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<path d="M437.5,386.6L306.9,256l130.6-130.6c14.1-14.1,14.1-36.8,0-50.9c-14.1-14.1-36.8-14.1-50.9,0L256,205.1L125.4,74.5
+ c-14.1-14.1-36.8-14.1-50.9,0c-14.1,14.1-14.1,36.8,0,50.9L205.1,256L74.5,386.6c-14.1,14.1-14.1,36.8,0,50.9
+ c14.1,14.1,36.8,14.1,50.9,0L256,306.9l130.6,130.6c14.1,14.1,36.8,14.1,50.9,0C451.5,423.4,451.5,400.6,437.5,386.6z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/closedcurve.svg b/Master/texmf-dist/asymptote/GUI/res/icons/closedcurve.svg new file mode 100644 index 00000000000..946ef84df7a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/closedcurve.svg @@ -0,0 +1,20 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='24pt' version='1.1' viewBox='56.4094 53.8583 24 24' width='24pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.7688)'> +<path d='M -9.39118 -0C -9.96861 -3.83004 -8.55706 -7.51944 -6.64057 -10.2171C -0.491089 -18.8733 8.35676 -15.1906 8.67632 -5.52948C 8.836 -0.702008 6.23968 2.94207 3.32028 5.10857C -1.96891 9.03371 -8.26895 7.4437 -9.39118 -0Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.7688)'> +<circle cx='-9.39118' cy='-0' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.7688)'> +<circle cx='-6.64057' cy='-10.2171' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.7688)'> +<circle cx='8.67632' cy='-5.52948' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.7688)'> +<circle cx='3.32028' cy='5.10857' r='3.01125' fill='#000000'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/closedpolygon.svg b/Master/texmf-dist/asymptote/GUI/res/icons/closedpolygon.svg new file mode 100644 index 00000000000..432195d706a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/closedpolygon.svg @@ -0,0 +1,20 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='24pt' version='1.1' viewBox='56.4094 53.8583 24 24' width='24pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<path d='M -9.39118 -0L -6.64057 -12.045L 8.67632 -6.51871L 3.32028 6.0225L -9.39118 -0Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='-9.39118' cy='-0' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='-6.64057' cy='-12.045' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='8.67632' cy='-6.51871' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='3.32028' cy='6.0225' r='3.01125' fill='#000000'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/code.svg b/Master/texmf-dist/asymptote/GUI/res/icons/code.svg new file mode 100644 index 00000000000..33a99fd5d72 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/code.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Code" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M5.719,14.75c-0.236,0-0.474-0.083-0.664-0.252L-0.005,10l5.341-4.748C5.748,4.887,6.38,4.922,6.747,5.335
+ c0.367,0.413,0.33,1.045-0.083,1.412L3.005,10l3.378,3.002c0.413,0.367,0.45,0.999,0.083,1.412
+ C6.269,14.637,5.994,14.75,5.719,14.75z M14.664,14.748L20.005,10l-5.06-4.498c-0.413-0.367-1.045-0.33-1.411,0.083
+ c-0.367,0.413-0.33,1.045,0.083,1.412L16.995,10l-3.659,3.252c-0.413,0.367-0.45,0.999-0.083,1.412C13.45,14.887,13.725,15,14,15
+ C14.236,15,14.474,14.917,14.664,14.748z M9.986,16.165l2-12c0.091-0.545-0.277-1.06-0.822-1.151
+ c-0.547-0.092-1.061,0.277-1.15,0.822l-2,12c-0.091,0.545,0.277,1.06,0.822,1.151C8.892,16.996,8.946,17,9.001,17
+ C9.481,17,9.905,16.653,9.986,16.165z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/edit.svg b/Master/texmf-dist/asymptote/GUI/res/icons/edit.svg new file mode 100644 index 00000000000..a0be3454e12 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/edit.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+
+ <rect x="178.846" y="92.087" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 224.3476 631.1498)" width="128.085" height="354.049"/>
+ <path d="M471.723,88.393l-48.115-48.114c-11.723-11.724-31.558-10.896-44.304,1.85l-45.202,45.203l90.569,90.568l45.202-45.202
+ C482.616,119.952,483.445,100.116,471.723,88.393z"/>
+ <polygon points="64.021,363.252 32,480 148.737,447.979 "/>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/eye.svg b/Master/texmf-dist/asymptote/GUI/res/icons/eye.svg new file mode 100644 index 00000000000..6b64633fce1 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/eye.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<g>
+ <path d="M256,128c-81.9,0-145.7,48.8-224,128c67.4,67.7,124,128,224,128c99.9,0,173.4-76.4,224-126.6
+ C428.2,198.6,354.8,128,256,128z M256,347.3c-49.4,0-89.6-41-89.6-91.3c0-50.4,40.2-91.3,89.6-91.3s89.6,41,89.6,91.3
+ C345.6,306.4,305.4,347.3,256,347.3z"/>
+ <g>
+ <path d="M256,224c0-7.9,2.9-15.1,7.6-20.7c-2.5-0.4-5-0.6-7.6-0.6c-28.8,0-52.3,23.9-52.3,53.3c0,29.4,23.5,53.3,52.3,53.3
+ s52.3-23.9,52.3-53.3c0-2.3-0.2-4.6-0.4-6.9c-5.5,4.3-12.3,6.9-19.8,6.9C270.3,256,256,241.7,256,224z"/>
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/filledbucket.svg b/Master/texmf-dist/asymptote/GUI/res/icons/filledbucket.svg new file mode 100644 index 00000000000..e768fa3d1c6 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/filledbucket.svg @@ -0,0 +1,17 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='24pt' version='1.1' viewBox='56.4094 53.8583 18.4375 24' width='18.4375pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M -8.37504 -5.02502L -8.37504 -0L -8.37504 -1.85963e-15C -8.37504 -1.23975e-15 -8.37504 -6.19877e-16 -8.37504 -0C -8.37504 1.5418 -4.62541 2.79168 0 2.79168C 4.62541 2.79168 8.37504 1.5418 8.37504 0L 8.37504 0L 8.37504 -5.02502L 8.37504 -10.05C 8.37504 -11.5918 4.62541 -12.8417 0 -12.8417C -4.62541 -12.8417 -8.37504 -11.5918 -8.37504 -10.05L -8.37504 -5.02502Z' fill='#808080'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M 8.37504 -11.1667C 8.37504 -12.7085 4.62541 -13.9584 0 -13.9584C -4.62541 -13.9584 -8.37504 -12.7085 -8.37504 -11.1667C -8.37504 -9.62492 -4.62541 -8.37504 0 -8.37504C 4.62541 -8.37504 8.37504 -9.62492 8.37504 -11.1667Z' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M -8.37504 -11.1667L -8.37504 -0L -8.37504 -1.85963e-15C -8.37504 -1.23975e-15 -8.37504 -6.19877e-16 -8.37504 -0C -8.37504 1.5418 -4.62541 2.79168 0 2.79168C 4.62541 2.79168 8.37504 1.5418 8.37504 0L 8.37504 0L 8.37504 -11.1667' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 65.6282 74.202)'> +<path d='M 8.37504 -11.1667C 8.37504 -15.7921 4.62541 -19.5418 0 -19.5418C -4.62541 -19.5418 -8.37504 -15.7921 -8.37504 -11.1667' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/grid.svg b/Master/texmf-dist/asymptote/GUI/res/icons/grid.svg new file mode 100644 index 00000000000..456e5011317 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/grid.svg @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<g>
+ <g>
+ <path d="M160,153.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V153.3z"
+ />
+ <path d="M288,153.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V153.3z"
+ />
+ <path d="M416,153.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V153.3z"
+ />
+ </g>
+ <g>
+ <path d="M160,281.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V281.3z"
+ />
+ <path d="M288,281.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V281.3z"
+ />
+ <path d="M416,281.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V281.3z"
+ />
+ </g>
+ <g>
+ <path d="M160,409.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V409.3z"
+ />
+ <path d="M288,409.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V409.3z"
+ />
+ <path d="M416,409.3c0,3.7-3,6.7-6.7,6.7h-50.5c-3.7,0-6.7-3-6.7-6.7v-50.5c0-3.7,3-6.7,6.7-6.7h50.5c3.7,0,6.7,3,6.7,6.7V409.3z"
+ />
+ </g>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/magnifying-glass.svg b/Master/texmf-dist/asymptote/GUI/res/icons/magnifying-glass.svg new file mode 100644 index 00000000000..f82283d618a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/magnifying-glass.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Magnifying_glass" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M17.545,15.467l-3.779-3.779c0.57-0.935,0.898-2.035,0.898-3.21c0-3.417-2.961-6.377-6.378-6.377
+ C4.869,2.1,2.1,4.87,2.1,8.287c0,3.416,2.961,6.377,6.377,6.377c1.137,0,2.2-0.309,3.115-0.844l3.799,3.801
+ c0.372,0.371,0.975,0.371,1.346,0l0.943-0.943C18.051,16.307,17.916,15.838,17.545,15.467z M4.004,8.287
+ c0-2.366,1.917-4.283,4.282-4.283c2.366,0,4.474,2.107,4.474,4.474c0,2.365-1.918,4.283-4.283,4.283
+ C6.111,12.76,4.004,10.652,4.004,8.287z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/opencurve.svg b/Master/texmf-dist/asymptote/GUI/res/icons/opencurve.svg new file mode 100644 index 00000000000..5086ab071b6 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/opencurve.svg @@ -0,0 +1,20 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='24pt' version='1.1' viewBox='56.4094 53.8583 24 24' width='24pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.8123)'> +<path d='M -9.39118 -0C -9.32257 -3.77783 -8.34324 -7.38444 -6.64057 -10.1298C -1.26219 -18.8017 7.68971 -15.5351 8.67632 -5.4822C 9.18679 -0.280877 6.74937 4.51887 3.32028 5.06489' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.8123)'> +<circle cx='-9.39118' cy='-0' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.8123)'> +<circle cx='-6.64057' cy='-10.1298' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.8123)'> +<circle cx='8.67632' cy='-5.4822' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 69.8123)'> +<circle cx='3.32028' cy='5.06489' r='3.01125' fill='#000000'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/openpolygon.svg b/Master/texmf-dist/asymptote/GUI/res/icons/openpolygon.svg new file mode 100644 index 00000000000..a07a63abd7d --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/openpolygon.svg @@ -0,0 +1,20 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- This file was generated by dvisvgm 2.4 --> +<svg height='24pt' version='1.1' viewBox='56.4094 53.8583 24 24' width='24pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'> +<g id='page1'> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<path d='M -9.39118 -0L -6.64057 -12.045L 8.67632 -6.51871L 3.32028 6.0225' fill='none' stroke='#000000' stroke-linecap='round' stroke-linejoin='round' stroke-miterlimit='10.0375' stroke-width='1.75656'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='-9.39118' cy='-0' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='-6.64057' cy='-12.045' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='8.67632' cy='-6.51871' r='3.01125' fill='#000000'/> +</g> +<g transform='matrix(0.996264 0 0 0.996264 68.7655 68.8583)'> +<circle cx='3.32028' cy='6.0225' r='3.01125' fill='#000000'/> +</g></g> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/plus-round.svg b/Master/texmf-dist/asymptote/GUI/res/icons/plus-round.svg new file mode 100644 index 00000000000..465d937802b --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/plus-round.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<path d="M417.4,224H288V94.6c0-16.9-14.3-30.6-32-30.6c-17.7,0-32,13.7-32,30.6V224H94.6C77.7,224,64,238.3,64,256
+ c0,17.7,13.7,32,30.6,32H224v129.4c0,16.9,14.3,30.6,32,30.6c17.7,0,32-13.7,32-30.6V288h129.4c16.9,0,30.6-14.3,30.6-32
+ C448,238.3,434.3,224,417.4,224z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/save.svg b/Master/texmf-dist/asymptote/GUI/res/icons/save.svg new file mode 100644 index 00000000000..2d2823d3919 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/save.svg @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Save" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<path d="M15.173,2H4C2.899,2,2,2.9,2,4v12c0,1.1,0.899,2,2,2h12c1.101,0,2-0.9,2-2V5.127L15.173,2z M14,8c0,0.549-0.45,1-1,1H7
+ C6.45,9,6,8.549,6,8V3h8V8z M13,4h-2v4h2V4z"/>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/social-python.svg b/Master/texmf-dist/asymptote/GUI/res/icons/social-python.svg new file mode 100644 index 00000000000..35fcdde7c10 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/social-python.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+ <path d="M193.46,249.056c3.723-0.67,7.589-1.041,11.586-1.041L201.924,248h103.823c4.503,0,8.806-0.617,12.908-1.754
+ c19.37-5.363,33.345-22.537,33.345-43.663v-30.822v-56.402c0-24.832-21.15-43.484-46.289-47.606
+ c-15.931-2.624-39.258-3.827-55.089-3.749c-15.829,0.086-30.981,1.404-44.277,3.749C167.143,74.576,160,88.928,160,115.359V144h96
+ v16H128.82c-35.628,0-64.538,42.571-64.813,95.242C64.005,255.495,64,255.747,64,256c0,9.523,0.94,18.72,2.685,27.404
+ C74.648,323.07,99.451,352,128.82,352H144v-2.662v-43.273C144,279.238,164.146,254.332,193.46,249.056z M203.656,127.002
+ c-9.592,0-17.384-7.785-17.384-17.403c0-9.664,7.774-17.52,17.384-17.52c9.574,0,17.399,7.855,17.399,17.52
+ C221.056,119.217,213.246,127.002,203.656,127.002z"/>
+ <path d="M443.951,222.543C434.78,186.021,411.033,160,383.18,160H368v2.626v38.046c0,33.915-22.286,58.474-49.489,62.681
+ c-2.737,0.424-5.483,0.646-8.301,0.646H206.351c-4.518,0-8.904,0.584-13.049,1.672C174.18,270.689,160,286.6,160,307.236v32.922
+ v54.305c0,24.832,24.977,39.426,49.481,46.551c29.327,8.531,61.267,10.068,96.366,0C329.15,434.354,352,420.893,352,394.463V368
+ h-96v-16h127.18c25.24,0,47.107-21.365,57.814-52.549C445.474,286.404,448,271.641,448,256
+ C448,244.232,446.567,232.962,443.951,222.543z M307.867,382.82c9.59,0,17.381,7.785,17.381,17.4
+ c0,9.65-7.791,17.521-17.381,17.521c-9.577,0-17.399-7.871-17.399-17.521C290.468,390.59,298.274,382.82,307.867,382.82z"/>
+</g>
+</svg>
diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/subdirectory-left.svg b/Master/texmf-dist/asymptote/GUI/res/icons/subdirectory-left.svg new file mode 100644 index 00000000000..bbbf3336529 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/subdirectory-left.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M18 12v1H8v5l-6-6 6-6v5h8V2h2z"/></svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/text.svg b/Master/texmf-dist/asymptote/GUI/res/icons/text.svg new file mode 100644 index 00000000000..7a4ffb4c0d2 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/text.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"> + <path d="M0 0v2h.5c0-.55.45-1 1-1h1.5v5.5c0 .28-.22.5-.5.5h-.5v1h4v-1h-.5c-.28 0-.5-.22-.5-.5v-5.5h1.5c.55 0 1 .45 1 1h.5v-2h-8z" /> +</svg>
\ No newline at end of file diff --git a/Master/texmf-dist/asymptote/GUI/res/icons/triangle-stroked-15.svg b/Master/texmf-dist/asymptote/GUI/res/icons/triangle-stroked-15.svg new file mode 100644 index 00000000000..c87ff1d223c --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/res/icons/triangle-stroked-15.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" + id="svg4619" inkscape:version="0.91+devel+osxmenu r12911" sodipodi:docname="triangle-stroked-15.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="15px" height="15px" + viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;" xml:space="preserve"> +<path id="rect3338" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccc" d="M7.5243,1.5004 + C7.2429,1.4913,6.9787,1.6423,6.8336,1.8952l-5.5,9.8692C1.0218,12.3078,1.395,12.9999,2,13h11 + c0.605-0.0001,0.9782-0.6922,0.6664-1.2355l-5.5-9.8692C8.0302,1.6579,7.7884,1.5092,7.5243,1.5004z M7.5,3.8993l4.1267,7.4704 + H3.3733L7.5,3.8993z"/> +</svg> diff --git a/Master/texmf-dist/asymptote/GUI/setup.py b/Master/texmf-dist/asymptote/GUI/setup.py new file mode 100755 index 00000000000..d6a4c0d6a78 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/setup.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import xasyVersion +from setuptools import setup + +setup( + name="xasy", + version=xasyVersion.xasyVersion, + author="Supakorn Rassameemasmuang, Orest Shardt, and John C. Bowman", + description="User interface for Asymptote, a vector graphics language", + url="http://asymptote.sourceforge.net", + download_url="https://sourceforge.net/projects/asymptote/" +) diff --git a/Master/texmf-dist/asymptote/GUI/xasy.py b/Master/texmf-dist/asymptote/GUI/xasy.py index f160404c19a..7e38f73a2aa 100755 --- a/Master/texmf-dist/asymptote/GUI/xasy.py +++ b/Master/texmf-dist/asymptote/GUI/xasy.py @@ -1,38 +1,16 @@ -#!/usr/bin/env python -########################################################################### -# -# xasy implements a graphical interface for Asymptote. -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -############################################################################ +#!/usr/bin/env python3 -import getopt,sys,signal -import xasyMainWin -if sys.version_info >= (3, 0): - from tkinter import * -else: - from Tkinter import * +import sys +import PyQt5.QtWidgets as Qw +from Window1 import MainWindow1 -signal.signal(signal.SIGINT,signal.SIG_IGN) -root = Tk() -mag = 1.0 -try: - opts,args = getopt.getopt(sys.argv[1:],"x:") - if(len(opts)>=1): - mag = float(opts[0][1]) -except: - print ("Invalid arguments.") - print ("Usage: xasy.py [-x magnification] [filename]") - sys.exit(1) -if(mag <= 0.0): - print ("Magnification must be positive.") - sys.exit(1) -if(len(args)>=1): - app = xasyMainWin.xasyMainWin(root,args[0],mag) -else: - app = xasyMainWin.xasyMainWin(root,magnification=mag) -root.mainloop() +def main(args): + qtApp = Qw.QApplication(args) + mainWin1 = MainWindow1() + mainWin1.show() + return qtApp.exec_() + + +if __name__ == '__main__': + sys.exit(main(sys.argv) or 0) diff --git a/Master/texmf-dist/asymptote/GUI/xasy2asy.py b/Master/texmf-dist/asymptote/GUI/xasy2asy.py index 06c0dbd7f54..2df6c3417a0 100755 --- a/Master/texmf-dist/asymptote/GUI/xasy2asy.py +++ b/Master/texmf-dist/asymptote/GUI/xasy2asy.py @@ -1,766 +1,1407 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 + ########################################################################### # # xasy2asy provides a Python interface to Asymptote # # -# Author: Orest Shardt -# Created: June 29, 2007 +# Authors: Orest Shardt, Supakorn Rassameemasmuang, and John C. Bowman # ########################################################################### -import sys,os,signal,threading -from subprocess import * -from string import * -import xasyOptions -from tempfile import mkdtemp - -if sys.version_info >= (3, 0): - from tkinter import * - import queue -else: - from Tkinter import * - import Queue as queue - -# PIL support is now mandatory due to rotations -try: - from PIL import ImageTk - from PIL import Image -except: - pass - -import CubicBezier - -quickAsyFailed = True -global AsyTempDir - -console=None - -def startQuickAsy(): - global quickAsy - global quickAsyFailed - global AsyTempDir - global fout,fin - if quickAsyRunning(): - return - try: - fout.close() - quickAsy.wait() - except: - pass - try: - quickAsyFailed = False - if os.name == "nt": - AsyTempDir=mkdtemp(prefix="asy_", dir="./") - else: - AsyTempDir=mkdtemp(prefix="asy_")+os.sep - if sys.platform[:3] == 'win': - quickAsy=Popen([xasyOptions.options['asyPath'],"-noV","-multiline","-q", - "-o"+AsyTempDir,"-inpipe=0","-outpipe=2"],stdin=PIPE, - stderr=PIPE,universal_newlines=True) - fout=quickAsy.stdin - fin=quickAsy.stderr - else: - (rx,wx) = os.pipe() - (ra,wa) = os.pipe() - if sys.version_info >= (3, 4): - os.set_inheritable(rx, True) - os.set_inheritable(wx, True) - os.set_inheritable(ra, True) - os.set_inheritable(wa, True) - quickAsy=Popen([xasyOptions.options['asyPath'],"-noV","-multiline","-q", - "-o"+AsyTempDir,"-inpipe="+str(rx),"-outpipe="+str(wa)], - close_fds=False) - fout=os.fdopen(wx,'w') - fin=os.fdopen(ra,'r') - if quickAsy.returncode != None: - quickAsyFailed = True - except: - quickAsyFailed = True - -def stopQuickAsy(): - if quickAsyRunning(): - fout.write("exit;\n"); - -def getAsyTempDir(): - return AsyTempDir - -def quickAsyRunning(): - if quickAsyFailed or quickAsy.returncode != None: - return False - else: - return True - -def asyExecute(command): - if not quickAsyRunning(): - startQuickAsy() - fout.write(command) - -def closeConsole(event): - global console - console = None - -def consoleOutput(line): - global console - global ctl - if console == None: - ctl=Toplevel() - ctl.title("Asymptote Console") - ctl.bind("<Destroy>",closeConsole) - yscrollbar=Scrollbar(ctl) - yscrollbar.pack(side=RIGHT,fill=Y) - console=Text(ctl,yscrollcommand=yscrollbar.set) - console.pack() - yscrollbar.config(command=console.yview) - console.insert(END,line) - ctl.lift() - -class asyTransform: - """A python implementation of an asy transform""" - def __init__(self,initTuple,delete=False): - """Initialize the transform with a 6 entry tuple""" - if type(initTuple) == type((0,)) and len(initTuple) == 6: - self.t = initTuple - self.x,self.y,self.xx,self.xy,self.yx,self.yy = initTuple - self.deleted = delete - else: - raise Exception("Illegal initializer for asyTransform") - - def getCode(self): - """Obtain the asy code that represents this transform""" - if self.deleted: - return str(self.t) + ", false" - else: - return str(self.t) - - def scale(self,s): - return asyTransform((0,0,s,0,0,s))*self - - def __str__(self): - """Equivalent functionality to getCode(). It allows the expression str(asyTransform) to be meaningful.""" - return self.getCode() - - def __mul__(self,other): - """Define multiplication of transforms as composition.""" - if type(other)==type((0,)): - if len(other) == 6: - return self*asyTransform(other) - elif len(other) == 2: - return ((self.t[0]+self.t[2]*other[0]+self.t[3]*other[1]),(self.t[1]+self.t[4]*other[0]+self.t[5]*other[1])) - else: - raise Exception("Illegal multiplier of {:s}".format(str(type(other)))) - elif isinstance(other,asyTransform): - result = asyTransform((0,0,0,0,0,0)) - result.x = self.x+self.xx*other.x+self.xy*other.y - result.y = self.y+self.yx*other.x+self.yy*other.y - result.xx = self.xx*other.xx+self.xy*other.yx - result.xy = self.xx*other.xy+self.xy*other.yy - result.yx = self.yx*other.xx+self.yy*other.yx - result.yy = self.yx*other.xy+self.yy*other.yy - result.t = (result.x,result.y,result.xx,result.xy,result.yx,result.yy) - return result - else: - raise Exception("Illegal multiplier of {:s}".format(str(type(other)))) + +import PyQt5.QtWidgets as Qw +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +import PyQt5.QtSvg as Qs + +import numpy as np + +import sys +import os +import signal +import threading +import string +import subprocess +import tempfile +import re +import shutil +import copy +import queue +import io +import atexit +import DebugFlags + +import xasyUtils as xu +import xasyArgs as xa +import xasyOptions as xo +import xasySvg as xs + +class AsymptoteEngine: + xasy=chr(4)+"\n" + + def __init__(self, path=None, keepFiles=DebugFlags.keepFiles, keepDefaultArgs=True): + if path is None: + path = xa.getArgs().asypath + if path is None: + opt = xo.BasicConfigs.defaultOpt + opt.load() + path = opt['asyPath'] + + if sys.platform[:3] == 'win': + rx = 0 # stdin + wa = 2 # stderr + else: + rx, wx = os.pipe() + ra, wa = os.pipe() + os.set_inheritable(rx, True) + os.set_inheritable(wx, True) + os.set_inheritable(ra, True) + os.set_inheritable(wa, True) + self.ostream = os.fdopen(wx, 'w') + self.istream = os.fdopen(ra, 'r') + + self.keepFiles = keepFiles + if sys.platform[:3] == 'win': + self.tmpdir = tempfile.mkdtemp(prefix='xasyData_',dir='./')+'/' + else: + self.tmpdir = tempfile.mkdtemp(prefix='xasyData_')+os.sep + + self.args=['-xasy', '-noV', '-q', '-inpipe=' + str(rx), '-outpipe=' + str(wa), '-o', self.tmpdir] + + self.asyPath = path + self.asyProcess = None + + def start(self): + try: + if sys.platform[:3] == 'win': + self.asyProcess = subprocess.Popen([self.asyPath] + self.args, + stdin=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + self.ostream = self.asyProcess.stdin + self.istream = self.asyProcess.stderr + else: + self.asyProcess = subprocess.Popen([self.asyPath] + self.args,close_fds=False) + finally: + atexit.register(self.cleanup) + + def wait(self): + if self.asyProcess.returncode is not None: + return + else: + return self.asyProcess.wait() + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + self.wait() + + @property + def tempDirName(self): + return self.tmpdir + + def startThenStop(self): + self.start() + self.stop() + self.wait() + + @property + def active(self): + if self.asyProcess is None: + return False + return self.asyProcess.returncode is None + + def stop(self): + if self.active: + self.asyProcess.kill() + + def cleanup(self): + self.stop() + if self.asyProcess is not None: + self.asyProcess.wait() + if not self.keepFiles: + if os.path.isdir(self.tempDirName + os.sep): + shutil.rmtree(self.tempDirName, ignore_errors=True) + +class asyTransform(Qc.QObject): + """A python implementation of an asy transform""" + + def __init__(self, initTuple, delete=False): + """Initialize the transform with a 6 entry tuple""" + super().__init__() + if isinstance(initTuple, (tuple, list)) and len(initTuple) == 6: + self.t = initTuple + self.x, self.y, self.xx, self.xy, self.yx, self.yy = initTuple + self._deleted = delete + else: + raise TypeError("Illegal initializer for asyTransform") + + @property + def deleted(self): + return self._deleted + + @deleted.setter + def deleted(self, value): + self._deleted = value + + @classmethod + def zero(cls): + return asyTransform((0, 0, 0, 0, 0, 0)) + + @classmethod + def fromQTransform(cls, transform: Qg.QTransform): + tx, ty = transform.dx(), transform.dy() + xx, xy, yx, yy = transform.m11(), transform.m21(), transform.m12(), transform.m22() + + return asyTransform((tx, ty, xx, xy, yx, yy)) + + @classmethod + def fromNumpyMatrix(cls, transform: np.ndarray): + assert transform.shape == (3, 3) + + tx = transform[0, 2] + ty = transform[1, 2] + + xx, xy, yx, yy = transform[0:2, 0:2].ravel().tolist()[0] + + return asyTransform((tx, ty, xx, xy, yx, yy)) + + def getRawCode(self): + return xu.tuple2StrWOspaces(self.t) + + def getCode(self, asy2psmap=None): + """Obtain the asy code that represents this transform""" + if asy2psmap is None: + asy2psmap = asyTransform((0, 0, 1, 0, 0, 1)) + if self.deleted: + return 'zeroTransform' + else: + return (asy2psmap.inverted() * self * asy2psmap).getRawCode() + + def scale(self, s): + return asyTransform((0, 0, s, 0, 0, s)) * self + + def toQTransform(self): + return Qg.QTransform(self.xx, self.yx, self.xy, self.yy, self.x, self.y) + + def __str__(self): + """Equivalent functionality to getCode(). It allows the expression str(asyTransform) to be meaningful.""" + return self.getCode() + + def isIdentity(self): + return self == identity() + + def inverted(self): + return asyTransform.fromQTransform(self.toQTransform().inverted()[0]) + + def __eq__(self, other): + return list(self.t) == list(other.t) + + def __mul__(self, other): + """Define multiplication of transforms as composition.""" + if isinstance(other, tuple): + if len(other) == 6: + return self * asyTransform(other) + elif len(other) == 2: + return ((self.t[0] + self.t[2] * other[0] + self.t[3] * other[1]), + (self.t[1] + self.t[4] * other[0] + self.t[5] * other[1])) + else: + raise Exception("Illegal multiplier of {:s}".format(str(type(other)))) + elif isinstance(other, asyTransform): + result = asyTransform((0, 0, 0, 0, 0, 0)) + result.x = self.x + self.xx * other.x + self.xy * other.y + result.y = self.y + self.yx * other.x + self.yy * other.y + result.xx = self.xx * other.xx + self.xy * other.yx + result.xy = self.xx * other.xy + self.xy * other.yy + result.yx = self.yx * other.xx + self.yy * other.yx + result.yy = self.yx * other.xy + self.yy * other.yy + result.t = (result.x, result.y, result.xx, result.xy, result.yx, result.yy) + return result + elif isinstance(other, str): + if other != 'cycle': + raise TypeError + else: + return 'cycle' + else: + raise TypeError("Illegal multiplier of {:s}".format(str(type(other)))) + def identity(): - return asyTransform((0,0,1,0,0,1)) + return asyTransform((0, 0, 1, 0, 0, 1)) + +def yflip(): + return asyTransform((0, 0, 1, 0, 0, -1)) -class asyObj: - """A base class for asy objects: an item represented by asymptote code.""" - def __init__(self): - """Initialize the object""" - self.asyCode = "" +class asyObj(Qc.QObject): + """A base class for asy objects: an item represented by asymptote code.""" + def __init__(self): + """Initialize the object""" + super().__init__() + self.asyCode = '' - def updateCode(self,mag=1.0): - """Update the object's code: should be overriden.""" - pass + def updateCode(self, ps2asymap=identity()): + """Update the object's code: should be overriden.""" + raise NotImplementedError + + def getCode(self, ps2asymap=identity()): + """Return the code describing the object""" + self.updateCode(ps2asymap) + return self.asyCode - def getCode(self): - """Return the code describing the object""" - self.updateCode() - return self.asyCode class asyPen(asyObj): - """A python wrapper for an asymptote pen""" - def __init__(self,color=(0,0,0),width=0.5,options=""): - """Initialize the pen""" - asyObj.__init__(self) - self.options=options - self.width=width - self.setColor(color) - self.updateCode() - if options != "": - self.computeColor() - - def updateCode(self,mag=1.0): - """Generate the pen's code""" - self.asyCode = "rgb({:g},{:g},{:g})+{:s}".format(self.color[0], self.color[1], self.color[2],str(self.width)) - if len(self.options) > 0: - self.asyCode += "+"+self.options - - def setWidth(self,newWidth): - """Set the pen's width""" - self.width=newWidth - self.updateCode() - - def setColor(self,color): - """Set the pen's color""" - if type(color) == type((1,)) and len(color) == 3: - self.color = color - else: - self.color = "(0,0,0)" - self.updateCode() - - def computeColor(self): - """Find out the color of an arbitrary asymptote pen.""" - fout.write("pen p="+self.getCode()+';\n') - fout.write("file fout=output(mode='pipe');\n") - fout.write("write(fout,colorspace(p),newl);\n") - fout.write("write(fout,colors(p));\n") - fout.write("flush(fout);\n") - fout.flush() - colorspace = fin.readline() - if colorspace.find("cmyk") != -1: - lines = fin.readline()+fin.readline()+fin.readline()+fin.readline() - parts = lines.split() - c,m,y,k = eval(parts[0]),eval(parts[1]),eval(parts[2]),eval(parts[3]) - k = 1-k - r,g,b = ((1-c)*k,(1-m)*k,(1-y)*k) - elif colorspace.find("rgb") != -1: - lines = fin.readline()+fin.readline()+fin.readline() - parts = lines.split() - r,g,b = eval(parts[0]),eval(parts[1]),eval(parts[2]) - elif colorspace.find("gray") != -1: - lines = fin.readline() - parts = lines.split() - r = g = b = eval(parts[0]) - self.color = (r,g,b) - - def tkColor(self): - """Return the tk version of the pen's color""" - self.computeColor() - return '#{}'.format("".join(["{:02x}".format(min(int(256*a),255)) for a in self.color])) + """A python wrapper for an asymptote pen""" + @staticmethod + def getColorFromQColor(color): + return color.redF(), color.greenF(), color.blueF() + + @staticmethod + def convertToQColor(color): + r, g, b = color + return Qg.QColor.fromRgbF(r, g, b) + + @classmethod + def fromAsyPen(cls, pen): + assert isinstance(pen, cls) + return cls(asyengine=pen._asyengine, color=pen.color, width=pen.width, pen_options=pen.options) + + def __init__(self, asyengine=None, color=(0, 0, 0), width=0.5, pen_options=""): + """Initialize the pen""" + asyObj.__init__(self) + self.color = (0, 0, 0) + self.options = pen_options + self.width = width + self._asyengine = asyengine + self._deferAsyfy = False + if pen_options: + self._deferAsyfy = True + self.updateCode() + self.setColor(color) + + @property + def asyEngine(self): + return self._asyengine + + @asyEngine.setter + def asyEngine(self, value): + self._asyengine = value + + def updateCode(self, asy2psmap=identity()): + """Generate the pen's code""" + if self._deferAsyfy: + self.computeColor() + self.asyCode = 'rgb({:g},{:g},{:g})+{:s}'.format(self.color[0], self.color[1], self.color[2], str(self.width)) + if len(self.options) > 0: + self.asyCode = self.asyCode + '+' + self.options + + def setWidth(self, newWidth): + """Set the pen's width""" + self.width = newWidth + self.updateCode() + + def setColor(self, color): + """Set the pen's color""" + if isinstance(color, tuple) and len(color) == 3: + self.color = color + else: + self.color = (0, 0, 0) + self.updateCode() + + def setColorFromQColor(self, color): + self.setColor(asyPen.getColorFromQColor(color)) + + def computeColor(self): + """Find out the color of an arbitrary asymptote pen.""" + assert isinstance(self.asyEngine, AsymptoteEngine) + assert self.asyEngine.active + + fout = self.asyEngine.ostream + fin = self.asyEngine.istream + + fout.write("pen p=" + self.getCode() + ';\n') + fout.write("write(_outpipe,colorspace(p),newl);\n") + fout.write("write(_outpipe,colors(p));\n") + fout.write("flush(_outpipe);\n") + fout.write(self.asyEngine.xasy) + fout.flush() + + colorspace = fin.readline() + if colorspace.find("cmyk") != -1: + lines = fin.readline() + fin.readline() + fin.readline() + fin.readline() + parts = lines.split() + c, m, y, k = eval(parts[0]), eval(parts[1]), eval(parts[2]), eval(parts[3]) + k = 1 - k + r, g, b = ((1 - c) * k, (1 - m) * k, (1 - y) * k) + elif colorspace.find("rgb") != -1: + lines = fin.readline() + fin.readline() + fin.readline() + parts = lines.split() + r, g, b = eval(parts[0]), eval(parts[1]), eval(parts[2]) + elif colorspace.find("gray") != -1: + lines = fin.readline() + parts = lines.split() + r = g = b = eval(parts[0]) + else: + raise ChildProcessError('Asymptote error.') + self.color = (r, g, b) + self._deferAsyfy = False + + def tkColor(self): + """Return the tk version of the pen's color""" + self.computeColor() + return '#{}'.format("".join(["{:02x}".format(min(int(256 * a), 255)) for a in self.color])) + + def toQPen(self): + if self._deferAsyfy: + self.computeColor() + newPen = Qg.QPen() + newPen.setColor(asyPen.convertToQColor(self.color)) + newPen.setWidthF(self.width) + + return newPen + class asyPath(asyObj): - """A python wrapper for an asymptote path""" - def __init__(self): - """Initialize the path to be an empty path: a path with no nodes, control points, or links.""" - asyObj.__init__(self) - self.nodeSet = [] - self.linkSet = [] - self.controlSet = [] - self.computed = False - - def initFromNodeList(self,nodeSet,linkSet): - """Initialize the path from a set of nodes and link types, "--", "..", or "::" """ - if len(nodeSet)>0: - self.nodeSet = nodeSet[:] - self.linkSet = linkSet[:] - self.computed = False - - def initFromControls(self,nodeSet,controlSet): - """Initialize the path from nodes and control points""" - self.controlSet = controlSet[:] - self.nodeSet = nodeSet[:] - self.computed = True - - def makeNodeStr(self,node): - """Represent a node as a string""" - if node == 'cycle': - return node - else: - return "("+str(node[0])+","+str(node[1])+")" - - def updateCode(self,mag=1.0): - """Generate the code describing the path""" - if not self.computed: - count = 0 - #this string concatenation could be optimised - self.asyCode = self.makeNodeStr(self.nodeSet[0]) - for node in self.nodeSet[1:]: - self.asyCode += self.linkSet[count]+self.makeNodeStr(node) - count += 1 - else: - count = 0 - #this string concatenation could be optimised - self.asyCode = self.makeNodeStr(self.nodeSet[0]) - for node in self.nodeSet[1:]: - self.asyCode += "..controls" - self.asyCode += self.makeNodeStr(self.controlSet[count][0]) - self.asyCode += "and" - self.asyCode += self.makeNodeStr(self.controlSet[count][1]) - self.asyCode += ".." + self.makeNodeStr(node) + "\n" - count += 1 - - def getNode(self,index): - """Return the requested node""" - return self.nodeSet[index] - - def getLink(self,index): - """Return the requested link""" - return self.linkSet[index] - - def setNode(self,index,newNode): - """Set a node to a new position""" - self.nodeSet[index] = newNode - - def moveNode(self,index,offset): - """Translate a node""" - if self.nodeSet[index] != "cycle": - self.nodeSet[index] = (self.nodeSet[index][0]+offset[0],self.nodeSet[1]+offset[1]) - - def setLink(self,index,ltype): - """Change the specified link""" - self.linkSet[index] = ltype - - def addNode(self,point,ltype): - """Add a node to the end of a path""" - self.nodeSet.append(point) - if len(self.nodeSet) != 1: - self.linkSet.append(ltype) - if self.computed: - self.computeControls() - - def insertNode(self,index,point,ltype=".."): - """Insert a node, and its corresponding link, at the given index""" - self.nodeSet.insert(index,point) - self.linkSet.insert(index,ltype) - if self.computed: - self.computeControls() - - def setControl(self,index,position): - """Set a control point to a new position""" - self.controlSet[index] = position - - def moveControl(self,index,offset): - """Translate a control point""" - self.controlSet[index] = (self.controlSet[index][0]+offset[0],self.controlSet[index][1]+offset[1]) - - def computeControls(self): - """Evaluate the code of the path to obtain its control points""" - fout.write("file fout=output(mode='pipe');\n") - fout.write("path p="+self.getCode()+';\n') - fout.write("write(fout,length(p),newl);\n") - fout.write("write(fout,unstraighten(p),endl);\n") - fout.flush() - lengthStr = fin.readline() - pathSegments = eval(lengthStr.split()[-1]) - pathStrLines = [] - for i in range(pathSegments+1): - line=fin.readline() - line=line.replace("\n","") - pathStrLines.append(line) - oneLiner = "".join(pathStrLines).replace(" ", "") - splitList = oneLiner.split("..") - nodes = [a for a in splitList if a.find("controls")==-1] - self.nodeSet = [] - for a in nodes: - if a == 'cycle': - self.nodeSet.append(a) - else: - self.nodeSet.append(eval(a)) - controls = [a.replace("controls","").split("and") for a in splitList if a.find("controls") != -1] - self.controlSet = [[eval(a[0]),eval(a[1])] for a in controls] - self.computed = True + """A python wrapper for an asymptote path""" + + def __init__(self, asyengine: AsymptoteEngine=None, forceCurve=False): + """Initialize the path to be an empty path: a path with no nodes, control points, or links.""" + super().__init__() + self.nodeSet = [] + self.linkSet = [] + self.forceCurve = forceCurve + self.controlSet = [] + self.computed = False + self.asyengine = asyengine + + @classmethod + def fromPath(cls, oldPath): + newObj = asyPath(None) + newObj.nodeSet = copy.copy(oldPath.nodeSet) + newObj.linkSet = copy.copy(oldPath.linkSet) + newObj.controlSet = copy.deepcopy(oldPath.controlSet) + newObj.computed = oldPath.computed + newObj.asyengine = oldPath.asyengine + + return newObj + + @classmethod + def fromBezierPoints(cls, pointList: list, engine=None): + if not pointList: + return None + assert isinstance(pointList[0], BezierCurveEditor.BezierPoint) + nodeList = [] + controlList = [] + for point in pointList: + nodeList.append(BezierCurveEditor.QPoint2Tuple(point.point)) + if point.rCtrlPoint is not None: # first + controlList.append([BezierCurveEditor.QPoint2Tuple(point.rCtrlPoint)]) + if point.lCtrlPoint is not None: # last + controlList[-1].append(BezierCurveEditor.QPoint2Tuple(point.lCtrlPoint)) + newPath = asyPath(asyengine=engine) + newPath.initFromControls(nodeList, controlList) + return newPath + + def setInfo(self, path): + self.nodeSet = copy.copy(path.nodeSet) + self.linkSet = copy.copy(path.linkSet) + self.controlSet = copy.deepcopy(path.controlSet) + self.computed = path.computed + + @property + def isEmpty(self): + return len(self.nodeSet) == 0 + + @property + def isDrawable(self): + return len(self.nodeSet) >= 2 + + def toQPainterPath(self) -> Qg.QPainterPath: + return self.toQPainterPathCurve() if self.containsCurve else self.toQPainterPathLine() + + def toQPainterPathLine(self): + baseX, baseY = self.nodeSet[0] + painterPath = Qg.QPainterPath(Qc.QPointF(baseX, baseY)) + + for pointIndex in range(1, len(self.nodeSet)): + node = self.nodeSet[pointIndex] + if self.nodeSet[pointIndex] == 'cycle': + node = self.nodeSet[0] + + painterPath.lineTo(*node) + + return painterPath + + + def toQPainterPathCurve(self): + if not self.computed: + self.computeControls() + + baseX, baseY = self.nodeSet[0] + painterPath = Qg.QPainterPath(Qc.QPointF(baseX, baseY)) + + for pointIndex in range(1, len(self.nodeSet)): + node = self.nodeSet[pointIndex] + if self.nodeSet[pointIndex] == 'cycle': + node = self.nodeSet[0] + endPoint = Qc.QPointF(node[0], node[1]) + ctrlPoint1 = Qc.QPointF(self.controlSet[pointIndex-1][0][0], self.controlSet[pointIndex-1][0][1]) + ctrlPoint2 = Qc.QPointF(self.controlSet[pointIndex-1][1][0], self.controlSet[pointIndex-1][1][1]) + + painterPath.cubicTo(ctrlPoint1, ctrlPoint2, endPoint) + return painterPath + + def initFromNodeList(self, nodeSet, linkSet): + """Initialize the path from a set of nodes and link types, "--", "..", or "::" """ + if len(nodeSet) > 0: + self.nodeSet = nodeSet[:] + self.linkSet = linkSet[:] + self.computed = False + + def initFromControls(self, nodeSet, controlSet): + """Initialize the path from nodes and control points""" + self.controlSet = controlSet[:] + self.nodeSet = nodeSet[:] + self.computed = True + + def makeNodeStr(self, node): + """Represent a node as a string""" + if node == 'cycle': + return node + else: + # if really want to, disable this rounding + # shouldn't be to much of a problem since 10e-6 is quite small... + return '({:.6g},{:.6g})'.format(node[0], node[1]) + + def updateCode(self, ps2asymap=identity()): + """Generate the code describing the path""" + # currently at postscript. Convert to asy + asy2psmap = ps2asymap.inverted() + with io.StringIO() as rawAsyCode: + count = 0 + rawAsyCode.write(self.makeNodeStr(asy2psmap * self.nodeSet[0])) + for node in self.nodeSet[1:]: + if not self.computed or count >= len(self.controlSet): + rawAsyCode.write(self.linkSet[count]) + rawAsyCode.write(self.makeNodeStr(asy2psmap * node)) + else: + rawAsyCode.write('..controls ') + rawAsyCode.write(self.makeNodeStr(asy2psmap * self.controlSet[count][0])) + rawAsyCode.write(' and ') + rawAsyCode.write(self.makeNodeStr(asy2psmap * self.controlSet[count][1])) + rawAsyCode.write(".." + self.makeNodeStr(asy2psmap * node)) + count = count + 1 + self.asyCode = rawAsyCode.getvalue() + + @property + def containsCurve(self): + return '..' in self.linkSet or self.forceCurve + + def getNode(self, index): + """Return the requested node""" + return self.nodeSet[index] + + def getLink(self, index): + """Return the requested link""" + return self.linkSet[index] + + def setNode(self, index, newNode): + """Set a node to a new position""" + self.nodeSet[index] = newNode + + def moveNode(self, index, offset): + """Translate a node""" + if self.nodeSet[index] != "cycle": + self.nodeSet[index] = (self.nodeSet[index][0] + offset[0], self.nodeSet[index][1] + offset[1]) + + def setLink(self, index, ltype): + """Change the specified link""" + self.linkSet[index] = ltype + + def addNode(self, point, ltype): + """Add a node to the end of a path""" + self.nodeSet.append(point) + if len(self.nodeSet) != 1: + self.linkSet.append(ltype) + if self.computed: + self.computeControls() + + def insertNode(self, index, point, ltype=".."): + """Insert a node, and its corresponding link, at the given index""" + self.nodeSet.insert(index, point) + self.linkSet.insert(index, ltype) + if self.computed: + self.computeControls() + + def setControl(self, index, position): + """Set a control point to a new position""" + self.controlSet[index] = position + + def popNode(self): + if len(self.controlSet) == len(self.nodeSet): + self.controlSet.pop() + self.nodeSet.pop() + self.linkSet.pop() + + def moveControl(self, index, offset): + """Translate a control point""" + self.controlSet[index] = (self.controlSet[index][0] + offset[0], self.controlSet[index][1] + offset[1]) + + def computeControls(self): + """Evaluate the code of the path to obtain its control points""" + # For now, if no asymptote process is given spawns a new one. + # Only happens if asyengine is None. + if self.asyengine is not None: + assert isinstance(self.asyengine, AsymptoteEngine) + assert self.asyengine.active + asy = self.asyengine + startUp = False + else: + startUp = True + asy = AsymptoteEngine() + asy.start() + + fout = asy.ostream + fin = asy.istream + + fout.write("path p=" + self.getCode() + ';\n') + fout.write("write(_outpipe,length(p),newl);\n") + fout.write("write(_outpipe,unstraighten(p),endl);\n") + fout.write(asy.xasy) + fout.flush() + + lengthStr = fin.readline() + pathSegments = eval(lengthStr.split()[-1]) + pathStrLines = [] + for i in range(pathSegments + 1): + line = fin.readline() + line = line.replace("\n", "") + pathStrLines.append(line) + oneLiner = "".join(pathStrLines).replace(" ", "") + splitList = oneLiner.split("..") + nodes = [a for a in splitList if a.find("controls") == -1] + self.nodeSet = [] + for a in nodes: + if a == 'cycle': + self.nodeSet.append(a) + else: + self.nodeSet.append(eval(a)) + controls = [a.replace("controls", "").split("and") for a in splitList if a.find("controls") != -1] + self.controlSet = [[eval(a[0]), eval(a[1])] for a in controls] + self.computed = True + + if startUp: + asy.stop() class asyLabel(asyObj): - """A python wrapper for an asy label""" - def __init__(self,text="",location=(0,0),pen=asyPen()): - """Initialize the label with the given test, location, and pen""" - asyObj.__init__(self) - self.text = text - self.location = location - self.pen = pen - - def updateCode(self,mag=1.0): - """Generate the code describing the label""" - self.asyCode = "Label(\""+self.text+"\","+str((self.location[0],self.location[1]))+","+self.pen.getCode()+",align=SE)" - - def setText(self,text): - """Set the label's text""" - self.text = text - self.updateCode() - - def setPen(self,pen): - """Set the label's pen""" - self.pen = pen - self.updateCode() - - def moveTo(self,newl): - """Translate the label's location""" - self.location = newl + """A python wrapper for an asy label""" + + def __init__(self, text="", location=(0, 0), pen=None, align=None, fontSize:int=None): + """Initialize the label with the given test, location, and pen""" + asyObj.__init__(self) + self.align = align + self.pen = pen + self.fontSize = fontSize + if align is None: + self.align = 'SE' + if pen is None: + self.pen = asyPen() + self.text = text + self.location = location + + def updateCode(self, asy2psmap=identity()): + """Generate the code describing the label""" + newLoc = asy2psmap.inverted() * self.location + locStr = xu.tuple2StrWOspaces(newLoc) + self.asyCode = 'Label("{0}",{1},p={2}{4},align={3})'.format(self.text, locStr, self.pen.getCode(), self.align, + self.getFontSizeText()) + + def getFontSizeText(self): + if self.fontSize is not None: + return '+fontsize({:.6g})'.format(self.fontSize) + else: + return '' + + def setText(self, text): + """Set the label's text""" + self.text = text + self.updateCode() + + def setPen(self, pen): + """Set the label's pen""" + self.pen = pen + self.updateCode() + + def moveTo(self, newl): + """Translate the label's location""" + self.location = newl + class asyImage: - """A structure containing an image and its format, bbox, and IDTag""" - def __init__(self,image,format,bbox): - self.image = image - self.format = format - self.bbox = bbox - self.IDTag = None - -class xasyItem: - """A base class for items in the xasy GUI""" - def __init__(self,canvas=None): - """Initialize the item to an empty item""" - self.transform = [identity()] - self.asyCode = "" - self.imageList = [] - self.IDTag = None - self.asyfied = False - self.onCanvas = canvas - - def updateCode(self,mag=1.0): - """Update the item's code: to be overriden""" - pass - - def getCode(self): - """Return the code describing the item""" - self.updateCode() - return self.asyCode - - def handleImageReception(self,file,format,bbox,count): - """Receive an image from an asy deconstruction. It replaces the default in asyProcess.""" - image = Image.open(file) - self.imageList.append(asyImage(image,format,bbox)) - if self.onCanvas != None: - self.imageList[-1].itk = ImageTk.PhotoImage(image) - self.imageList[-1].originalImage = image.copy() - self.imageList[-1].originalImage.theta = 0.0 - self.imageList[-1].originalImage.bbox = bbox - if count >= len(self.transform) or self.transform[count].deleted == False: - self.imageList[-1].IDTag = self.onCanvas.create_image(bbox[0],-bbox[3],anchor=NW,tags=("image"),image=self.imageList[-1].itk) - self.onCanvas.update() - - def asyfy(self,mag=1.0): - self.removeFromCanvas() - self.imageList = [] - self.imageHandleQueue = queue.Queue() - worker = threading.Thread(target=self.asyfyThread,args=(mag,)) - worker.start() - item = self.imageHandleQueue.get() - if console != None: - console.delete(1.0,END) - while item != (None,) and item[0] != "ERROR": - if(item[0] == "OUTPUT"): - consoleOutput(item[1]) - else: - self.handleImageReception(*item) - try: - os.remove(item[0]) - except: - pass - item = self.imageHandleQueue.get() - #self.imageHandleQueue.task_done() - worker.join() - - def asyfyThread(self,mag=1.0): - """Convert the item to a list of images by deconstructing this item's code""" - fout.write("reset;\n") - fout.write("initXasyMode();\n") - fout.write("atexit(null);\n") - for line in self.getCode().splitlines(): - fout.write(line+"\n"); - fout.write("deconstruct({:f});\n".format(mag)) - fout.flush() - maxargs = int(fin.readline().split()[0]) - boxes=[] - batch=0 - n=0 - text = fin.readline() - # template=AsyTempDir+"%d_%d.%s" - fileformat = "png" - def render(): - for i in range(len(boxes)): - l,b,r,t = [float(a) for a in boxes[i].split()] - name="{:s}{:d}_{:d}.{:s}".format(AsyTempDir,batch,i+1,fileformat) - self.imageHandleQueue.put((name,fileformat,(l,b,r,t),i)) - while text != "Done\n" and text != "Error\n": - boxes.append(text) - text = fin.readline() - n += 1 - if n >= maxargs: - render() - boxes=[] - batch += 1 - n=0 - if text == "Error\n": - self.imageHandleQueue.put(("ERROR",fin.readline())) - else: - render() - self.imageHandleQueue.put((None,)) - self.asyfied = True - - def drawOnCanvas(self,canvas,mag,forceAddition=False): - pass - def removeFromCanvas(self): - pass + """A structure containing an image and its format, bbox, and IDTag""" + def __init__(self, image, format, bbox, transfKey=None, keyIndex=0): + self.image = image + self.format = format + self.bbox = bbox + self.IDTag = None + self.key = transfKey + self.keyIndex = keyIndex + +class xasyItem(Qc.QObject): + """A base class for items in the xasy GUI""" + mapString = 'xmap' + setKeyFormatStr = string.Template('$map("{:s}",{:s});').substitute(map=mapString) + setKeyAloneFormatStr = string.Template('$map("{:s}");').substitute(map=mapString) + resizeComment="// Resize to initial xasy transform" + asySize="" + def __init__(self, canvas=None, asyengine=None): + """Initialize the item to an empty item""" + super().__init__() + self.transfKeymap = {} # the new keymap. + # should be a dictionary to a list... + self.asyCode = '' + self.imageList = [] + self.IDTag = None + self.asyfied = False + self.onCanvas = canvas + self.keyBuffer = None + self._asyengine = asyengine + self.drawObjects = [] + self.drawObjectsMap = {} + self.setKeyed = True + self.unsetKeys = set() + self.userKeys = set() + self.lineOffset = 0 + self.imageHandleQueue = queue.Queue() + + def updateCode(self, ps2asymap=identity()): + """Update the item's code: to be overriden""" + with io.StringIO() as rawCode: + transfCode = self.getTransformCode() + objCode = self.getObjectCode() + + rawCode.write(transfCode) + rawCode.write(objCode) + self.asyCode = rawCode.getvalue() + + return len(transfCode.splitlines()), len(objCode.splitlines()) + + @property + def asyengine(self): + return self._asyengine + + @asyengine.setter + def asyengine(self, value): + self._asyengine = value + + def getCode(self, ps2asymap=identity()): + """Return the code describing the item""" + self.updateCode(ps2asymap) + return self.asyCode + + def getTransformCode(self, asy2psmap=identity()): + raise NotImplementedError + + def getObjectCode(self, asy2psmap=identity()): + raise NotImplementedError + + def generateDrawObjects(self): + raise NotImplementedError + + def handleImageReception(self, file, fileformat, bbox, count, key=None, localCount=0, containsClip=False): + """Receive an image from an asy deconstruction. It replaces the default n asyProcess.""" + # image = Image.open(file).transpose(Image.FLIP_TOP_BOTTOM) + if fileformat == 'png': + image = Qg.QImage(file) + elif fileformat == 'svg': + if containsClip: + image = xs.SvgObject(file) + else: + image = Qs.QSvgRenderer(file) + assert image.isValid() + else: + raise Exception('Format not supported!') + self.imageList.append(asyImage(image, fileformat, bbox, transfKey=key, keyIndex=localCount)) + if self.onCanvas is not None: + # self.imageList[-1].iqt = ImageTk.PhotoImage(image) + currImage = self.imageList[-1] + currImage.iqt = image + currImage.originalImage = image + currImage.originalImage.theta = 0.0 + currImage.originalImage.bbox = list(bbox) + currImage.performCanvasTransform = False + + # handle this case if transform is not in the map yet. + # if deleted - set transform to 0, 0, 0, 0, 0 + transfExists = key in self.transfKeymap.keys() + if transfExists: + transfExists = localCount <= len(self.transfKeymap[key]) - 1 + if transfExists: + validKey = not self.transfKeymap[key][localCount].deleted + else: + validKey = False + + if (not transfExists) or validKey: + currImage.IDTag = str(file) + newDrawObj = DrawObject(currImage.iqt, self.onCanvas['canvas'], transform=identity(), + btmRightanchor=Qc.QPointF(bbox[0], bbox[2]), drawOrder=-1, key=key, + parentObj=self, keyIndex=localCount) + newDrawObj.setBoundingBoxPs(bbox) + newDrawObj.setParent(self) + + self.drawObjects.append(newDrawObj) + + if key not in self.drawObjectsMap.keys(): + self.drawObjectsMap[key] = [newDrawObj] + else: + self.drawObjectsMap[key].append(newDrawObj) + return containsClip + def asyfy(self, force=False): + if self.asyengine is None: + return 1 + if self.asyfied and not force: + return + + self.drawObjects = [] + self.drawObjectsMap.clear() + assert isinstance(self.asyengine, AsymptoteEngine) + self.imageList = [] + + self.unsetKeys.clear() + self.userKeys.clear() + + self.imageHandleQueue = queue.Queue() + worker = threading.Thread(target=self.asyfyThread, args=[]) + worker.start() + item = self.imageHandleQueue.get() + while item != (None,) and item[0] != "ERROR": + if item[0] == "OUTPUT": + print(item[1]) + else: + keepFile = self.handleImageReception(*item) + if not DebugFlags.keepFiles and not keepFile: + try: + os.remove(item[0]) + pass + except OSError: + pass + finally: + pass + item = self.imageHandleQueue.get() + # self.imageHandleQueue.task_done() + worker.join() + + def asyfyThread(self): + """Convert the item to a list of images by deconstructing this item's code""" + assert self.asyengine.active + + fout = self.asyengine.ostream + fin = self.asyengine.istream + + self.lineOffset = len(self.getTransformCode().splitlines()) + + fout.write("reset\n") + fout.flush(); + for line in self.getCode().splitlines(): + if DebugFlags.printDeconstTranscript: + print('fout:', line) + fout.write(line+"\n") + fout.write(self.asySize) + fout.write("deconstruct();\n") + fout.write('write(_outpipe,yscale(-1)*currentpicture.calculateTransform(),endl);\n') + fout.write(self.asyengine.xasy) + fout.flush() + + imageInfos = [] # of (box, key) + n = 0 + + keyCounts = {} + + def render(): + for i in range(len(imageInfos)): + box, key, localCount, useClip = imageInfos[i] + l, b, r, t = [float(a) for a in box.split()] + name = "{:s}_{:d}.{:s}".format(self.asyengine.tempDirName, i, fileformat) + + self.imageHandleQueue.put((name, fileformat, (l, -t, r, -b), i, key, localCount, useClip)) + + # key first, box second. + # if key is "Done" + raw_text = fin.readline() + text = "" + if DebugFlags.printDeconstTranscript: + print(raw_text.strip()) + + # template=AsyTempDir+"%d_%d.%s" + fileformat = 'svg' + + while raw_text != "Done\n" and raw_text != "Error\n": +# print(raw_text) + text = fin.readline() # the actual bounding box. + # print('TESTING:', text) + keydata = raw_text.strip().replace('KEY=', '', 1) # key + + clipflag = keydata[-1] == '1' + userkey = keydata[-2] == '1' + keydata = keydata[:-3] + + if not userkey: + self.unsetKeys.add(keydata) # the line and column to replace. + else: + self.userKeys.add(keydata) + +# print(line, col) + + if keydata not in keyCounts.keys(): + keyCounts[keydata] = 0 + + imageInfos.append((text, keydata, keyCounts[keydata], clipflag)) # key-data pair + + # for the next item + keyCounts[keydata] += 1 + + raw_text = fin.readline() + + if DebugFlags.printDeconstTranscript: + print(text.rstrip()) + print(raw_text.rstrip()) + + n += 1 + + if text == "Error\n": + self.imageHandleQueue.put(("ERROR", fin.readline())) + else: + render() + + self.asy2psmap = asyTransform(xu.listize(fin.readline().rstrip(),float)) + self.imageHandleQueue.put((None,)) + self.asyfied = True class xasyDrawnItem(xasyItem): - """A base class for GUI items was drawn by the user. It combines a path, a pen, and a transform.""" - def __init__(self,path,pen = asyPen(),transform = identity()): - """Initialize the item with a path, pen, and transform""" - xasyItem.__init__(self) - self.path = path - self.pen = pen - self.transform = [transform] - - def appendPoint(self,point,link=None): - """Append a point to the path. If the path is cyclic, add this point before the 'cycle' node.""" - if self.path.nodeSet[-1] == 'cycle': - self.path.nodeSet[-1] = point - self.path.nodeSet.append('cycle') - else: - self.path.nodeSet.append(point) - self.path.computed = False - if len(self.path.nodeSet) > 1 and link != None: - self.path.linkSet.append(link) - - def clearTransform(self): - """Reset the item's transform""" - self.transform = [identity()] - - def removeLastPoint(self): - """Remove the last point in the path. If the path is cyclic, remove the node before the 'cycle' node.""" - if self.path.nodeSet[-1] == 'cycle': - del self.path.nodeSet[-2] - else: - del self.path.nodeSet[-1] - del self.path.linkSet[-1] - self.path.computed = False - - def setLastPoint(self,point): - """Modify the last point in the path. If the path is cyclic, modify the node before the 'cycle' node.""" - if self.path.nodeSet[-1] == 'cycle': - self.path.nodeSet[-2] = point - else: - self.path.nodeSet[-1] = point - self.path.computed = False + """A base class for GUI items was drawn by the user. It combines a path, a pen, and a transform.""" + + def __init__(self, path, engine, pen=None, transform=identity(), key=None): + """Initialize the item with a path, pen, and transform""" + super().__init__(canvas=None, asyengine=engine) + if pen is None: + pen = asyPen() + self.path = path + self.path.asyengine = engine + self.asyfied = True + self.pen = pen + self._asyengine = engine + self.rawIdentifier = '' + self.transfKey = key + self.transfKeymap = {self.transfKey: [transform]} + + @property + def asyengine(self): + return self._asyengine + + @asyengine.setter + def asyengine(self, value: AsymptoteEngine): + self._asyengine = value + self.path.asyengine = value + + def setKey(self, newKey=None): + transform = self.transfKeymap[self.transfKey][0] + + self.transfKey = newKey + self.transfKeymap = {self.transfKey: [transform]} + + def generateDrawObjects(self, forceUpdate=False): + raise NotImplementedError + + def appendPoint(self, point, link=None): + """Append a point to the path. If the path is cyclic, add this point before the 'cycle' node.""" + if self.path.nodeSet[-1] == 'cycle': + self.path.nodeSet[-1] = point + self.path.nodeSet.append('cycle') + else: + self.path.nodeSet.append(point) + self.path.computed = False + self.asyfied = False + if len(self.path.nodeSet) > 1 and link is not None: + self.path.linkSet.append(link) + + def clearTransform(self): + """Reset the item's transform""" + self.transform = [identity()] + self.asyfied = False + + def removeLastPoint(self): + """Remove the last point in the path. If the path is cyclic, remove the node before the 'cycle' node.""" + if self.path.nodeSet[-1] == 'cycle': + del self.path.nodeSet[-2] + else: + del self.path.nodeSet[-1] + del self.path.linkSet[-1] + self.path.computed = False + self.asyfied = False + + def setLastPoint(self, point): + """Modify the last point in the path. If the path is cyclic, modify the node before the 'cycle' node.""" + if self.path.nodeSet[-1] == 'cycle': + self.path.nodeSet[-2] = point + else: + self.path.nodeSet[-1] = point + self.path.computed = False + self.asyfied = False + class xasyShape(xasyDrawnItem): - """An outlined shape drawn on the GUI""" - def __init__(self,path,pen=asyPen(),transform=identity()): - """Initialize the shape with a path, pen, and transform""" - xasyDrawnItem.__init__(self,path,pen,transform) - - def updateCode(self,mag=1.0): - """Generate the code to describe this shape""" - self.asyCode = "xformStack.push("+self.transform[0].getCode()+");\n" - self.asyCode += "draw("+self.path.getCode()+","+self.pen.getCode()+");" - - def removeFromCanvas(self,canvas): - """Remove the shape's depiction from a tk canvas""" - if self.IDTag != None: - canvas.delete(self.IDTag) - - def drawOnCanvas(self,canvas,mag,asyFy=False,forceAddition=False): - """Add this shape to a tk canvas""" - if not asyFy: - if self.IDTag == None or forceAddition: - #add ourselves to the canvas - self.path.computeControls() - self.IDTag = canvas.create_line(0,0,0,0,tags=("drawn","xasyShape"),fill=self.pen.tkColor(),width=self.pen.width*mag) - self.drawOnCanvas(canvas,mag) - else: - self.path.computeControls() - pointSet = [] - previousNode = self.path.nodeSet[0] - nodeCount = 0 - if len(self.path.nodeSet) == 0: - pointSet = [0,0,0,0] - elif len(self.path.nodeSet) == 1: - if self.path.nodeSet[-1] != 'cycle': - p = self.transform[0]*(self.path.nodeSet[0][0],self.path.nodeSet[0][1]) - pointSet = [p[0],-p[1],p[0],-p[1],p[0],-p[1]] - else: - pointSet = [0,0,0,0] + """An outlined shape drawn on the GUI""" + def __init__(self, path, asyengine, pen=None, transform=identity()): + """Initialize the shape with a path, pen, and transform""" + super().__init__(path=path, engine=asyengine, pen=pen, transform=transform) + + def getObjectCode(self, asy2psmap=identity()): + return 'draw(KEY="{0}",{1},{2});'.format(self.transfKey, self.path.getCode(asy2psmap), self.pen.getCode())+'\n\n' + + def getTransformCode(self, asy2psmap=identity()): + transf = self.transfKeymap[self.transfKey][0] + if transf == identity(): + return '' else: - for node in self.path.nodeSet[1:]: - if node == 'cycle': - node = self.path.nodeSet[0] - transform = self.transform[0].scale(mag) - points = CubicBezier.makeBezier(transform*previousNode,transform*self.path.controlSet[nodeCount][0],transform*self.path.controlSet[nodeCount][1],transform*node) - for point in points: - pointSet += [point[0],-point[1]] - nodeCount += 1 - previousNode = node - canvas.coords(self.IDTag,*pointSet) - canvas.itemconfigure(self.IDTag,fill=self.pen.tkColor(),width=self.pen.width*mag) - else: - #first asyfy then add an image list - pass - - def __str__(self): - """Create a string describing this shape""" - return "xasyShape code:{:s}".format("\n\t".join(self.getCode().splitlines())) + return xasyItem.setKeyFormatStr.format(self.transfKey, transf.getCode(asy2psmap))+'\n' + + def generateDrawObjects(self, forceUpdate=False): + if self.path.containsCurve: + self.path.computeControls() + transf = self.transfKeymap[self.transfKey][0] + + newObj = DrawObject(self.path.toQPainterPath(), None, drawOrder=0, transform=transf, pen=self.pen, + key=self.transfKey) + newObj.originalObj = self + newObj.setParent(self) + return [newObj] + + def __str__(self): + """Create a string describing this shape""" + return "xasyShape code:{:s}".format("\n\t".join(self.getCode().splitlines())) + class xasyFilledShape(xasyShape): - """A filled shape drawn on the GUI""" - def __init__(self,path,pen=asyPen(),transform=identity()): - """Initialize this shape with a path, pen, and transform""" - if path.nodeSet[-1] != 'cycle': - raise Exception("Filled paths must be cyclic") - xasyShape.__init__(self,path,pen,transform) - - def updateCode(self,mag=1.0): - """Generate the code describing this shape""" - self.asyCode = "xformStack.push("+self.transform[0].getCode()+");\n" - self.asyCode += "fill("+self.path.getCode()+","+self.pen.getCode()+");" - - def removeFromCanvas(self,canvas): - """Remove this shape's depiction from a tk canvas""" - if self.IDTag != None: - canvas.delete(self.IDTag) - - def drawOnCanvas(self,canvas,mag,asyFy=False,forceAddition=False): - """Add this shape to a tk canvas""" - if not asyFy: - if self.IDTag == None or forceAddition: - #add ourselves to the canvas - self.path.computeControls() - self.IDTag = canvas.create_polygon(0,0,0,0,0,0,tags=("drawn","xasyFilledShape"),fill=self.pen.tkColor(),outline=self.pen.tkColor(),width=1*mag) - self.drawOnCanvas(canvas,mag) - else: - self.path.computeControls() - pointSet = [] - previousNode = self.path.nodeSet[0] - nodeCount = 0 - if len(self.path.nodeSet) == 0: - pointSet = [0,0,0,0,0,0] - elif len(self.path.nodeSet) == 1: - if self.path.nodeSet[-1] != 'cycle': - p = self.transform[0]*(self.path.nodeSet[0][0],self.path.nodeSet[0][1]) - pointSet = [p[0],-p[1],p[0],-p[1],p[0],-p[1]] - else: - pointSet = [0,0,0,0,0,0] - elif len(self.path.nodeSet) == 2: - if self.path.nodeSet[-1] != 'cycle': - p = self.transform[0].scale(mag)*(self.path.nodeSet[0][0],self.path.nodeSet[0][1]) - p2 = self.transform[0].scale(mag)*(self.path.nodeSet[1][0],self.path.nodeSet[1][1]) - pointSet = [p[0],-p[1],p2[0],-p2[1],p[0],-p[1]] - else: - pointSet = [0,0,0,0,0,0] - else: - for node in self.path.nodeSet[1:]: - if node == 'cycle': - node = self.path.nodeSet[0] - transform = self.transform[0].scale(mag) - points = CubicBezier.makeBezier(transform*previousNode,transform*self.path.controlSet[nodeCount][0],transform*self.path.controlSet[nodeCount][1],transform*node) - for point in points: - pointSet += [point[0],-point[1]] - nodeCount += 1 - previousNode = node - canvas.coords(self.IDTag,*pointSet) - canvas.itemconfigure(self.IDTag,fill=self.pen.tkColor(),outline=self.pen.tkColor(),width=1*mag) - else: - #first asyfy then add an image list - pass - - def __str__(self): - """Return a string describing this shape""" - return "xasyFilledShape code:{:s}".format("\n\t".join(self.getCode().splitlines())) + """A filled shape drawn on the GUI""" + + def __init__(self, path, asyengine, pen=None, transform=identity()): + """Initialize this shape with a path, pen, and transform""" + if path.nodeSet[-1] != 'cycle': + raise Exception("Filled paths must be cyclic") + super().__init__(path, asyengine, pen, transform) + + def getObjectCode(self, asy2psmap=identity()): + return 'fill(KEY="{0}",{1},{2});'.format(self.transfKey, self.path.getCode(asy2psmap), self.pen.getCode())+'\n\n' + + def generateDrawObjects(self, forceUpdate=False): + if self.path.containsCurve: + self.path.computeControls() + newObj = DrawObject(self.path.toQPainterPath(), None, drawOrder=0, transform=self.transfKeymap[self.transfKey][0], + pen=self.pen, key=self.transfKey, fill=True) + newObj.originalObj = self + newObj.setParent(self) + return [newObj] + + def __str__(self): + """Return a string describing this shape""" + return "xasyFilledShape code:{:s}".format("\n\t".join(self.getCode().splitlines())) + class xasyText(xasyItem): - """Text created by the GUI""" - def __init__(self,text,location,pen=asyPen(),transform=identity()): - """Initialize this item with text, a location, pen, and transform""" - xasyItem.__init__(self) - self.label=asyLabel(text,location,pen) - self.transform = [transform] - self.onCanvas = None - - def updateCode(self,mag=1.0): - """Generate the code describing this object""" - self.asyCode = "xformStack.push("+self.transform[0].getCode()+");\n" - self.asyCode += "label("+self.label.getCode()+");" - - def removeFromCanvas(self): - """Removes the label's images from a tk canvas""" - if self.onCanvas == None: - return - for image in self.imageList: - if image.IDTag != None: - self.onCanvas.delete(image.IDTag) - - def drawOnCanvas(self,canvas,mag,asyFy=True,forceAddition=False): - """Adds the label's images to a tk canvas""" - if self.onCanvas == None: - self.onCanvas = canvas - elif self.onCanvas != canvas: - raise Exception("Error: item cannot be added to more than one canvas") - self.asyfy(mag) - - def __str__(self): - return "xasyText code:{:s}".format("\n\t".join(self.getCode().splitlines())) + """Text created by the GUI""" + + def __init__(self, text, location, asyengine, pen=None, transform=yflip(), key=None, align=None, fontsize:int=None): + """Initialize this item with text, a location, pen, and transform""" + super().__init__(asyengine=asyengine) + if pen is None: + pen = asyPen(asyengine=asyengine) + if pen.asyEngine is None: + pen.asyEngine = asyengine + self.label = asyLabel(text, location, pen, align, fontSize=fontsize) + # self.transform = [transform] + self.transfKey = key + self.transfKeymap = {self.transfKey: [transform]} + self.asyfied = False + self.onCanvas = None + + def setKey(self, newKey=None): + transform = self.transfKeymap[self.transfKey][0] + + self.transfKey = newKey + self.transfKeymap = {self.transfKey: [transform]} + + def getTransformCode(self, asy2psmap=yflip()): + transf = self.transfKeymap[self.transfKey][0] + if transf == yflip(): + # return xasyItem.setKeyAloneFormatStr.format(self.transfKey) + return '' + else: + return xasyItem.setKeyFormatStr.format(self.transfKey, transf.getCode(asy2psmap))+"\n" + + def getObjectCode(self, asy2psmap=yflip()): + return 'label(KEY="{0}",{1});'.format(self.transfKey, self.label.getCode(asy2psmap))+'\n' + + def generateDrawObjects(self, forceUpdate=False): + self.asyfy(forceUpdate) + return self.drawObjects + + def getBoundingBox(self): + self.asyfy() + return self.imageList[0].bbox + + def __str__(self): + return "xasyText code:{:s}".format("\n\t".join(self.getCode().splitlines())) + class xasyScript(xasyItem): - """A set of images create from asymptote code. It is always deconstructed.""" - def __init__(self,canvas,script="",transforms=[]): - """Initialize this script item""" - xasyItem.__init__(self,canvas) - self.transform = transforms[:] - self.script = script - - def clearTransform(self): - """Reset the transforms for each of the deconstructed images""" - self.transform = [identity() for im in self.imageList] - - def updateCode(self,mag=1.0): - """Generate the code describing this script""" - self.asyCode = ""; - if len(self.transform) > 0: - self.asyCode = "xformStack.add(" - isFirst = True - count = 0 - for xform in self.transform: - if not isFirst: - self.asyCode+=",\n" - self.asyCode += "indexedTransform({:d},{:s})".format(count,str(xform)) - isFirst = False - count += 1 - self.asyCode += ");\n" - self.asyCode += "startScript(); {\n" - self.asyCode += self.script.replace("\t"," ") - self.asyCode = self.asyCode.rstrip() - self.asyCode += "\n} endScript();\n" - - def setScript(self,script): - """Sets the content of the script item.""" - self.script = script - self.updateCode() - - def removeFromCanvas(self): - """Removes the script's images from a tk canvas""" - if self.onCanvas == None: - return - for image in self.imageList: - if image.IDTag != None: - self.onCanvas.delete(image.IDTag) - - def asyfy(self,mag): - """Generate the list of images described by this object and adjust the length of the transform list.""" - xasyItem.asyfy(self,mag) - while len(self.imageList) > len(self.transform): - self.transform.append(identity()) - while len(self.imageList) < len(self.transform): - self.transform.pop() - self.updateCode() - - def drawOnCanvas(self,canvas,mag,asyFy=True,forceAddition=False): - """Adds the script's images to a tk canvas""" - if self.onCanvas == None: - self.onCanvas = canvas - elif self.onCanvas != canvas: - raise Exception("Error: item cannot be added to more than one canvas") - self.asyfy(mag) - - def __str__(self): - """Return a string describing this script""" - retVal = "xasyScript\n\tTransforms:\n" - for xform in self.transform: - retVal += "\t"+str(xform)+"\n" - retVal += "\tCode Ommitted" - return retVal - -if __name__=='__main__': - root = Tk() - t=xasyText("test",(0,0)) - t.asyfy() + """A set of images create from asymptote code. It is always deconstructed.""" + + def __init__(self, canvas, engine, script="", transforms=None, transfKeyMap=None): + """Initialize this script item""" + super().__init__(canvas, asyengine=engine) + if transfKeyMap is not None: + self.transfKeymap = transfKeyMap + else: + self.transfKeymap = {} + + self.script = script + self.key2imagemap = {} + self.namedUnsetKeys = {} + self.keyPrefix = '' + self.scriptAsyfied = False + self.updatedPrefix = True + + def clearTransform(self): + """Reset the transforms for each of the deconstructed images""" + # self.transform = [identity()] * len(self.imageList) + keyCount = {} + + for im in self.imageList: + if im.key not in keyCount.keys(): + keyCount[im.key] = 1 + else: + keyCount[im.key] += 1 + + for key in keyCount: + self.transfKeymap[key] = [identity()] * keyCount[key] + + def getMaxKeyCounter(self): + maxCounter = -1 + for key in self.transfKeymap: + testNum = re.match(r'^x(\d+)$', key) + if testNum is not None: + maxCounter = max(maxCounter, int(testNum.group(1))) + return maxCounter + 1 + + def getTransformCode(self, asy2psmap=identity()): + with io.StringIO() as rawAsyCode: + if self.transfKeymap: + for key in self.transfKeymap.keys(): + val = self.transfKeymap[key] + + writeval = list(reversed(val)) + # need to map all transforms in a list if there is any non-identity + # unfortunately, have to check all transformations in the list. + while not all(checktransf == identity() for checktransf in writeval) and writeval: + transf = writeval.pop() + if transf.deleted: + rawAsyCode.write(xasyItem.setKeyFormatStr.format(key, transf.getCode(asy2psmap)) + '\n//') + if transf == identity() and not transf.deleted: + rawAsyCode.write(xasyItem.setKeyAloneFormatStr.format(key)) + else: + rawAsyCode.write(xasyItem.setKeyFormatStr.format(key, transf.getCode(asy2psmap))) + rawAsyCode.write('\n') + result = rawAsyCode.getvalue() + return result + + def findNonIdKeys(self): + return {key for key in self.transfKeymap if not all(transf == identity() for transf in self.transfKeymap[key]) } + + def getObjectCode(self, asy2psmap=identity()): + numeric=r'([-+]?(?:(?:\d*\.\d+)|(?:\d+\.?)))' + rSize=re.compile("size\(\("+numeric+","+numeric+","+numeric+"," + +numeric+","+numeric+","+numeric+"\)\); "+ + self.resizeComment) + + newScript = self.getReplacedKeysCode(self.findNonIdKeys()) + with io.StringIO() as rawAsyCode: + for line in newScript.splitlines(): + if(rSize.match(line)): + self.asySize=line.rstrip()+'\n' + else: + raw_line = line.rstrip().replace('\t', ' ' * 4) + rawAsyCode.write(raw_line + '\n') + + self.updatedCode = rawAsyCode.getvalue() + return self.updatedCode + + def setScript(self, script): + """Sets the content of the script item.""" + self.script = script + self.updateCode() + + def setKeyPrefix(self, newPrefix=''): + self.keyPrefix = newPrefix + self.updatedPrefix = False + + def getReplacedKeysCode(self, key2replace: set=None) -> str: + keylist = {} + prefix = '' + + key2replaceSet = self.unsetKeys if key2replace is None else \ + self.unsetKeys & key2replace + + linenum2key = {} + + if not self.updatedPrefix: + prefix = self.keyPrefix + + for key in key2replaceSet: + actualkey = key + + key = key.split(':')[0] + raw_parsed = xu.tryParseKey(key) + assert raw_parsed is not None + line, col = [int(val) for val in raw_parsed.groups()] + if line not in keylist: + keylist[line] = set() + keylist[line].add(col) + linenum2key[(line, col)] = actualkey + self.unsetKeys.discard(key) + + + raw_code_lines = self.script.splitlines() + with io.StringIO() as raw_str: + for i_0 in range(len(raw_code_lines)): + i = i_0 + self.lineOffset + curr_str = raw_code_lines[i_0] + if i + 1 in keylist.keys(): + # this case, we have a key. + with io.StringIO() as raw_line: + for j in range(len(curr_str)): + raw_line.write(curr_str[j]) + if j + 1 in keylist[i + 1]: + # at this point, replace keys with xkey + raw_line.write('KEY="{0:s}",'.format(linenum2key[(i + 1, j + 1)])) + self.userKeys.add(linenum2key[(i + 1, j + 1)]) + curr_str = raw_line.getvalue() + # else, skip and just write the line. + raw_str.write(curr_str + '\n') + return raw_str.getvalue() + + def getUnusedKey(self, oldkey) -> str: + baseCounter = 0 + newKey = oldkey + while newKey in self.userKeys: + newKey = oldkey + ':' + str(baseCounter) + baseCounter += 1 + return newKey + + def asyfy(self, keyOnly=False): + """Generate the list of images described by this object and adjust the length of the transform list.""" + super().asyfy() + + # Id --> Transf --> asy-fied --> Transf + # Transf should keep the original, raw transformation + # but for all new drawn objects - assign Id as transform. + + if self.scriptAsyfied: + return + + keyCount = {} + settedKey = {} + + for im in self.imageList: + if im.key in self.unsetKeys and im.key not in settedKey.keys(): + oldkey = im.key + self.unsetKeys.remove(im.key) + im.key = self.getUnusedKey(im.key) + self.unsetKeys.add(im.key) + + for drawobj in self.drawObjectsMap[oldkey]: + drawobj.key = im.key + + self.drawObjectsMap[im.key] = self.drawObjectsMap[oldkey] + self.drawObjectsMap.pop(oldkey) + + settedKey[oldkey] = im.key + elif im.key in settedKey.keys(): + im.key = settedKey[im.key] + + if im.key not in keyCount.keys(): + keyCount[im.key] = 1 + else: + keyCount[im.key] += 1 + + if im.key not in self.key2imagemap.keys(): + self.key2imagemap[im.key] = [im] + else: + self.key2imagemap[im.key].append(im) + + + + for key in keyCount: + if key not in self.transfKeymap.keys(): + self.transfKeymap[key] = [identity()] * keyCount[key] + else: + while len(self.transfKeymap[key]) < keyCount[key]: + self.transfKeymap[key].append(identity()) + + # while len(self.transfKeymap[key]) > keyCount[key]: + # self.transfKeymap[key].pop() + + # change of basis + for keylist in self.transfKeymap.values(): + for i in range(len(keylist)): + if keylist[i] != identity(): + keylist[i] = self.asy2psmap * keylist[i] * self.asy2psmap.inverted() + + self.updateCode() + self.scriptAsyfied = True + + def generateDrawObjects(self, forceUpdate=False): + self.asyfy(forceUpdate) + return self.drawObjects + + def __str__(self): + """Return a string describing this script""" + retVal = "xasyScript\n\tTransforms:\n" + for xform in self.transform: + retVal += "\t" + str(xform) + "\n" + retVal += "\tCode Ommitted" + return retVal + + +class DrawObject(Qc.QObject): + def __init__(self, drawObject, mainCanvas=None, transform=identity(), btmRightanchor=Qc.QPointF(0, 0), + drawOrder=(-1, -1), pen=None, key=None, parentObj=None, fill=False, keyIndex=0): + super().__init__() + self.drawObject = drawObject + self.mainCanvas = mainCanvas + self.pTransform = transform + self.baseTransform = transform + self.drawOrder = drawOrder + self.btmRightAnchor = btmRightanchor + self.originalObj = parentObj + self.explicitBoundingBox = None + self.useCanvasTransformation = False + self.key = key + self.cachedSvgImg = None + self.cachedDPI = None + self.maxDPI=0 + self.keyIndex = keyIndex + self.pen = pen + self.fill = fill + + def getInteriorScrTransform(self, transform): + """Generates the transform with Interior transform applied beforehand.""" + if isinstance(transform, Qg.QTransform): + transform = asyTransform.fromQTransform(transform) + return self.transform * transform * self.baseTransform.inverted() + + @property + def transform(self): + return self.pTransform + + @transform.setter + def transform(self, value): + self.pTransform = value + + def setBoundingBoxPs(self, bbox): + l, b, r, t = bbox + self.explicitBoundingBox = Qc.QRectF(Qc.QPointF(l, b), Qc.QPointF(r, t)) + # self.explicitBoundingBox = Qc.QRectF(0, 0, 100, 100) + + @property + def boundingBox(self): + if self.explicitBoundingBox is not None: + testBbox = self.explicitBoundingBox + else: + if isinstance(self.drawObject, Qg.QImage): + testBbox = self.drawObject.rect() + testBbox.moveTo(self.btmRightAnchor.toPoint()) + elif isinstance(self.drawObject, Qg.QPainterPath): + testBbox = self.baseTransform.toQTransform().mapRect(self.drawObject.boundingRect()) + else: + raise TypeError('drawObject is not a valid type!') + pointList = [self.getScreenTransform().toQTransform().map(point) for point in [ + testBbox.topLeft(), testBbox.topRight(), testBbox.bottomLeft(), testBbox.bottomRight() + ]] + return Qg.QPolygonF(pointList).boundingRect() + + @property + def localBoundingBox(self): + testBbox = self.drawObject.rect() + testBbox.moveTo(self.btmRightAnchor.toPoint()) + return testBbox + + def getScreenTransform(self): + scrTransf = self.baseTransform.toQTransform().inverted()[0] * self.pTransform.toQTransform() + return asyTransform.fromQTransform(scrTransf) + + def draw(self, additionalTransformation=None, applyReverse=False, canvas: Qg.QPainter=None, dpi=300): + if canvas is None: + canvas = self.mainCanvas + if additionalTransformation is None: + additionalTransformation = Qg.QTransform() + + assert canvas.isActive() + + canvas.save() + if self.pen: + oldPen = Qg.QPen(canvas.pen()) + canvas.setPen(self.pen.toQPen()) + else: + oldPen = Qg.QPen() + + if not applyReverse: + canvas.setTransform(additionalTransformation, True) + canvas.setTransform(self.transform.toQTransform(), True) + else: + canvas.setTransform(self.transform.toQTransform(), True) + canvas.setTransform(additionalTransformation, True) + + canvas.setTransform(self.baseTransform.toQTransform().inverted()[0], True) + + if isinstance(self.drawObject, Qg.QImage): + canvas.drawImage(self.explicitBoundingBox, self.drawObject) + elif isinstance(self.drawObject, xs.SvgObject): + threshold = 1.44 + + if self.cachedDPI is None or self.cachedSvgImg is None \ + or dpi > self.maxDPI*threshold: + self.cachedDPI = dpi + self.maxDPI=max(self.maxDPI,dpi) + self.cachedSvgImg = self.drawObject.render(dpi) + + canvas.drawImage(self.explicitBoundingBox, self.cachedSvgImg) + elif isinstance(self.drawObject, Qs.QSvgRenderer): + self.drawObject.render(canvas, self.explicitBoundingBox) + elif isinstance(self.drawObject, Qg.QPainterPath): + path = self.baseTransform.toQTransform().map(self.drawObject) + if self.fill: + if self.pen: + brush = self.pen.toQPen().brush() + else: + brush = Qg.QBrush() + canvas.fillPath(path, brush) + else: + canvas.drawPath(path) + + if self.pen: + canvas.setPen(oldPen) + canvas.restore() + + def collide(self, coords, canvasCoordinates=True): + # modify these values to grow/shrink the fuzz. + fuzzTolerance = 1 + marginGrowth = 1 + leftMargin = marginGrowth if self.boundingBox.width() < fuzzTolerance else 0 + topMargin = marginGrowth if self.boundingBox.height() < fuzzTolerance else 0 + + newMargin = Qc.QMarginsF(leftMargin, topMargin, leftMargin, topMargin) + return self.boundingBox.marginsAdded(newMargin).contains(coords) + + def getID(self): + return self.originalObj diff --git a/Master/texmf-dist/asymptote/GUI/xasyActions.py b/Master/texmf-dist/asymptote/GUI/xasyActions.py deleted file mode 100755 index 95411c5d9a4..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyActions.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# xasyActions implements the possible actions and their inverses -# for the undo/redo stack in xasy -# -# Author: Orest Shardt -# Created: July 23, 2007 -# -########################################################################### -import math -import sys -import UndoRedoStack -import xasy2asy -if sys.version_info >= (3, 0): - from tkinter import * -else: - from Tkinter import * - -class translationAction(UndoRedoStack.action): - def __init__(self,owner,itemList,indexList,translation): - self.translation = translation - self.owner = owner - self.itemList = itemList - self.indexList = indexList - UndoRedoStack.action.__init__(self,self.transF,self.unTransF) - - def transF(self): - mag = self.owner.magnification - for i in range(len(self.itemList)): - for index in self.indexList[i]: - self.owner.translateSomething(-1,(self.translation[0]/mag,self.translation[1]/mag),self.itemList[i],index) - if index==None: - index = 0 - try: - self.owner.mainCanvas.move(self.itemList[i].imageList[index].IDTag,self.translation[0]*mag,-self.translation[1]*mag) - except: - self.owner.mainCanvas.move(self.itemList[i].IDTag,self.translation[0]*mag,-self.translation[1]*mag) - self.owner.updateSelection() - self.owner.updateCanvasSize() - - def unTransF(self): - mag = self.owner.magnification - for i in range(len(self.itemList)): - for index in self.indexList[i]: - self.owner.translateSomething(-1,(-self.translation[0]/mag,-self.translation[1]/mag),self.itemList[i],index) - try: - self.owner.mainCanvas.move(self.itemList[i].imageList[index].IDTag,-self.translation[0]*mag,self.translation[1]*mag) - except: - self.owner.mainCanvas.move(self.itemList[i].IDTag,-self.translation[0]*mag,self.translation[1]*mag) - self.owner.updateSelection() - self.owner.updateCanvasSize() - - def __str__(self): - return "Translation of "+str(self.itemList)+str(self.indexList)+" by "+str(self.translation) - -class rotationAction(UndoRedoStack.action): - def __init__(self,owner,itemList,indexList,angle,origin): - self.owner = owner - self.itemList = itemList - self.indexList = indexList - self.angle = angle - self.origin = origin - UndoRedoStack.action.__init__(self,self.rotF,self.unRotF) - - def rotF(self): - for i in range(len(self.itemList)): - for index in self.indexList[i]: - self.owner.rotateSomething(-1,self.angle,self.origin,self.itemList[i],index) - for item in self.itemList: - item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(item) - self.owner.updateSelection() - self.owner.updateCanvasSize() - - def unRotF(self): - for i in range(len(self.itemList)): - for index in self.indexList[i]: - self.owner.rotateSomething(-1,-self.angle,self.origin,self.itemList[i],index) - for item in self.itemList: - item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(item) - self.owner.updateSelection() - self.owner.updateCanvasSize() - - def __str__(self): - return "Rotation of "+str(self.itemList)+str(self.indexList)+" by "+"%.3f"%(self.angle*180.0/math.pi)+" about "+str(self.origin) - -class addLabelAction(UndoRedoStack.action): - def __init__(self,owner,label): - self.owner = owner - self.label = label - UndoRedoStack.action.__init__(self,self.addF,self.unAddF) - - def addF(self): - self.owner.addItemToFile(self.label) - self.label.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.label) - - def unAddF(self): - self.label.removeFromCanvas() - del self.owner.fileItems[-1] - self.owner.propList.delete(0) - self.owner.clearSelection() - - def __str__(self): - return "Addition of a label" - -class deleteLabelAction(UndoRedoStack.action): - def __init__(self,owner,label,index): - self.owner = owner - self.label = label - self.index = index - UndoRedoStack.action.__init__(self,self.delF,self.unDelF) - - def delF(self): - self.owner.fileItems[self.index].removeFromCanvas() - self.owner.propList.delete(len(self.owner.fileItems)-self.index-1) - del self.owner.fileItems[self.index] - - def unDelF(self): - self.owner.fileItems.insert(self.index,self.label) - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.propList.insert(len(self.owner.fileItems)-self.index-1,self.owner.describeItem(self.label)) - self.owner.bindItemEvents(self.label) - - def __str__(self): - return "Deletion of a label" - -class editLabelTextAction(UndoRedoStack.action): - def __init__(self,owner,label,newText,oldText): - self.owner = owner - self.label = label - self.newText = newText - self.oldText = oldText - UndoRedoStack.action.__init__(self,self.modT,self.unModT) - - def modT(self): - self.label.label.setText(self.newText) - self.label.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.label) - - def unModT(self): - self.label.label.setText(self.oldText) - self.label.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.label) - - def __str__(self): - return "Editing a label's text" - -class editLabelPenAction(UndoRedoStack.action): - def __init__(self,owner,oldPen,newPen,index): - self.owner = owner - self.newPen = newPen - self.oldPen = oldPen - self.index = index - UndoRedoStack.action.__init__(self,self.editF,self.unEditF) - - def editF(self): - self.owner.fileItems[self.index].removeFromCanvas() - self.owner.fileItems[self.index].label.pen = self.newPen - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.owner.fileItems[self.index]) - - def unEditF(self): - self.owner.fileItems[self.index].removeFromCanvas() - self.owner.fileItems[self.index].label.pen = self.oldPen - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.owner.fileItems[self.index]) - - def __str__(self): - return "Changing a label's pen" - -class addScriptAction(UndoRedoStack.action): - def __init__(self,owner,script): - self.owner = owner - self.script = script - UndoRedoStack.action.__init__(self,self.addF,self.unAddF) - - def addF(self): - self.owner.addItemToFile(self.script) - self.script.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.script) - - def unAddF(self): - self.script.removeFromCanvas() - del self.owner.fileItems[-1] - self.owner.propList.delete(0) - self.owner.clearSelection() - - def __str__(self): - return "Addition of a script" - -class deleteScriptAction(UndoRedoStack.action): - def __init__(self,owner,script,index): - self.owner = owner - self.script = script - self.index = index - UndoRedoStack.action.__init__(self,self.delF,self.unDelF) - - def delF(self): - self.owner.fileItems[self.index].removeFromCanvas() - self.owner.propList.delete(len(self.owner.fileItems)-self.index-1) - del self.owner.fileItems[self.index] - - def unDelF(self): - self.owner.fileItems.insert(self.index,self.script) - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.propList.insert(len(self.owner.fileItems)-self.index-1,self.owner.describeItem(self.script)) - self.owner.bindItemEvents(self.script) - - def __str__(self): - return "Deletion of a script" - -class deleteScriptItemAction(UndoRedoStack.action): - def __init__(self,owner,script,indices,oldTransforms): - self.owner = owner - self.script = script - self.indices = indices[:] - UndoRedoStack.action.__init__(self,self.delI,self.unDelI) - def delI(self): - for index in self.indices: - self.script.transform[index].deleted = True - self.owner.mainCanvas.delete(self.script.imageList[index].IDTag) - - def unDelI(self): - for i in range(len(self.indices)): - index = self.indices[i] - self.script.transform[index].deleted = False - bbox = self.script.imageList[index].originalImage.bbox - self.script.imageList[index].IDTag = self.owner.mainCanvas.create_image(bbox[0],-bbox[3],anchor=NW,tags=("image"),image=self.script.imageList[index].itk) - self.owner.bindEvents(self.script.imageList[index].IDTag) - self.owner.resetStacking() - - def __str__(self): - return "Deletion of item "+str(self.indices)+" in "+str(self.script) - -class editScriptAction(UndoRedoStack.action): - def __init__(self,owner,script,newText,oldText): - self.owner = owner - self.script = script - self.newText = newText - self.oldText = oldText - UndoRedoStack.action.__init__(self,self.modS,self.unModS) - - def modS(self): - self.script.setScript(self.newText) - self.script.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.script) - - def unModS(self): - self.script.setScript(self.oldText) - self.script.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - self.owner.bindItemEvents(self.script) - - def __str__(self): - return "Modification of a script" - -class clearItemTransformsAction(UndoRedoStack.action): - def __init__(self,owner,item,oldTransforms): - self.owner = owner - self.item = item - self.oldTransforms = oldTransforms - UndoRedoStack.action.__init__(self,self.clearF,self.unClearF) - - def clearF(self): - for i in range(len(self.oldTransforms)): - self.item.transform[i] = xasy2asy.identity() - self.item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - - def unClearF(self): - for i in range(len(self.oldTransforms)): - self.item.transform[i] = self.oldTransforms[i] - self.item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification) - - def __str__(self): - return "Clear the transforms of "+str(self.item)+" from "+str(self.oldTransforms) - -class itemRaiseAction(UndoRedoStack.action): - def __init__(self,owner,items,oldPositions): - self.owner = owner - self.items = items[:] - self.oldPositions = oldPositions[:] - UndoRedoStack.action.__init__(self,self.raiseI,self.unRaiseI) - - def raiseI(self): - for item in self.items: - self.owner.raiseSomething(item) - - def unRaiseI(self): - length = len(self.owner.fileItems) - indices = self.oldPositions[:] - indices = [length-i-1 for i in indices] - indices.reverse() - for index in indices: - for i in range(index): - self.owner.raiseSomething(self.owner.fileItems[length-index-1]) - - def __str__(self): - return "Raise items "+str(self.items)+" from positions "+str(self.oldPositions) - -class itemLowerAction(UndoRedoStack.action): - def __init__(self,owner,items,oldPositions): - self.owner = owner - self.items = items[:] - self.oldPositions = oldPositions[:] - UndoRedoStack.action.__init__(self,self.lowerI,self.unLowerI) - - def lowerI(self): - for item in self.items: - self.owner.lowerSomething(item) - - def unLowerI(self): - indices = self.oldPositions[:] - indices.reverse() - for index in indices: - for i in range(index): - self.owner.lowerSomething(self.owner.fileItems[index]) - - def __str__(self): - return "Lower items "+str(self.items)+" from positions "+str(self.oldPositions) - -class addDrawnItemAction(UndoRedoStack.action): - def __init__(self,owner,item): - self.owner = owner - self.item = item - UndoRedoStack.action.__init__(self,self.drawF,self.unDrawF) - - def drawF(self): - self.owner.addItemToFile(self.item) - self.item.drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True) - self.owner.bindItemEvents(self.item) - - def unDrawF(self): - self.item.removeFromCanvas(self.owner.mainCanvas) - del self.owner.fileItems[-1] - self.owner.propList.delete(0) - self.owner.clearSelection() - - def __str__(self): - return "Drawing of an item" - -class deleteDrawnItemAction(UndoRedoStack.action): - def __init__(self,owner,item,index): - self.owner = owner - self.item = item - self.index = index - UndoRedoStack.action.__init__(self,self.delF,self.unDelF) - - def delF(self): - self.owner.fileItems[self.index].removeFromCanvas(self.owner.mainCanvas) - self.owner.propList.delete(len(self.owner.fileItems)-self.index-1) - del self.owner.fileItems[self.index] - - def unDelF(self): - self.owner.fileItems.insert(self.index,self.item) - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True) - self.owner.propList.insert(len(self.owner.fileItems)-self.index-1,self.owner.describeItem(self.item)) - self.owner.bindItemEvents(self.item) - - def __str__(self): - return "Deletion of a drawn item" - -class editDrawnItemAction(UndoRedoStack.action): - def __init__(self,owner,oldItem,newItem,index): - self.owner = owner - self.oldItem = oldItem - self.newItem = newItem - self.index = index - UndoRedoStack.action.__init__(self,self.editF,self.unEditF) - - def editF(self): - self.owner.fileItems[self.index].removeFromCanvas(self.owner.mainCanvas) - self.owner.fileItems[self.index].path = self.newItem.path - self.owner.fileItems[self.index].pen = self.newItem.pen - self.owner.fileItems[self.index].transform = self.newItem.transform - self.owner.fileItems[self.index].IDTag = self.newItem.IDTag - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True) - self.owner.bindItemEvents(self.owner.fileItems[self.index]) - - def unEditF(self): - self.owner.fileItems[self.index].removeFromCanvas(self.owner.mainCanvas) - self.owner.fileItems[self.index].path = self.oldItem.path - self.owner.fileItems[self.index].pen = self.oldItem.pen - self.owner.fileItems[self.index].transform = self.oldItem.transform - self.owner.fileItems[self.index].IDTag = self.oldItem.IDTag - self.owner.fileItems[self.index].drawOnCanvas(self.owner.mainCanvas,self.owner.magnification,forceAddition=True) - self.owner.bindItemEvents(self.owner.fileItems[self.index]) - - def __str__(self): - return "Modification of a drawn item" diff --git a/Master/texmf-dist/asymptote/GUI/xasyArgs.py b/Master/texmf-dist/asymptote/GUI/xasyArgs.py new file mode 100755 index 00000000000..045f4bc6692 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasyArgs.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import argparse +import xasyVersion +import PyQt5.QtCore as Qc +# Add arguments here. + + +def parseArgs(args): + parser = argparse.ArgumentParser(args) + parser.add_argument('-p', '--asypath', help='Custom Asymptote asy executable') + parser.add_argument('-v', '--version', help='Version number', action='version', + version='xasy v{0}'.format(xasyVersion.xasyVersion)) + parser.add_argument('-l', '--language', help='language') + parser.add_argument('-x', '--mag', help='Magnification. Defaults to 1', default=1, type=float) + + parser.add_argument( + 'filename', help='Filename to load. If empty, initializes empty xasy canvas.', nargs='?', default=None) + + return parser.parse_args() + + +def getArgs(): + return parseArgs(Qc.QCoreApplication.arguments()) diff --git a/Master/texmf-dist/asymptote/GUI/xasyBezierEditor.py b/Master/texmf-dist/asymptote/GUI/xasyBezierEditor.py deleted file mode 100755 index 75ddeafa2d2..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyBezierEditor.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# xasyBezierEditor implements the ability to graphically edit the location -# of the nodes and control points of a bezier curve. -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -########################################################################### - -import math -import sys -from CubicBezier import * -import xasy2asy - -if sys.version_info >= (3, 0): - from tkinter import * -else: - from Tkinter import * - -class node: - def __init__(self,precontrol,node,postcontrol,uid,isTied = True): - self.node = node - self.precontrol = precontrol - self.postcontrol = postcontrol - self.isTied = isTied - self.uid = uid - self.nodeID = self.precontrolID = self.prelineID = self.postcontrolID = self.postlineID = None - - def shiftNode(self,delta): - self.node = (self.node[0]+delta[0],self.node[1]+delta[1]) - if self.precontrol != None: - self.precontrol = (self.precontrol[0]+delta[0],self.precontrol[1]+delta[1]) - if self.postcontrol != None: - self.postcontrol = (self.postcontrol[0]+delta[0],self.postcontrol[1]+delta[1]) - - def shiftPrecontrol(self,delta): - self.precontrol = (self.precontrol[0]+delta[0],self.precontrol[1]+delta[1]) - if self.isTied and self.postcontrol != None: - self.rotatePostControl(self.precontrol) - - def shiftPostcontrol(self,delta): - self.postcontrol = (self.postcontrol[0]+delta[0],self.postcontrol[1]+delta[1]) - if self.isTied and self.precontrol != None: - self.rotatePrecontrol(self.postcontrol) - - def rotatePrecontrol(self,after): - vx,vy = after[0]-self.node[0],after[1]-self.node[1] - l = norm((vx,vy)) - if l == 0: - return - m = norm((self.precontrol[0]-self.node[0],self.precontrol[1]-self.node[1])) - vx = -m*vx/l - vy = -m*vy/l - self.precontrol = self.node[0]+vx,self.node[1]+vy - - def rotatePostControl(self,after): - vx,vy = after[0]-self.node[0],after[1]-self.node[1] - l = norm((vx,vy)) - if l == 0: - return - m = norm((self.postcontrol[0]-self.node[0],self.postcontrol[1]-self.node[1])) - vx = -m*vx/l - vy = -m*vy/l - self.postcontrol = self.node[0]+vx,self.node[1]+vy - - def draw(self,canvas): - width = 3 - if self.precontrol != None: - if self.prelineID == None: - self.prelineID = canvas.create_line(self.precontrol[0],-self.precontrol[1],self.node[0],-self.node[1],tags=("preline",self.uid)) - else: - canvas.coords(self.prelineID,self.precontrol[0],-self.precontrol[1],self.node[0],-self.node[1]) - if self.precontrolID == None: - self.precontrolID = canvas.create_oval(self.precontrol[0]-width,-self.precontrol[1]-width,self.precontrol[0]+width,-self.precontrol[1]+width, - fill="red",outline="black",tags=("precontrol",self.uid)) - else: - canvas.coords(self.precontrolID,self.precontrol[0]-width,-self.precontrol[1]-width,self.precontrol[0]+width,-self.precontrol[1]+width) - if self.postcontrol != None: - if self.postlineID == None: - self.postlineID = canvas.create_line(self.postcontrol[0],-self.postcontrol[1],self.node[0],-self.node[1],tags=("postline",self.uid)) - else: - canvas.coords(self.postlineID,self.postcontrol[0],-self.postcontrol[1],self.node[0],-self.node[1]) - if self.postcontrolID == None: - self.postcontrolID = canvas.create_oval(self.postcontrol[0]-width,-self.postcontrol[1]-width,self.postcontrol[0]+width,-self.postcontrol[1]+width, - fill="red",outline="black",tags=("postcontrol",self.uid)) - else: - canvas.coords(self.postcontrolID,self.postcontrol[0]-width,-self.postcontrol[1]-width,self.postcontrol[0]+width,-self.postcontrol[1]+width) - if self.isTied: - color = "blue" - else: - color = "green" - if self.nodeID == None: - self.nodeID = canvas.create_oval(self.node[0]-width,-self.node[1]-width,self.node[0]+width,-self.node[1]+width, - fill=color,outline="black",tags=("node",self.uid)) - else: - canvas.coords(self.nodeID,self.node[0]-width,-self.node[1]-width,self.node[0]+width,-self.node[1]+width) - canvas.itemconfigure(self.nodeID,fill=color) - -class xasyBezierEditor: - def __init__(self,parent,shape,canvas): - self.parent = parent - self.shape = shape - self.transform = self.shape.transform[0] - self.path = self.shape.path - self.canvas = canvas - self.modified = False - self.path.computeControls() - isCyclic = self.path.nodeSet[-1] == 'cycle' - segments = len(self.path.controlSet) - self.nodeList = [] - for i in range(segments): - if i == 0: - node0 = self.transform*self.path.nodeSet[i] - control = self.transform*self.path.controlSet[i][0] - self.nodeList.append(node(None,node0,control,len(self.nodeList))) - else: - node0 = self.transform*self.path.nodeSet[i] - precontrol = self.transform*self.path.controlSet[i-1][1] - postcontrol = self.transform*self.path.controlSet[i][0] - self.nodeList.append(node(precontrol,node0,postcontrol,len(self.nodeList))) - if not isCyclic: - node0 = self.transform*self.path.nodeSet[-1] - precontrol = self.transform*self.path.controlSet[-1][1] - self.nodeList.append(node(precontrol,node0,None,len(self.nodeList))) - else: - self.nodeList[0].precontrol = self.transform*self.path.controlSet[-1][1] - self.showControls() - self.bindNodeEvents() - self.bindControlEvents() - - def showControls(self): - for n in self.nodeList: - n.draw(self.canvas) - self.bindNodeEvents() - self.bindControlEvents() - self.parent.updateCanvasSize() - - def bindNodeEvents(self): - self.canvas.tag_bind("node","<B1-Motion>",self.nodeDrag) - self.canvas.tag_bind("node","<Button-1>",self.buttonDown) - self.canvas.tag_bind("node","<Double-Button-1>",self.toggleNode) - - def unbindNodeEvents(self): - self.canvas.tag_unbind("node","<B1-Motion>") - self.canvas.tag_unbind("node","<Button-1>") - self.canvas.tag_unbind("node","<Double-Button-1>") - - def bindControlEvents(self): - self.canvas.tag_bind("precontrol || postcontrol","<B1-Motion>",self.controlDrag) - self.canvas.tag_bind("precontrol || postcontrol","<Button-1>",self.buttonDown) - - def unbindControlEvents(self): - self.canvas.tag_unbind("precontrol || postcontrol","<B1-Motion>") - self.canvas.tag_unbind("precontrol || postcontrol","<Button-1>") - - def buttonDown(self,event): - self.parent.freeMouseDown = False - self.startx,self.starty = event.x,event.y - - def toggleNode(self,event): - self.parent.freeMouseDown = False - tags = self.canvas.gettags(CURRENT) - obj = tags[0] - uid = int(tags[1]) - self.nodeList[uid].isTied = not self.nodeList[uid].isTied - self.showControls() - - def nodeDrag(self,event): - self.parent.freeMouseDown = False - deltax = event.x-self.startx - deltay = event.y-self.starty - tags = self.canvas.gettags(CURRENT) - obj = tags[0] - uid = int(tags[1]) - self.nodeList[uid].shiftNode((deltax,-deltay)) - self.startx,self.starty = event.x,event.y - self.applyChanges() - self.showControls() - self.shape.drawOnCanvas(self.canvas,self.parent.magnification) - - def controlDrag(self,event): - self.parent.freeMouseDown = False - deltax = event.x-self.startx - deltay = event.y-self.starty - tags = self.canvas.gettags(CURRENT) - obj = tags[0] - uid = int(tags[1]) - if obj == "precontrol": - self.nodeList[uid].shiftPrecontrol((deltax,-deltay)) - elif obj == "postcontrol": - self.nodeList[uid].shiftPostcontrol((deltax,-deltay)) - self.startx,self.starty = event.x,event.y - self.applyChanges() - self.showControls() - self.shape.drawOnCanvas(self.canvas,self.parent.magnification) - - def applyChanges(self): - self.modified = True - self.shape.transform[0] = xasy2asy.asyTransform((0,0,1,0,0,1)) - for i in range(len(self.nodeList)): - self.path.nodeSet[i] = self.nodeList[i].node - if self.nodeList[i].postcontrol != None: - self.path.controlSet[i][0] = self.nodeList[i].postcontrol - if self.nodeList[i].precontrol != None: - self.path.controlSet[i-1][1] = self.nodeList[i].precontrol - - def endEdit(self): - self.unbindNodeEvents() - self.unbindControlEvents() - self.canvas.delete("node || precontrol || postcontrol || preline || postline") diff --git a/Master/texmf-dist/asymptote/GUI/xasyBezierInterface.py b/Master/texmf-dist/asymptote/GUI/xasyBezierInterface.py new file mode 100755 index 00000000000..0a5c689bffe --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasyBezierInterface.py @@ -0,0 +1,375 @@ +#!/usr/bin/env python3 + +import xasy2asy as x2a +import xasyUtils as xu + +import PyQt5.QtCore as Qc +import PyQt5.QtGui as Qg +import PyQt5.QtWidgets as Qw + +import Widg_editBezier as Web + +import InplaceAddObj + +import math + +class CurrentlySelctedType: + none = -1 + node = 0 + ctrlPoint = 1 + +class InteractiveBezierEditor(InplaceAddObj.InplaceObjProcess): + editAccepted = Qc.pyqtSignal() + editRejected = Qc.pyqtSignal() + + def __init__(self, parent: Qc.QObject, obj: x2a.xasyDrawnItem, info: dict={}): + super().__init__(parent) + self.info = info + self.asyPathBackup = x2a.asyPath.fromPath(obj.path) + self.asyPath = obj.path + self.curveMode = self.asyPath.containsCurve + assert isinstance(self.asyPath, x2a.asyPath) + self.transf = obj.transfKeymap[obj.transfKey][0] + self._active = True + + self.currentSelMode = None + # (Node index, Node subindex for ) + self.currentSelIndex = (None, 0) + + self.nodeSelRects = [] + self.ctrlSelRects = [] + + self.setSelectionBoundaries() + + self.lastSelPoint = None + self.preCtrlOffset = None + self.postCtrlOffset = None + self.inTransformMode = False + + self.opt = None + + self.prosectiveNodes = [] + self.prospectiveCtrlPts = [] + + def setSelectionBoundaries(self): + self.nodeSelRects = self.handleNodeSelectionBounds() + + if self.curveMode: + self.ctrlSelRects = self.handleCtrlSelectionBoundaries() + + def handleNodeSelectionBounds(self): + nodeSelectionBoundaries = [] + + for node in self.asyPath.nodeSet: + if node == 'cycle': + nodeSelectionBoundaries.append(None) + continue + + selEpsilon = 6/self.info['magnification'] + newRect = Qc.QRect(0, 0, 2 * selEpsilon, 2 * selEpsilon) + x, y = self.transf * node + x = int(round(x)) + y = int(round(y)) + newRect.moveCenter(Qc.QPoint(x, y)) + + nodeSelectionBoundaries.append(newRect) + + return nodeSelectionBoundaries + + def handleCtrlSelectionBoundaries(self): + ctrlPointSelBoundaries = [] + + for nodes in self.asyPath.controlSet: + nodea, nodeb = nodes + + selEpsilon = 6/self.info['magnification'] + + newRect = Qc.QRect(0, 0, 2 * selEpsilon, 2 * selEpsilon) + newRectb = Qc.QRect(0, 0, 2 * selEpsilon, 2 * selEpsilon) + + x, y = self.transf * nodea + x2, y2 = self.transf * nodeb + + x = int(round(x)) + y = int(round(y)) + + x2 = int(round(x2)) + y2 = int(round(y2)) + + newRect.moveCenter(Qc.QPoint(x, y)) + newRectb.moveCenter(Qc.QPoint(x2, y2)) + + ctrlPointSelBoundaries.append((newRect, newRectb)) + + return ctrlPointSelBoundaries + + def postDrawPreview(self, canvas: Qg.QPainter): + assert canvas.isActive() + + dashedPen = Qg.QPen(Qc.Qt.DashLine) + dashedPen.setWidthF(1/self.info['magnification']) + # draw the base points + canvas.save() + canvas.setWorldTransform(self.transf.toQTransform(), True) + + epsilonSize = 6/self.info['magnification'] + + if self.info['autoRecompute'] or not self.curveMode: + ctrlPtsColor = 'gray' + else: + ctrlPtsColor = 'red' + + canvas.setPen(dashedPen) + + canvas.drawPath(self.asyPath.toQPainterPath()) + + nodePen = Qg.QPen(Qg.QColor('blue')) + nodePen.setWidthF(1/self.info['magnification']) + + ctlPtsPen = Qg.QPen(Qg.QColor(ctrlPtsColor)) + ctlPtsPen.setWidthF(1/self.info['magnification']) + + for index in range(len(self.asyPath.nodeSet)): + point = self.asyPath.nodeSet[index] + + if point != 'cycle': + basePoint = Qc.QPointF(point[0], point[1]) + canvas.setPen(nodePen) + canvas.drawEllipse(basePoint, epsilonSize, epsilonSize) + else: + point = self.asyPath.nodeSet[0] + basePoint = Qc.QPointF(point[0], point[1]) + if self.curveMode: + if index != 0: + canvas.setPen(ctlPtsPen) + postCtrolSet = self.asyPath.controlSet[index - 1][1] + postCtrlPoint = Qc.QPointF(postCtrolSet[0], postCtrolSet[1]) + canvas.drawEllipse(postCtrlPoint, epsilonSize, epsilonSize) + + canvas.setPen(dashedPen) + canvas.drawLine(basePoint, postCtrlPoint) + + if index != len(self.asyPath.nodeSet) - 1: + canvas.setPen(ctlPtsPen) + preCtrlSet = self.asyPath.controlSet[index][0] + preCtrlPoint = Qc.QPointF(preCtrlSet[0], preCtrlSet[1]) + canvas.drawEllipse(preCtrlPoint, epsilonSize, epsilonSize) + + canvas.setPen(dashedPen) + canvas.drawLine(basePoint, preCtrlPoint) + + canvas.restore() + + def getPreAndPostCtrlPts(self, index): + isCycle = self.asyPath.nodeSet[-1] == 'cycle' + + if index == 0 and not isCycle: + preCtrl = None + else: + preCtrl = self.asyPath.controlSet[index - 1][1] + + if index == len(self.asyPath.nodeSet) - 1 and not isCycle: + postCtrl = None + else: + postCtrl = self.asyPath.controlSet[index % (len(self.asyPath.nodeSet) - 1)][0] + + return preCtrl, postCtrl + + def findLinkingNode(self, index, subindex): + """index and subindex are of the control points list.""" + if subindex == 0: + return index + else: + if self.asyPath.nodeSet[index + 1] == 'cycle': + return 0 + else: + return index + 1 + + def resetObj(self): + self.asyPath.setInfo(self.asyPathBackup) + self.setSelectionBoundaries() + + def mouseDown(self, pos, info, mouseEvent: Qg.QMouseEvent=None): + self.lastSelPoint = pos + if self.inTransformMode: + return + + if self.prosectiveNodes and not self.inTransformMode: + self.currentSelMode = CurrentlySelctedType.node + self.currentSelIndex = (self.prosectiveNodes[0], 0) + self.inTransformMode = True + self.parentNodeIndex = self.currentSelIndex[0] + elif self.prospectiveCtrlPts and not self.inTransformMode: + self.currentSelMode = CurrentlySelctedType.ctrlPoint + self.currentSelIndex = self.prospectiveCtrlPts[0] + self.inTransformMode = True + self.parentNodeIndex = self.findLinkingNode(*self.currentSelIndex) + + if self.inTransformMode: + parentNode = self.asyPath.nodeSet[self.parentNodeIndex] + + # find the offset of each control point to the node + if not self.curveMode: + return + + preCtrl, postCtrl = self.getPreAndPostCtrlPts(self.parentNodeIndex) + + if parentNode == 'cycle': + parentNode = self.asyPath.nodeSet[0] + self.parentNodeIndex = 0 + + if preCtrl is not None: + self.preCtrlOffset = xu.funcOnList( + preCtrl, parentNode, lambda a, b: a - b) + else: + self.preCtrlOffset = None + + if postCtrl is not None: + self.postCtrlOffset = xu.funcOnList( + postCtrl, parentNode, lambda a, b: a - b) + else: + self.postCtrlOffset = None + + def mouseMove(self, pos, event: Qg.QMouseEvent): + if self.currentSelMode is None and not self.inTransformMode: + # in this case, search for prosective nodes. + prospectiveNodes = [] + prospectiveCtrlpts = [] + + for i in range(len(self.nodeSelRects)): + rect = self.nodeSelRects[i] + if rect is None: + continue + if rect.contains(pos): + prospectiveNodes.append(i) + + self.prosectiveNodes = prospectiveNodes + + if not self.info['autoRecompute'] and self.curveMode: + for i in range(len(self.ctrlSelRects)): + recta, rectb = self.ctrlSelRects[i] + + if recta.contains(pos): + prospectiveCtrlpts.append((i, 0)) + + if rectb.contains(pos): + prospectiveCtrlpts.append((i, 1)) + + self.prospectiveCtrlPts = prospectiveCtrlpts + else: + self.prospectiveCtrlPts = [] + + + if self.inTransformMode: + index, subindex = self.currentSelIndex + deltaPos = pos - self.lastSelPoint + newNode = (pos.x(), pos.y()) + if self.currentSelMode == CurrentlySelctedType.node: + # static throughout the moving + if self.asyPath.nodeSet[index] == 'cycle': + return + + self.asyPath.setNode(index, newNode) + # if also move node: + + if self.curveMode: + checkPre, checkPost = self.getPreAndPostCtrlPts(index) + + if 1 == 1: # TODO: Replace this with an option to also move control pts. + if checkPre is not None: + self.asyPath.controlSet[index - 1][1] = xu.funcOnList( + newNode, self.preCtrlOffset, lambda a, b: a + b + ) + if checkPost is not None: + self.asyPath.controlSet[index][0] = xu.funcOnList( + newNode, self.postCtrlOffset, lambda a, b: a + b + ) + + if self.info['autoRecompute']: + self.quickRecalculateCtrls() + + + elif self.currentSelMode == CurrentlySelctedType.ctrlPoint and self.curveMode: + self.asyPath.controlSet[index][subindex] = newNode + parentNode = self.asyPath.nodeSet[self.parentNodeIndex] + + if parentNode == 'cycle': + parentNode = self.asyPath.nodeSet[0] + isCycle = True + else: + isCycle = False + + if self.parentNodeIndex == 0 and self.asyPath.nodeSet[-1] == 'cycle': + isCycle = True + + rawNewNode = xu.funcOnList(newNode, parentNode, lambda a, b: a - b) + rawAngle = math.atan2(rawNewNode[1], rawNewNode[0]) + newNorm = xu.twonorm(rawNewNode) + + + if self.info['editBezierlockMode'] >= Web.LockMode.angleLock: + otherIndex = 1 - subindex # 1 if 0, 0 otherwise. + if otherIndex == 0: + if index < (len(self.asyPath.controlSet) - 1) or isCycle: + newIndex = 0 if isCycle else index + 1 + + oldOtherCtrlPnt = xu.funcOnList( + self.asyPath.controlSet[newIndex][0], parentNode, lambda a, b: a - b) + + if self.info['editBezierlockMode'] >= Web.LockMode.angleAndScaleLock: + rawNorm = newNorm + else: + rawNorm = xu.twonorm(oldOtherCtrlPnt) + + newPnt = (rawNorm * math.cos(rawAngle + math.pi), + rawNorm * math.sin(rawAngle + math.pi)) + + self.asyPath.controlSet[newIndex][0] = xu.funcOnList( + newPnt, parentNode, lambda a, b: a + b) + else: + if index > 0 or isCycle: + newIndex = -1 if isCycle else index - 1 + oldOtherCtrlPnt = xu.funcOnList( + self.asyPath.controlSet[newIndex][1], parentNode, lambda a, b: a - b) + + if self.info['editBezierlockMode'] >= Web.LockMode.angleAndScaleLock: + rawNorm = newNorm + else: + rawNorm = xu.twonorm(oldOtherCtrlPnt) + + newPnt = (rawNorm * math.cos(rawAngle + math.pi), + rawNorm * math.sin(rawAngle + math.pi)) + self.asyPath.controlSet[newIndex][1] = xu.funcOnList( + newPnt, parentNode, lambda a, b: a + b) + + def recalculateCtrls(self): + self.quickRecalculateCtrls() + self.setSelectionBoundaries() + + def quickRecalculateCtrls(self): + self.asyPath.controlSet.clear() + self.asyPath.computeControls() + + def mouseRelease(self): + if self.inTransformMode: + self.inTransformMode = False + self.currentSelMode = None + + self.setSelectionBoundaries() + + def forceFinalize(self): + self.objectUpdated.emit() + + def createOptWidget(self, info): + self.opt = Web.Widg_editBezier(self.info, self.curveMode) + self.opt.ui.btnOk.clicked.connect(self.editAccepted) + self.opt.ui.btnCancel.clicked.connect(self.editRejected) + self.opt.ui.btnForceRecompute.clicked.connect(self.recalculateCtrls) + + return self.opt + + def getObject(self): + pass + + def getXasyObject(self): + pass diff --git a/Master/texmf-dist/asymptote/GUI/xasyCodeEditor.py b/Master/texmf-dist/asymptote/GUI/xasyCodeEditor.py deleted file mode 100755 index 8f3de4034fe..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyCodeEditor.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# xasyCodeEditor implements a simple text editor for Asymptote scripts in -# xasy. -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -############################################################################ - -from subprocess import call -from tempfile import mkstemp -from os import remove -from os import fdopen -from os import path -import xasyOptions - -def getText(text=""): - """Launch the external editor""" - temp = mkstemp() - tempf = fdopen(temp[0],"w") - tempf.write(text) - tempf.close() - try: - cmdpath,cmd = path.split(path.expandvars(xasyOptions.options['externalEditor'])) - split_cmd = cmd.split() - cmdpart = [path.join(cmdpath,split_cmd[0])] - argpart = split_cmd[1:]+[temp[1]] - arglist = cmdpart+argpart - call(arglist) - except Exception as e: - raise Exception('Error launching external editor.') - - try: - tempf = open(temp[1],"r") - text = tempf.read() - tempf.close() - remove(temp[1]) - except Exception as e: - raise Exception('Error reading from external editor.') - return text - -if __name__ == '__main__': - #run a test - print (getText("Here is some text to edit")) diff --git a/Master/texmf-dist/asymptote/GUI/xasyColorPicker.py b/Master/texmf-dist/asymptote/GUI/xasyColorPicker.py deleted file mode 100755 index 217835bdbb0..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyColorPicker.py +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# xasyColorPicker implements a dialog that allows a user to choose a color -# from those already defined in Asymptote or a custom RGB color. -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -############################################################################ - -import sys - -if sys.version_info >= (3, 0): - from tkinter import * - from tkinter import colorchooser -else: - from Tkinter import * - import tkColorChooser as colorchooser - -asyColors = { "black":(0,0,0), - "white":(1,1,1), - "gray":(0.5,0.5,0.5), - "red":(1,0,0), - "green":(0,1,0), - "blue":(0,0,1), - "cmyk":(1,1,1), - "Cyan":(0,1,1), - "Magenta":(1,0,1), - "Yellow":(1,1,0), - "Black":(0,0,0), - "cyan":(0,1,1), - "magenta":(1,0,1), - "yellow":(1,1,0), - "palered":(1,0.75,0.75), - "palegreen":(0.75,1,0.75), - "paleblue":(0.75,0.75,1), - "palecyan":(0.75,1,1), - "palemagenta":(1,0.75,1), - "paleyellow":(1,1,0.75), - "palegray":(0.95,0.95,0.95), - "lightred":(1,0.5,0.5), - "lightgreen":(0.5,1,0.5), - "lightblue":(0.5,0.5,1), - "lightcyan":(0.5,1,1), - "lightmagenta":(1,0.5,1), - "lightyellow":(1,1,0.5), - "lightgray":(0.9,0.9,0.9), - "mediumred":(1,0.25,0.25), - "mediumgreen":(0.25,1,0.25), - "mediumblue":(0.25,0.25,1), - "mediumcyan":(0.25,1,1), - "mediummagenta":(1,0.25,1), - "mediumyellow":(1,1,0.25), - "mediumgray":(0.75,0.75,0.75), - "heavyred":(0.75,0,0), - "heavygreen":(0,0.75,0), - "heavyblue":(0,0,0.75), - "heavycyan":(0,0.75,0.75), - "heavymagenta":(0.75,0,0.75), - "lightolive":(0.75,0.75,0), - "heavygray":(0.25,0.25,0.25), - "deepred":(0.5,0,0), - "deepgreen":(0,0.5,0), - "deepblue":(0,0,0.5), - "deepcyan":(0,0.5,0.5), - "deepmagenta":(0.5,0,0.5), - "olive":(0.5,0.5,0), - "deepgray":(0.1,0.1,0.1), - "darkred":(0.25,0,0), - "darkgreen":(0,0.25,0), - "darkblue":(0,0,0.25), - "darkcyan":(0,0.25,0.25), - "darkmagenta":(0.25,0,0.25), - "darkolive":(0.25,0.25,0), - "darkgray":(0.05,0.05,0.05), - "orange":(1,0.5,0), - "fuchsia":(1,0,0.5), - "chartreuse":(0.5,1,0), - "springgreen":(0,1,0.5), - "purple":(0.5,0,1), - "royalblue":(0,0.5,1) - } -colorLayout = [['palered', - 'lightred', - 'mediumred', - 'red', - 'heavyred', - 'deepred', - 'darkred', - 'palegreen', - 'lightgreen', - 'mediumgreen', - 'green', - 'heavygreen', - 'deepgreen', - 'darkgreen', - 'paleblue', - 'lightblue', - 'mediumblue', - 'blue', - 'heavyblue', - 'deepblue', - 'darkblue'], - ['palecyan', - 'lightcyan', - 'heavycyan', - 'deepcyan', - 'darkcyan', - 'palemagenta', - 'lightmagenta', - 'mediummagenta', - 'magenta', - 'heavymagenta', - 'deepmagenta', - 'darkmagenta', - 'yellow', - 'lightyellow', - 'mediumyellow', - 'yellow', - 'lightolive', - 'olive', - 'darkolive', - 'palegray', - 'lightgray', - 'mediumgray', - 'gray', - 'heavygray', - 'deepgray', - 'darkgray'], - ['black', - 'white', - 'orange', - 'fuchsia', - 'chartreuse', - 'springgreen', - 'purple', - 'royalblue', - 'Cyan', - 'Magenta', - 'Yellow', - 'Black']] - -def makeRGBfromTkColor(tkColor): - """Convert a Tk color of the form #rrggbb to an asy rgb color""" - r = int('0x'+tkColor[1:3],16) - g = int('0x'+tkColor[3:5],16) - b = int('0x'+tkColor[5:7],16) - r /= 255.0 - g /= 255.0 - b /= 255.0 - return (r,g,b) - -def RGBreal255(rgb): - """Convert an RGB color from 0-1 to 0-255""" - return [min(int(256*a),255) for a in rgb] - -def RGB255hex(rgb): - """Make a color in the form #rrggbb in hex from r,g,b in 0-255""" - return "#{}".format("".join(["{:02x}".format(a) for a in rgb])) - -class xasyColorDlg(Toplevel): - """A dialog for choosing an asymptote color. It displays the usual asy presets and allows custom rgb colors""" - def __init__(self,master=None,color=(0,0,0)): - Toplevel.__init__(self,master,width=500,height=500) - self.resizable(False,False) - self.parent = master - self.title("Color Picker") - self.transient(master) - self.focus_set() - self.wait_visibility() - self.grab_set() - self.color = self.oldColor = color - cwidth = 120 - rheight = 20 - self.pframe=Frame(self,bd=0) - self.pframe.rowconfigure(0,weight=1) - self.pframe.columnconfigure(0,weight=1) - Label(self.pframe,text="Color Presets").grid(row=0,column=0) - self.colScroll = Scrollbar(self.pframe,orient=VERTICAL) - self.colorList = Canvas(self.pframe, width=cwidth*len(colorLayout), scrollregion=(0,0,20+cwidth*len(colorLayout),20+rheight*max([len(i) for i in colorLayout])),yscrollcommand=self.colScroll.set,relief=FLAT) - self.colScroll.config(command=self.colorList.yview) - self.colScroll.grid(row=1,column=1,sticky=N+S) - self.colorList.grid(row=1,column=0,sticky=W) - ccount = 0 - for column in colorLayout: - rcount = 0 - for name in column: - self.colorList.create_rectangle(10+cwidth*ccount,10+rheight*rcount,cwidth*ccount+25,rheight*rcount+25,tags=(name,"preset"),fill=RGB255hex(RGBreal255(asyColors[name]))) - self.colorList.create_text(cwidth*ccount+30,10+rheight*rcount,text=name,anchor=NW,tags=(name,"preset"),fill="black",activefill=RGB255hex(RGBreal255(asyColors[name]))) - rcount += 1 - ccount += 1 - self.colorList.tag_bind("preset","<Button-1>",self.setColorEvt) - Button(self,text="Custom color...",command=self.getCustom).grid(row=2,column=0,sticky=W,padx=5,pady=5) - self.colDisp = Canvas(self,width=200,height=20,background=RGB255hex(RGBreal255(self.color)),relief=SUNKEN, bd=3) - self.colDisp.grid(row=2,column=1,columnspan=2) - self.rowconfigure(3,minsize=10) - self.columnconfigure(0,weight=1) - self.columnconfigure(1,weight=1) - self.columnconfigure(2,weight=1) - Button(self,text="OK",default=ACTIVE,command=self.destroy).grid(row=4,column=1,sticky=E+W,padx=5,pady=5) - Button(self,text="Cancel",command=self.cancel).grid(row=4,column=2,sticky=E+W,padx=5,pady=5) - self.pframe.grid(row=1,column=0,columnspan=3,padx=10,pady=10) - self.bind("<Return>",self.closeUp) - self.setColor(color) - def closeUp(self,event): - """Close the dialog forcibly""" - self.destroy() - def getCustom(self): - """Request a custom RGB color using a colorchooser""" - result=colorchooser.askcolor(initialcolor=RGB255hex(RGBreal255(self.color)),title="Custom Color",parent=self) - if result != (None,None): - self.setColor((result[0][0]/255.0,result[0][1]/255.0,result[0][2]/255.0)) - def cancel(self): - """Respond to the user pressing cancel""" - self.color = self.oldColor - self.destroy() - def setColor(self,color): - """Save the color and update the color display""" - self.color = color - self.colDisp.configure(background=RGB255hex(RGBreal255(self.color))) - def setColorEvt(self,event): - """Respond to the user clicking a color from the palette""" - self.setColor(asyColors[self.colorList.gettags(CURRENT)[0]]) - def getColor(self,initialColor=(0,0,0)): - """Use this method to prompt for a color. It returns the new color or the old color if the user cancelled the operation. - - e.g: - print (xasyColorDlg(Tk()).getColor((1,1,0))) - """ - self.setColor(initialColor) - self.oldColor = initialColor - self.wait_window(self) - return self.color - -if __name__ == '__main__': - root = Tk() - Button(root,text="Pick Color",command=lambda:xasyColorDlg(root).getColor()).pack() - root.mainloop() diff --git a/Master/texmf-dist/asymptote/GUI/xasyFile.py b/Master/texmf-dist/asymptote/GUI/xasyFile.py index 7516fc7d3b5..eee6d69a856 100755 --- a/Master/texmf-dist/asymptote/GUI/xasyFile.py +++ b/Master/texmf-dist/asymptote/GUI/xasyFile.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ########################################################################### # # xasyFile implements the loading, parsing, and saving of an xasy file. @@ -10,306 +10,74 @@ ############################################################################ from string import * -from xasy2asy import * +import xasy2asy as x2a +import io import re -class xasyParseError(Exception): - """A parsing error""" - pass - -class xasyFileError(Exception): - """An i/o error or other error not related to parsing""" - pass - -def parseFile(inFile): - """Parse a file returning a list of xasyItems""" - lines = inFile.read() - lines = lines.splitlines() - #lines = [line for line in lines.splitlines() if not line.startswith("//")] - result = [] - if lines[0] != "initXasyMode();": - raise xasyFileError("Invalid file format: First line must be \"initXasyMode();\"") - lines.pop(0) - lineCount = 2 - lineNum = len(lines) - while lineNum > 0: - line = lines[0] - lines.pop(0) - if not line.isspace() and len(line)>0: - try: - #print ("Line {:d}: {:s}".format(lineCount,line)) - lineResult = parseLine(line.strip(),lines) - except: - raise xasyParseError("Parsing error: line {:d} in {:s}\n{:s}".format(lineCount,inFile.name,line)) - - if lineResult != None: - result.append(lineResult) - #print ("\tproduced: {:s}".format(str(lineResult))) - lineCount += lineNum-len(lines) - lineNum = len(lines) - return result - -transformPrefix = "xformStack" -scriptPrefix = "startScript(); {" -scriptSuffix = "} endScript();" -def extractScript(lines): - """Find the code belonging to a script item""" - theScript = "" - line = lines.pop(0) - level = 1 - while level > 0: - check = line.lstrip() - while check.endswith(scriptSuffix): - level -= 1 - line = line[:len(line)-len(scriptSuffix)] - check = line.lstrip() - if check.startswith(scriptPrefix): - level += 1 - theScript += line + "\n" - if level > 0: - line = lines.pop(0) - - global pendingTransformsD - ts = pendingTransformsD[:] - pendingTransformsD = [] - return xasyScript(None,script=theScript,transforms=ts[:]) - -pendingTransforms = [] -pendingTransformsD = [] -def addTransform(index,t,active=1): - """Place a transform in the list of transforms, expanding the list as needed""" - while len(pendingTransformsD) < index+1: - pendingTransformsD.append(identity()) - deleted = int(active==0) - pendingTransformsD[index]=asyTransform(t,deleted) - -def parseIndexedTransforms(args): - """Parse a list of indexedTransforms, adding them to the current list of transforms""" - global pendingTransformsD - pendingTransformsD = [] - args = args.replace("indexedTransform","") - false = 0 - tList = [eval(a) for a in ")?(".join(args.split("),(")).split("?")] - for a in tList: - addTransform(*a) - -def parseTransformExpression(line): - """Parse statements related to the xformStack - - Syntax: - xformStack.push(transform) - e.g.: xformStack.push((0,0,1,0,0,1)); //the identity - xformStack.add(indexedTransform(index,transform)[,...]) - e.g.: xformStack.add(indexedTransform(1,(0,0,1,0,0,1)); - """ - global pendingTransforms - stackCmd = line[len(transformPrefix)+1:line.find("(")] - if line[-2:] != ");": - raise xasyParseError("Invalid syntax") - args = line[line.find("(")+1:-2] - if stackCmd == "push": - t = asyTransform(eval(args)) - pendingTransforms.append(t) - elif stackCmd == "add": - parseIndexedTransforms(args) - else: - raise xasyParseError("Invalid transform stack command.") - return None - -def parseLabel(line): - """Parse an asy Label statement, returning an xasyText item""" - if not (line.startswith("Label(") and line.endswith(",align=SE)")): - raise xasyParseError("Invalid syntax") - args = line[6:-1] - loc2 = args.rfind(",align=SE") - loc1 = args.rfind(",",0,loc2-1) - loc = args.rfind(",(",0,loc1-1) - if loc < 2: - raise xasyParseError("Invalid syntax") - text = args[1:loc-1] - location = eval(args[loc+1:args.find("),",loc)+1]) - pen = args[loc:loc2] - pen = pen[pen.find(",")+1:] - pen = pen[pen.find(",")+1:] - pen = pen[pen.find(",")+1:] - global pendingTransforms - return xasyText(text,location,parsePen(pen),pendingTransforms.pop()) - -def parseLabelCommand(line): - """Parse a label command returning an xasyText object - - Syntax: - label(Label(text,location,pen,align=SE)); - e.g.: label(Label("Hello world!",(0,0),rgb(0,0,0)+0.5,align=SE)); - """ - if line[-2:] != ");": - raise xasyParseError("Invalid syntax") - arguments = line[6:-2] - return parseLabel(arguments) -def parseDrawCommand(line): - """Parse a draw command returning an xasyShape object - - Syntax: - draw(path,pen); - e.g.: draw((0,0)..controls(0.33,0.33)and(0.66,0.66)..(1,1),rgb(1,0,1)+1.5); - """ - if line[-2:] != ");": - raise xasyParseError("Invalid syntax") - args = line[5:-2] - loc = args.rfind(",rgb") - path = args[:loc] - pen = args[loc+1:] - global pendingTransforms - return xasyShape(parsePathExpression(path),parsePen(pen),pendingTransforms.pop()) +class xasyParseError(Exception): + """A parsing error""" + pass -def parseFillCommand(line): - """Parse a fill command returning an xasyFilledShape object - - Syntax: - fill(cyclic path,pen); - e.g.: fill((0,0)..controls(0.33,0.33)and(0.66,0.66)..(1,1)..controls(0.66,0)and(0.33,0)..cycle,rgb(1,0,1)+1.5); - """ - if line[-2:] != ");": - raise xasyParseError("Invalid syntax") - args = line[5:-2] - loc = args.rfind(",rgb") - path = args[:loc] - pen = args[loc+1:] - global pendingTransforms - return xasyFilledShape(parsePathExpression(path),parsePen(pen),pendingTransforms.pop()) -def parsePen(pen): - """Parse a pen expression returning an asyPen - - Syntax: - color+width[+options] - e.g.: rgb(0,0,0)+1.5+evenodd - e.g.: rgb(0,1,0)+1.23 - """ - try: - tokens = pen.split("+") - color = eval(tokens[0][3:]) - width = float(tokens[1]) - if len(tokens)>2: - options = "+".join(tokens[2:]) +class xasyFileError(Exception): + """An i/o error or other error not related to parsing""" + pass + + +def extractTransform(line): + """Returns key and the new transform.""" + # see https://regex101.com/r/6DqkRJ/4 for info + mapString = x2a.xasyItem.mapString + testMatch = re.match( + r'^{0:s}\s*\(\s*\"([^\"]+)\"\s*,\s*\(([-\d, .]+)\)\s*\)'.format(mapString), line.strip()) + if testMatch is None: + mapOnlyMatch = re.match(r'^{0:s}\s*\(\s *\"([^\"]+)\"\s*\)'.format(mapString), line.strip()) + if mapOnlyMatch is None: + return None + else: + key = mapOnlyMatch.group(1) + return key, x2a.identity() else: - options = "" - return asyPen(color,width,options) - except: - raise xasyParseError("Invalid pen") - -def parsePathExpression(expr): - """Parse an asy path returning an asyPath()""" - result = asyPath() - expr = "".join(expr.split()) - #print (expr) - if expr.find("controls") != -1: - #parse a path with control points - tokens = expr.split("..") - nodes = [a for a in tokens if not a.startswith("controls")] - for a in range(len(nodes)): - if nodes[a] != "cycle": - nodes[a] = eval(nodes[a]) - controls = [[eval(b) for b in a.replace("controls", "").split("and")] for a in tokens if a.startswith("controls")] - result.initFromControls(nodes, controls) - else: - #parse a path without control points - tokens = re.split(r"(::|--|\.\.)",expr) - linkSet = re.findall("::|--|\.\.",expr) - nodeSet = [a for a in tokens if not re.match(r"::|--|\.\.",a)] - #print (nodeSet) - for a in range(len(nodeSet)): - if nodeSet[a] != "cycle": - nodeSet[a] = eval(nodeSet[a]) - #print (nodeSet) - result.initFromNodeList(nodeSet, linkSet) - return result - -def takeUntilSemicolon(line,lines): - """Read and concatenate lines until the collected lines end with a semicolon""" - data = line - while not data.endswith(";"): - newline = lines.pop(0) - data += newline - return data - -def parseLine(line,lines): - """Parse a line of the file""" - if len(line)==0 or line.isspace() or line.startswith("//"): - return None - elif line.startswith(scriptPrefix): - return extractScript(lines) - elif line.startswith(transformPrefix): - return parseTransformExpression(takeUntilSemicolon(line,lines)) - elif line.startswith("label("): - return parseLabelCommand(takeUntilSemicolon(line,lines)) - elif line.startswith("draw("): - return parseDrawCommand(takeUntilSemicolon(line,lines)) - elif line.startswith("fill("): - return parseFillCommand(takeUntilSemicolon(line,lines)) - elif line.startswith("exitXasyMode();"): - return None - raise Exception("Could not parse the line") - -fileHeader = """initXasyMode(); -// This file was generated by xasy. It may be edited manually, however, a strict -// syntax must be followed. It is advised that manually scripted items be added -// in the form of a script either by using xasy or by mimicking the format of an -// xasy-generated script item. -// Please consult the documentation or the examples provided for details. -""" - -fileFooter = """// This is the end of the file -exitXasyMode(); - -""" - -def saveFile(file,xasyItems): - """Write a list of xasyItems to a file""" - file.write(fileHeader) - for item in xasyItems: - file.write(item.getCode()+"\n\n") - file.write(fileFooter) - -if __name__ == '__main__': - root = Tk() - try: - name = raw_input("enter file name (\"../../xasyTest.asy\"):") - if name == '': - name = "../../xasyTest.asy" - f = open(name,"rt") - except: - print ("Could not open file.") - asy.quit() - sys.exit(1) - - fileItems = [] - try: - fileItems = parseFile(f) - res = [str(a) for a in fileItems] - print ("----------------------------------") - print ("Objects in {:s}".format(f.name)) - print ("----------------------------------") - for a in res: - print (a) - print ("----------------------------------") - print ("successful parse") - f.close() - except: - f.close() - print ("parse failed") - raise - - print ("making a file") - f = open("testfile.asy","wt") - saveFile(f,fileItems) - f.close() - root.configure(width=500,height=500) - root.title("Results") - canv = Canvas(root,width=500,height=500) - canv.pack() - for i in fileItems[1].imageList: - canv.create_image(250+i.bbox[0],250-i.bbox[3],anchor = NW, image=i.image) - Button(root,image=i.image).pack(side=LEFT) - root.mainloop() + key = testMatch.group(1) + rawStr = testMatch.group(2) + rawStrArray = rawStr.split(',') + + if len(rawStrArray) != 6: + return None + transf = [float(val.strip()) for val in rawStrArray] + return key, x2a.asyTransform(transf) + + +def extractTransformsFromFile(fileStr): + transfDict = {} + maxItemCount = 0 + with io.StringIO() as rawCode: + for line in fileStr.splitlines(): + test_transf = extractTransform(line.rstrip()) + if test_transf is None: + rawCode.write(line + '\n') + else: + key, transf = test_transf + if key not in transfDict.keys(): + transfDict[key] = [] + transfDict[key].append(transf) + + # see https://regex101.com/r/RgeBVc/2 for regex + + testNum = re.match(r'^x(\d+)($|:.*$)', key) + if testNum is not None: + maxItemCount = max(maxItemCount, int(testNum.group(1))) + final_str = rawCode.getvalue() + return final_str, transfDict, maxItemCount + + +def saveFile(file, xasyItems, asy2psmap): + """Write a list of xasyItems to a file""" + for item in xasyItems: + file.write(item.getTransformCode(asy2psmap)) + + for item in xasyItems: + file.write(item.getObjectCode(asy2psmap)) + + file.write('size('+str(asy2psmap*x2a.yflip())+'); '+ x2a.xasyItem.resizeComment+'\n') diff --git a/Master/texmf-dist/asymptote/GUI/xasyGUIIcons.py b/Master/texmf-dist/asymptote/GUI/xasyGUIIcons.py deleted file mode 100755 index 7f07d5748d7..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyGUIIcons.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python -################################################################## -# This file stores the icons used by the xasy GUI -# -# About images and base64 -# -# Suppose you have image.gif and want to create a base64 -# string. This can be accomplished using: -# -# import base64 -# base64.encodestring(open("image.gif","rb").read()) -# -# The resulting output, including the enclosing single quotes, -# is the base64 encoding of the image and can be used in the -# dictionary below. -# -# -# Suppose you have a base64 string, b64str, and want to create -# an image. This can be accomplished using: -# -# import base64 -# open("image.gif","w").write(base64.decodestring(b64str)) -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -################################################################## -import base64 -import os -#toolbar icon image data in base64 eliminates need to worry about files -#these are the base64 encodings of the content of the directory xasy3Imgs -iconB64 = { -'lower': 'R0lGODlhGAAYAPEBAAAAAP///8zMzAAAACH5BAEAAAIALAAAAAAYABgAAAItlI+py+0Po5yUgosz\nrrybK2giqADed6LHKCZm+p7xx2Zuqsqr95KcJpv9cJUCADs=\n', -'rotate': 'R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAI7jI8JkO231mux1mkistL1zX0Q\ng2Fi6aGmurKp+8KKrJB0Zt+nzOQw6XPZgqjczuQ7eohKEDKoUYWIgQIAOw==\n', -'raise': 'R0lGODlhGAAYAPEBAAAAAP///8zMzAAAACH5BAEAAAIALAAAAAAYABgAAAIwlI+pywgND3ixzVvZ\nNDSn3nlKKH7fhaZmObKtk8Yh6dKlLcfC5vZ1jvIJh8SikVUAADs=\n', -'fillPoly': 'R0lGODlhGAAYAPEAAAAAAIOBgwAAAAAAACH5BAEAAAIALAAAAAAYABgAAAJGlI+py+0PEYgNBDCp\nDPxqY3UcRoViRzrmKWbLyqIMHI9vHbsbfuoHjfOBcrlbT0ATIo+gldKpMD1lL8vUo5oqS9vS5wsp\nAAA7\n', -'move': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAI4jI+py+0I3gNUNhqtwlVD7m3h\nkoVdUJ4MaKTYysVymbDoYcM4Tmv9eAO2cp6YEKUavY5BpvMZKgAAOw==\n', -'drawBezi': 'R0lGODlhGAAYAPEBAAAAAP///6usrQAAACH5BAEAAAIALAAAAAAYABgAAAI6lI+py+0AnYRUKhox\nsFvUFDXdM4LWUaKnEaorhqSX1noPmMquWJukzpr0YitRcfE5oobFpPIJjUoZBQA7\n', -'vertiMove': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAIsjI+py+0I3gNUNhqtwlVD7m3h\nko2QmZRooKKt+Y5xOFtc7dwrtrLd3gsKTQUAOw==\n', -'horizMove': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAIljI+py+0Po5y02oshAGu/7Skg\n143mSYpgGTYt8mbyTNf2jedWAQA7\n', -'fillEllip': 'R0lGODlhGAAYAPECAAAAAIOBg////6usrSH5BAEAAAMALAAAAAAYABgAAAJAnI+py+0PowS0gkmD\n3qE6wIXctYDi2SkmepLGyrYHHIcuXW93Lr+86BrgakHfrzjjIRGVFgVjWUqm1Kr1ijUUAAA7\n', -'text': 'R0lGODlhGAAYAIABAAAAAP///yH5BAEAAAEALAAAAAAYABgAAAI+jI+py+0Po5x0AgSu1SZvHnhS\nBnpio5Ukt2Idm3bysYrnddLwy+czH0rhFDkbTigj6UzKl68CjUqn1Ko1UAAAOw==\n', -'drawPoly': 'R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAI4jI+py+0PEYhtgkmlzgFL/4DJ\nFULiVi4ns66smrUxrMj1fdqHR+60kfPdgCwLzbWTIU1LE+cJKQAAOw==\n', -'drawLines': 'R0lGODlhGAAYAPEBAAAAAP///6usrQAAACH5BAEAAAIALAAAAAAYABgAAAI3lI+py+0AnYRAPmoZ\njvlwX3Vh8j2XUIIWNXoZS3ZoO8soSK+4fRuYnQPyFEHhcHecFV+ppDNRAAA7\n', -'drawShape': 'R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAI5jI+pywffIjQzIrCwdXnTplmh\nMoKmKIHVeZXp5cFcPH+0HbjbqKN17OoxgrTeKiOkPHjH3fIGjS4KADs=\n', -'drawEllip': 'R0lGODlhGAAYAPEBAAAAAP///6usrQAAACH5BAEAAAIALAAAAAAYABgAAAIylI+py+0PowS0gklX\ndRd29XmgdIQh+Z1TSSJpyxpqZMLqzOB4sgsbmKFZgrCi8YhMNgoAOw==\n', -'select': 'R0lGODlhGAAYAPIDAAAAAICAgMDAwP///6usrQAAAAAAAAAAACH5BAEAAAQALAAAAAAYABgAAANH\nSLrc/mvA6YCkGIiLIQhb54Gh2HwkZxKo4KoiSpam7L6rfdNZ4M+C3I+0Ush8wSLKCFIyPsnisyld\nAD7VabR6DWSt37BYmgAAOw==\n', -'fillShape': 'R0lGODlhGAAYAPEAAAAAAIOBgwAAAAAAACH5BAEAAAIALAAAAAAYABgAAAJHlI+pywff4gsUxgSo\nrhflzXXCB4YXWQIiCqpnubnLw8KyU8Omket77wvcgD4ZUTcMIj3KlOLYejY1N8/R0qChaCIrtgsO\nRwoAOw==\n', -'asy': 'R0lGODlhGAAYAIABAP8AAAAAACH5BAEKAAEALAIAAwAUABIAAAImjI+py+0AHINy0ZouNjBurmGd\nt40fFT4j2aydGqaBq8jvxH46UwAAOw==\n' -} - -def createGIF(key): - """Create a gif file from the data in the iconB64 list of icons""" - if key not in iconB64.keys(): - print ("Error: {:s} not found in icon list.".format(key)) - print ("Available icons:",iconB64.keys()) - else: - print ("Generating {:s}.gif".format(key)) - open("{:s}.gif".format(key),"w").write(base64.decodestring(iconB64[key])) - -def createGIFs(): - """Create the files for all the icons in iconB64""" - for name in iconB64.keys(): - createGIF(name) - -def createStrFromGif(gifFile): - """Create the base64 representation of a file""" - return base64.encodestring(gifFile.read()) - -if __name__=='__main__': - print ("Testing the xasyGUIIcons module.") - print ("Generating all the GIFs:") - createGIFs() - print ("Checking consistency of all icons in iconB64") - allpassed = True - for icon in iconB64.keys(): - print ("Checking {:s}".format(icon)) - if createStrFromGif(open("{:s}.gif".format(icon),"rb")) == iconB64[icon]: - print ("\tPassed.") - else: - print ("\tFailed.") - allpassed= False - if allpassed: - print ("All files succeeded.") - s = raw_input("Delete generated files? (y/n)") - if s == "y": - for name in iconB64.keys(): - print ("Deleting {:s}.gif".format(name)) - os.unlink(name+".gif") - print ("\tdone") - print ("Done") diff --git a/Master/texmf-dist/asymptote/GUI/xasyMainWin.py b/Master/texmf-dist/asymptote/GUI/xasyMainWin.py deleted file mode 100755 index b0549e72f9c..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyMainWin.py +++ /dev/null @@ -1,1748 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# xasyMainWin implements the functionality of the GUI. It depends on -# xasy2asy for its interaction with Asymptote. -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -########################################################################### - -import os -import sys -from string import * -import subprocess -import math -import copy - -if sys.version_info >= (3, 0): - # python3 - from tkinter import * - from tkinter import filedialog, messagebox, simpledialog -else: - # python2 - # from Tkinter import * - import tkFileDialog as filedialog - import tkMessageBox as messagebox - import tkSimpleDialog as simpledialog - -import threading -import time - -from xasyVersion import xasyVersion -import xasyCodeEditor -from xasy2asy import * -import xasyFile -import xasyOptions -import xasyOptionsDialog -import CubicBezier -from xasyBezierEditor import xasyBezierEditor -from xasyGUIIcons import iconB64 -from xasyColorPicker import * - -from UndoRedoStack import * -from xasyActions import * - -import string - -try: - from PIL import ImageTk - from PIL import Image - PILAvailable = True -except: - PILAvailable = False - -class xasyMainWin: - def __init__(self,master,file=None,magnification=1.0): - self.opLock = threading.Lock() - self.parent = master - self.magnification = magnification - self.previousZoom = self.magnification - self.magList = [0.1,0.25,1.0/3,0.5,1,2,3,4,5,10] - self.bindGlobalEvents() - self.createWidgets() - self.resetGUI() - site="" - if not PILAvailable: - messagebox.showerror("Failed Dependencies","An error occurred loading the required PIL library. Please install Pillow from http://pypi.python.org/pypi/Pillow") - self.parent.destroy() - sys.exit(1) - if file != None: - self.loadFile(file) - self.parent.after(100,self.tickHandler) - - def testOrAcquireLock(self): - val = self.opLock.acquire(False) - if val: - self.closeDisplayLock() - return val - - def acquireLock(self): - self.closeDisplayLock() - self.opLock.acquire() - - def releaseLock(self): - self.opLock.release() - self.openDisplayLock() - - def tickHandler(self): - self.tickCount += 1 - self.mainCanvas.itemconfigure("outlineBox",dashoffset=self.tickCount%9) - self.parent.after(100,self.tickHandler) - - def closeDisplayLock(self): - self.status.config(text="Busy") - self.parent.update_idletasks() - - def openDisplayLock(self): - self.status.config(text="Ready") - - def bindGlobalEvents(self): - #global bindings - self.parent.bind_all("<Control-z>",lambda q:self.editUndoCmd())# z -> no shift - self.parent.bind_all("<Control-Z>",lambda q:self.editRedoCmd())# Z -> with shift - self.parent.bind_all("<Control-o>",lambda q:self.fileOpenCmd()) - self.parent.bind_all("<Control-n>",lambda q:self.fileNewCmd()) - self.parent.bind_all("<Control-s>",lambda q:self.fileSaveCmd()) - self.parent.bind_all("<Control-q>",lambda q:self.fileExitCmd()) - self.parent.bind_all("<F1>",lambda q:self.helpHelpCmd()) - - def unbindGlobalEvents(self): - #global bindings - self.parent.unbind("<Control-z>") - self.parent.unbind("<Control-Z>") - self.parent.unbind("<Control-o>") - self.parent.unbind("<Control-n>") - self.parent.unbind("<Control-s>") - self.parent.unbind("<Control-q>") - self.parent.unbind("<F1>") - - def createWidgets(self): - #first some configuration - self.parent.geometry("800x600") - self.parent.title("Xasy") - self.parent.resizable(True,True) - - #try to capture the closing of the window - #find a better way to do this since the widgets may - #already be destroyed when this is called - self.parent.protocol("WM_DELETE_WINDOW",self.canQuit) - - #the main menu - self.mainMenu = Menu(self.parent) - self.parent.config(menu=self.mainMenu) - - #the file menu - self.fileMenu = Menu(self.mainMenu,tearoff=0) - self.fileMenu.add_command(label="New",command=self.fileNewCmd,accelerator="Ctrl+N",underline=0) - self.fileMenu.add_command(label="Open",command=self.fileOpenCmd,accelerator="Ctrl+O",underline=0) - self.fileMenu.add_separator() - self.fileMenu.add_command(label="Save",command=self.fileSaveCmd,accelerator="Ctrl+S",underline=0) - self.fileMenu.add_command(label="Save As",command=self.fileSaveAsCmd,underline=5) - self.fileMenu.add_separator() - - #an export menu - self.exportMenu = Menu(self.fileMenu,tearoff=0) - self.exportMenu.add_command(label="EPS...",command=self.exportEPS,underline=0) - self.exportMenu.add_command(label="PDF...",command=self.exportPDF,underline=0) - self.exportMenu.add_command(label="GIF...",command=self.exportGIF,underline=0) - self.exportMenu.add_command(label="PNG...",command=self.exportPNG,underline=1) - self.exportMenu.add_command(label="SVG...",command=self.exportSVG,underline=0) - self.fileMenu.add_cascade(label="Export",menu=self.exportMenu,underline=1) - self.fileMenu.add_separator() - - self.fileMenu.add_command(label="Quit",command=self.fileExitCmd,accelerator="Ctrl+Q",underline=0) - - self.mainMenu.add_cascade(label="File",menu=self.fileMenu,underline=0) - - #the edit menu - self.editMenu = Menu(self.mainMenu,tearoff=0) - self.editMenu.add_command(label="Undo",command=self.editUndoCmd,accelerator="Ctrl+Z",underline=0) - self.editMenu.add_command(label="Redo",command=self.editRedoCmd,accelerator="Shift+Ctrl+Z",underline=0) - self.mainMenu.add_cascade(label="Edit",menu=self.editMenu,underline=0) - - #the tools menu - self.toolsMenu = Menu(self.mainMenu,tearoff=0) - self.mainMenu.add_cascade(label="Tools",menu=self.toolsMenu,underline=0) - - #the options menu - self.optionsMenu = Menu(self.toolsMenu,tearoff=0) - self.toolsMenu.add_cascade(label="Options",menu=self.optionsMenu,underline=0) - self.optionsMenu.add_command(label="Edit...",command=self.editOptions,underline=0) - self.optionsMenu.add_command(label="Reset defaults",command=self.resetOptions,underline=6) - - #the help menu - self.helpMenu = Menu(self.mainMenu,tearoff=0) - self.helpMenu.add_command(label="Help",command=self.helpHelpCmd,state=DISABLED,accelerator="F1",underline=0) - self.helpMenu.add_command(label="Asymptote Documentation",command=self.helpAsyDocCmd,underline=10) - self.helpMenu.add_separator() - self.helpMenu.add_command(label="About xasy",command=self.helpAboutCmd,underline=0) - self.mainMenu.add_cascade(label="Help",menu=self.helpMenu,underline=0) - - #status bar - self.statusBar = Frame(self.parent,relief=FLAT) - - self.magVal = DoubleVar() - self.magVal.set(round(100*self.magnification,1)) - self.magVal.trace('w',self.zoomViewCmd) - zoomList = self.magList - if self.magnification not in zoomList: - zoomList.append(self.magnification) - zoomList.sort() - zoomList = [round(100*i,1) for i in zoomList] - self.zoomMenu = OptionMenu(self.statusBar,self.magVal,*zoomList) - self.zoomMenu.pack(side=RIGHT) - Label(self.statusBar,text="Zoom:",anchor=E,width=7).pack(side=RIGHT) - - self.coords = Label(self.statusBar,text="(0,0)",relief=SUNKEN,anchor=W) - self.coords.pack(side=RIGHT,anchor=S) - self.status = Label(self.statusBar,text="Ready",relief=SUNKEN,anchor=W) - self.status.pack(side=RIGHT,fill=X,expand=1,anchor=SW) - self.statusBar.pack(side=BOTTOM,fill=X) - - #toolbar for transformation, drawing, and adjustment commands - self.toolBar = Frame(self.parent,relief=FLAT,borderwidth=3) - - #let's load some images - self.toolIcons = {} - for x in iconB64.keys(): - self.toolIcons[x] = PhotoImage(data=iconB64[x]) - - self.transformLbl = Label(self.toolBar,text="",anchor=W) - self.transformLbl.grid(row=0,column=0,columnspan=2,sticky=W) - self.toolSelectButton = Button(self.toolBar,command=self.toolSelectCmd,image=self.toolIcons["select"]) - self.toolSelectButton.grid(row=1,column=0,sticky=N+S+E+W) - self.toolMoveButton = Button(self.toolBar,command=self.toolMoveCmd,image=self.toolIcons["move"]) - self.toolMoveButton.grid(row=2,column=0,sticky=N+S+E+W) - self.toolRotateButton = Button(self.toolBar,command=self.toolRotateCmd,image=self.toolIcons["rotate"]) - self.toolRotateButton.grid(row=2,column=1,sticky=N+S+E+W) - self.toolVertiMoveButton = Button(self.toolBar,command=self.toolVertiMoveCmd,image=self.toolIcons["vertiMove"]) - self.toolVertiMoveButton.grid(row=3,column=0,sticky=N+S+E+W) - self.toolHorizMoveButton = Button(self.toolBar,command=self.toolHorizMoveCmd,image=self.toolIcons["horizMove"]) - self.toolHorizMoveButton.grid(row=3,column=1,sticky=N+S+E+W) - - self.drawLbl = Label(self.toolBar,text="",anchor=W) - self.drawLbl.grid(row=4,column=0,columnspan=2,sticky=W) - self.toolDrawLinesButton = Button(self.toolBar,command=self.toolDrawLinesCmd,image=self.toolIcons["drawLines"]) - self.toolDrawLinesButton.grid(row=5,column=0,sticky=N+S+E+W) - self.toolDrawBeziButton = Button(self.toolBar,command=self.toolDrawBeziCmd,image=self.toolIcons["drawBezi"]) - self.toolDrawBeziButton.grid(row=5,column=1,sticky=N+S+E+W) - self.toolDrawPolyButton = Button(self.toolBar,command=self.toolDrawPolyCmd,image=self.toolIcons["drawPoly"]) - self.toolDrawPolyButton.grid(row=6,column=0,sticky=N+S+E+W) - self.toolFillPolyButton = Button(self.toolBar,command=self.toolFillPolyCmd,image=self.toolIcons["fillPoly"]) - self.toolFillPolyButton.grid(row=6,column=1,sticky=N+S+E+W) - self.toolDrawEllipButton = Button(self.toolBar,command=self.toolDrawEllipCmd,image=self.toolIcons["drawEllip"],state=DISABLED,relief=FLAT) - #self.toolDrawEllipButton.grid(row=7,column=0,sticky=N+S+E+W) - self.toolFillEllipButton = Button(self.toolBar,command=self.toolFillEllipCmd,image=self.toolIcons["fillEllip"],state=DISABLED,relief=FLAT) - #self.toolFillEllipButton.grid(row=7,column=1,sticky=N+S+E+W) - self.toolDrawShapeButton = Button(self.toolBar,command=self.toolDrawShapeCmd,image=self.toolIcons["drawShape"]) - self.toolDrawShapeButton.grid(row=8,column=0,sticky=N+S+E+W) - self.toolFillShapeButton = Button(self.toolBar,command=self.toolFillShapeCmd,image=self.toolIcons["fillShape"]) - self.toolFillShapeButton.grid(row=8,column=1,sticky=N+S+E+W) - self.toolTextButton = Button(self.toolBar,command=self.toolTextCmd,image=self.toolIcons["text"]) - self.toolTextButton.grid(row=9,column=0,sticky=N+S+E+W) - self.toolAsyButton = Button(self.toolBar,command=self.toolAsyCmd,image=self.toolIcons["asy"]) - self.toolAsyButton.grid(row=9,column=1,sticky=N+S+E+W) - - self.adjLbl = Label(self.toolBar,text="",anchor=W) - self.adjLbl.grid(row=10,column=0,columnspan=2,sticky=W) - self.toolRaiseButton = Button(self.toolBar,command=self.toolRaiseCmd,image=self.toolIcons["raise"]) - self.toolRaiseButton.grid(row=11,column=0,sticky=N+S+E+W) - self.toolLowerButton = Button(self.toolBar,command=self.toolLowerCmd,image=self.toolIcons["lower"]) - self.toolLowerButton.grid(row=11,column=1,sticky=N+S+E+W) - - self.toolBar.pack(side=LEFT,anchor=NW) - - #documentation for the tool bar buttons - self.toolDocs = { - self.toolSelectButton : "Click an item to select it. Control-Click will select/deselect additional items. Use mouse scroller (or Up/Down keys) to raise/lower highlighted items.", - self.toolMoveButton : "Drag a selected item.", - self.toolHorizMoveButton : "Drag a selected item. Only horizontal translation will be applied.", - self.toolVertiMoveButton : "Drag a selected item. Only vertical translation will be applied.", - self.toolRotateButton : "Drag a selected item to rotate it.", - self.toolDrawLinesButton : "Click to draw line segments. Double click to place last point.", - self.toolDrawBeziButton : "Click to place points. Double click to place last point.", - self.toolDrawPolyButton : "Click to place vertices. Double click to place last point.", - self.toolFillPolyButton : "Click to place vertices. Double click to place last point.", - self.toolDrawEllipButton : "(UNIMPLEMENTED)Click to place center. Move mouse to achieve correct shape and double click.", - self.toolFillEllipButton : "(UNIMPLEMENTED)Click to place center. Move mouse to achieve correct shape and double click.", - self.toolDrawShapeButton : "Click to place points. Double click to place last point.", - self.toolFillShapeButton : "Click to place points. Double click to place last point.", - self.toolTextButton : "Click location of top left label position and enter text in dialog.", - self.toolRaiseButton : "Raise selected items to top.", - self.toolLowerButton : "Lower selected items to bottom.", - self.toolAsyButton : "Insert/Edit Asymptote code." - } - - #Current pen settings - self.optionsBar = Frame(self.parent,height=100,relief=FLAT,borderwidth=3) - self.penDisp = Canvas(self.optionsBar,width=100,height=25,bg="white",relief=SUNKEN,borderwidth=3) - self.penDisp.grid(row=0,column=0,padx=3,pady=3) - self.penDisp.create_line(10,25,30,10,60,20,80,10,smooth=True,tags="penDisp") - self.penDisp.create_text(100,30,text="x1",tags="penMag",anchor=SE,font=("times","8")) - self.penColButton = Button(self.optionsBar,text="Color...",width=5,command=self.setPenColCmd,relief=FLAT) - self.penColButton.grid(row=0,column=1,padx=3,pady=3) - Label(self.optionsBar,text="Width",anchor=E).grid(row=0,column=2) - self.penWidthEntry = Entry(self.optionsBar,width=5) - self.penWidthEntry.bind("<KeyRelease>",self.penWidthChanged) - self.penWidthEntry.bind("<FocusOut>",self.applyPenWidthEvt) - self.penWidthEntry.bind("<Return>",self.applyPenWidthEvt) - self.penWidthEntry.grid(row=0,column=3) - Label(self.optionsBar,text="Options",anchor=E).grid(row=0,column=4) - self.penOptEntry = Entry(self.optionsBar) - self.penOptEntry.bind("<FocusOut>",self.applyPenOptEvt) - self.penOptEntry.bind("<Return>",self.applyPenOptEvt) - self.penOptEntry.grid(row=0,column=5) - self.optionsBar.pack(side=BOTTOM,anchor=NW) - - #a paned window for the canvas and propert explorer - self.windowPane = PanedWindow(self.parent) - - #a property explorer - self.propFrame = Frame(self.parent) - self.propFrame.rowconfigure(1,weight=1) - self.propFrame.columnconfigure(0,weight=1) - Label(self.propFrame,text="Item List").grid(row=0,column=0,columnspan=2) - self.itemScroll = Scrollbar(self.propFrame,orient=VERTICAL) - self.propList = Listbox(self.propFrame, yscrollcommand=self.itemScroll.set) - self.itemScroll.config(command=self.propList.yview) - self.itemScroll.grid(row=1,column=1,sticky=N+S) - self.propList.grid(row=1,column=0,sticky=N+S+E+W) - self.propList.bind("<Double-Button-1>",self.propSelect) - self.propList.bind("<Button-3>",self.itemPropMenuPopup) - - #the canvas's frame - self.canvFrame = Frame(self.parent,relief=FLAT,borderwidth=0) - self.canvFrame.rowconfigure(0,weight=1) - self.canvFrame.columnconfigure(0,weight=1) - self.canvVScroll = Scrollbar(self.canvFrame,orient=VERTICAL) - self.canvHScroll = Scrollbar(self.canvFrame,orient=HORIZONTAL) - self.canvHScroll.grid(row=1,column=0,sticky=E+W) - self.canvVScroll.grid(row=0,column=1,sticky=N+S) - - #add the frames to the window pane - self.windowPane.pack(side=RIGHT,fill=BOTH,expand=True) - self.windowPane.add(self.canvFrame) - self.windowPane.add(self.propFrame) - self.windowPane.paneconfigure(self.propFrame,minsize=50,sticky=N+S+E+W) - self.windowPane.bind("<Double-Button-1>",self.togglePaneEvt) - - #the highly important canvas! - self.mainCanvas = Canvas(self.canvFrame,relief=SUNKEN,background="white",borderwidth=3, - highlightthickness=0,closeenough=1.0,yscrollcommand=self.canvVScroll.set, - xscrollcommand=self.canvHScroll.set) - self.mainCanvas.grid(row=0,column=0,sticky=N+S+E+W) - - self.canvVScroll.config(command=self.mainCanvas.yview) - self.canvHScroll.config(command=self.mainCanvas.xview) - - self.mainCanvas.bind("<Motion>",self.canvMotion) - self.mainCanvas.bind("<Button-1>",self.canvLeftDown) - self.mainCanvas.bind("<Double-Button-1>",self.endDraw) - self.mainCanvas.bind("<ButtonRelease-1>",self.canvLeftUp) - self.mainCanvas.bind("<B1-Motion>",self.canvDrag) - - self.mainCanvas.bind("<Enter>",self.canvEnter) - self.mainCanvas.bind("<Leave>",self.canvLeave) - self.mainCanvas.bind("<Delete>",self.itemDelete) - #self.mainCanvas.bind("<Button-3>",self.canvRightDown) - #self.mainCanvas.bind("<ButtonRelease-3>",self.canvRightUp) - self.mainCanvas.bind("<Button-4>",self.itemRaise) - self.mainCanvas.bind("<Button-5>",self.itemLower) - self.mainCanvas.bind("<Up>",self.itemRaise) - self.mainCanvas.bind("<Down>",self.itemLower) - self.mainCanvas.bind("<Configure>",self.configEvt) - - def foregroundPenColor(self,hex): - hex = hex[1:] - rgb = max(hex[0:2], hex[2:4], hex[4:6]) - if(rgb >= "80"): - return "black" - else: - return "white" - - def resetGUI(self): - #set up the main window - self.filename = None - self.fileToOpen = None - self.retitle() - - #set up the paned window - self.paneVisible = True - - #setup the pen entries - self.pendingPenWidthChange = None - self.pendingPenOptChange = None - - #load one-time configs - xasyOptions.load() - self.tkPenColor = xasyOptions.options['defPenColor'] - self.penColor = makeRGBfromTkColor(self.tkPenColor) - self.penColButton.config(activebackground=self.tkPenColor, - activeforeground=self.foregroundPenColor(self.tkPenColor)) - self.penWidth = xasyOptions.options['defPenWidth'] - self.penWidthEntry.select_range(0,END) - self.penWidthEntry.delete(0,END) - self.penWidthEntry.insert(END,str(self.penWidth)) - self.penOptions = xasyOptions.options['defPenOptions'] - self.penOptEntry.select_range(0,END) - self.penOptEntry.delete(0,END) - self.penOptEntry.insert(END,str(self.penOptions)) - self.showCurrentPen() - - #load modifiable configs - self.applyOptions() - - #set up editing - self.editor = None - - #set up drawing - self.pathInProgress = asyPath() - self.currentIDTag = -1 - self.inDrawingMode = False - self.freeMouseDown = True - self.dragSelecting = False - self.itemsBeingRotated = [] - self.inRotatingMode = False - - #set up the toolbar - try: - self.updateSelectedButton(self.toolSelectButton) - except: - self.selectedButton = self.toolSelectButton - self.updateSelectedButton(self.toolSelectButton) - - #set up the canvas - self.mainCanvas.delete(ALL) - self.mainCanvas.create_rectangle(0,0,0,0,tags="outlineBox",width=0,outline="#801111",dash=(3,6)) - self.backColor = "white" #in future, load this from an options file. Or, should this really be an option? - self.mainCanvas.configure(background=self.backColor) - - #set up the xasy item list - self.fileItems = [] - self.propList.delete(0,END) - self.updateCanvasSize() - - #setup timer - self.tickCount = 0 - - #setup undo/redo! - self.undoRedoStack = actionStack() - self.amDragging = False - - def retitle(self): - if self.filename == None: - self.parent.title("Xasy - New File") - else: - name = os.path.abspath(self.filename) - name = os.path.basename(name) - self.parent.title("Xasy - %s"%name) - - def applyOptions(self): - self.gridcolor = xasyOptions.options['gridColor'] - self.tickcolor = xasyOptions.options['tickColor'] - self.axiscolor = xasyOptions.options['axesColor'] - self.gridVisible = xasyOptions.options['showGrid'] - self.gridxspace = xasyOptions.options['gridX'] - self.gridyspace = xasyOptions.options['gridY'] - self.axesVisible = xasyOptions.options['showAxes'] - self.axisxspace = xasyOptions.options['axisX'] - self.axisyspace = xasyOptions.options['axisY'] - self.updateCanvasSize() - #test the asyProcess - startQuickAsy() - if not quickAsyRunning(): - if messagebox.askyesno("Xasy Error","Asymptote could not be executed.\r\nTry to find Asymptote automatically?"): - xasyOptions.setAsyPathFromWindowsRegistry() - xasyOptions.save() - startQuickAsy() - while not quickAsyRunning(): - if messagebox.askyesno("Xasy Error","Asymptote could not be executed.\r\nEdit settings?"): - xasyOptionsDialog.xasyOptionsDlg(self.parent) - xasyOptions.save() - startQuickAsy() - else: - self.parent.destroy() - sys.exit(1) - - def drawGrid(self): - self.mainCanvas.delete("grid") - if not self.gridVisible: - return - left,top,right,bottom = [int(float(a)) for a in self.mainCanvas.cget("scrollregion").split()] - gridyspace = int(self.magnification*self.gridyspace) - gridxspace = int(self.magnification*self.gridxspace) - if gridxspace >= 3 and gridyspace >= 3: - for i in range(0,right,gridxspace): - self.mainCanvas.create_line(i,top,i,bottom,tags=("grid","vertical"),fill=self.gridcolor) - for i in range(-gridxspace,left,-gridxspace): - self.mainCanvas.create_line(i,top,i,bottom,tags=("grid","vertical"),fill=self.gridcolor) - for i in range(-gridyspace,top,-gridyspace): - self.mainCanvas.create_line(left,i,right,i,tags=("grid","horizontal"),fill=self.gridcolor) - for i in range(0,bottom,gridyspace): - self.mainCanvas.create_line(left,i,right,i,tags=("grid","horizontal"),fill=self.gridcolor) - self.mainCanvas.tag_lower("grid") - - def drawAxes(self): - self.mainCanvas.delete("axes") - if not self.axesVisible: - return - left,top,right,bottom = [int(float(a)) for a in self.mainCanvas.cget("scrollregion").split()] - self.mainCanvas.create_line(0,top,0,bottom,tags=("axes","yaxis"),fill=self.axiscolor) - self.mainCanvas.create_line(left,0,right,0,tags=("axes","xaxis"),fill=self.axiscolor) - axisxspace = int(self.magnification*self.axisxspace) - axisyspace = int(self.magnification*self.axisyspace) - if axisxspace >= 3 and axisyspace >= 3: - for i in range(axisxspace,right,axisxspace): - self.mainCanvas.create_line(i,-5,i,5,tags=("axes","xaxis-ticks"),fill=self.tickcolor) - for i in range(-axisxspace,left,-axisxspace): - self.mainCanvas.create_line(i,-5,i,5,tags=("axes","xaxis-ticks"),fill=self.tickcolor) - for i in range(-axisyspace,top,-axisyspace): - self.mainCanvas.create_line(-5,i,5,i,tags=("axes","yaxis-ticks"),fill=self.tickcolor) - for i in range(axisyspace,bottom,axisyspace): - self.mainCanvas.create_line(-5,i,5,i,tags=("axes","yaxis-ticks"),fill=self.tickcolor) - self.mainCanvas.tag_lower("axes") - - def updateCanvasSize(self,left=-200,top=-200,right=200,bottom=200): - self.parent.update_idletasks() - bbox = self.mainCanvas.bbox("drawn || image || node || precontrol || postcontrol") - if bbox == None: - bbox = (0,0,0,0) - #(topleft, bottomright) - left = min(bbox[0],left) - top = min(bbox[1],top) - right = max(bbox[2],right) - bottom = max(bbox[3],bottom) - w,h = self.mainCanvas.winfo_width(),self.mainCanvas.winfo_height() - if right-left < w: - extraw = w-(right-left) - right += extraw//2 - left -= extraw//2 - if bottom-top < h: - extrah = h-(bottom-top) - bottom += extrah//2 - top -= extrah//2 - self.mainCanvas.config(scrollregion=(left,top,right,bottom)) - #self.mainCanvas.xview(MOVETO,float(split(self.mainCanvas["scrollregion"])[0])) - #self.mainCanvas.yview(MOVETO,float(split(self.mainCanvas["scrollregion"])[1])) - #self.mainCanvas.xview(MOVETO,(left+right)/2) - #self.mainCanvas.yview(MOVETO,(top+bottom)/2) - self.drawAxes() - self.drawGrid() - - def bindEvents(self,tagorID): - if tagorID == None: - return - self.mainCanvas.tag_bind(tagorID,"<Control-Button-1>",self.itemToggleSelect) - self.mainCanvas.tag_bind(tagorID,"<Button-1>",self.itemSelect) - self.mainCanvas.tag_bind(tagorID,"<ButtonRelease-1>",self.itemMouseUp) - self.mainCanvas.tag_bind(tagorID,"<Double-Button-1>",self.itemEditEvt) - self.mainCanvas.tag_bind(tagorID,"<B1-Motion>",self.itemDrag) - self.mainCanvas.tag_bind(tagorID,"<Delete>",self.itemDelete) - self.mainCanvas.tag_bind(tagorID,"<Enter>",self.itemHighlight) - self.mainCanvas.tag_bind(tagorID,"<Button-3>",self.itemCanvasMenuPopup) - - def bindItemEvents(self,item): - if item == None: - return - if isinstance(item,xasyScript) or isinstance(item,xasyText): - for image in item.imageList: - self.bindEvents(image.IDTag) - else: - self.bindEvents(item.IDTag) - - def canQuit(self,force=False): - #print ("Quitting") - if not force and not self.testOrAcquireLock(): - return - try: - self.releaseLock() - except: - pass - if self.undoRedoStack.changesMade(): - result = messagebox._show("xasy","File has been modified.\nSave changes?",icon=messagebox.QUESTION,type=messagebox.YESNOCANCEL) - if str(result) == messagebox.CANCEL: - return - elif result == messagebox.YES: - self.fileSaveCmd() - try: - os.rmdir(getAsyTempDir()) - except: - pass - - stopQuickAsy() - self.parent.destroy() - - def openFile(self,name): - if(not self.testOrAcquireLock()): - return - self.releaseLock() #release the lock for loadFile - self.resetGUI() - self.loadFile(name) - - def loadFile(self,name): - self.status.config(text="Loading "+name) - self.filename = os.path.abspath(name) - startQuickAsy() - self.retitle() - try: - try: - f = open(self.filename,'rt') - except: - if self.filename[-4:] == ".asy": - raise - else: - f = open(self.filename+".asy",'rt') - self.filename += ".asy" - self.retitle() - self.fileItems = xasyFile.parseFile(f) - f.close() - except IOError: - messagebox.showerror("File Opening Failed.","File could not be opened.") - self.fileItems = [] - except: - self.fileItems = [] - self.autoMakeScript = True - if self.autoMakeScript or messagebox.askyesno("Error Opening File", "File was not recognized as an xasy file.\nLoad as a script item?"): - try: - item = xasyScript(self.mainCanvas) - f.seek(0) - item.setScript(f.read()) - self.addItemToFile(item) - except: - messagebox.showerror("File Opening Failed.","Could not load as a script item.") - self.fileItems = [] - self.populateCanvasWithItems() - self.populatePropertyList() - self.updateCanvasSize() - - def populateCanvasWithItems(self): - if(not self.testOrAcquireLock()): - return - self.mainCanvas.delete("drawn || image") - self.itemCount = 0 - for item in self.fileItems: - item.drawOnCanvas(self.mainCanvas,self.magnification,forceAddition=True) - self.bindItemEvents(item) - self.releaseLock() - - def propListCountItem(self,item): - plist = self.propList.get(0,END) - count = 1 - for text in plist: - if text.startswith(item): - count += 1 - return count - - def describeItem(self,item): - if isinstance(item,xasyScript): - return "Code Module "+str(self.propListCountItem("Code Module")) - elif isinstance(item,xasyText): - return "Text Label "+str(self.propListCountItem("Text Label")) - elif isinstance(item,xasyFilledShape): - return "Filled Shape "+str(self.propListCountItem("Filled Shape")) - elif isinstance(item,xasyShape): - return "Outline "+str(self.propListCountItem("Outline")) - else: - return "If this happened, the program is corrupt!" - - def populatePropertyList(self): - self.propList.delete(0,END) - for item in self.fileItems: - self.propList.insert(0,self.describeItem(item)) - - def saveFile(self,name): - if(not self.testOrAcquireLock()): - return - f = open(name,"wt") - xasyFile.saveFile(f,self.fileItems) - f.close() - self.undoRedoStack.setCommitLevel() - self.retitle() - self.releaseLock() - - #menu commands - def fileNewCmd(self): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - #print ("Create New File") - if self.undoRedoStack.changesMade(): - result = messagebox._show("xasy","File has been modified.\nSave changes?",icon=messagebox.QUESTION,type=messagebox.YESNOCANCEL) - if str(result) == messagebox.CANCEL: - return - elif result == messagebox.YES: - self.fileSaveCmd() - self.resetGUI() - - def fileOpenCmd(self): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - #print ("Open a file") - if self.undoRedoStack.changesMade(): - result = messagebox._show("xasy","File has been modified.\nSave changes?",icon=messagebox.QUESTION,type=messagebox.YESNOCANCEL) - if str(result) == messagebox.CANCEL: - return - elif result == messagebox.YES: - self.fileSaveCmd() - filename=filedialog.askopenfilename(filetypes=[("asy files","*.asy"),("All files","*")],title="Open File",parent=self.parent) - if type(filename) != type((0,)) and filename != None and filename != '': - self.filename = filename - self.openFile(self.filename) - - def fileSaveCmd(self): - #print ("Save current file") - if(not self.testOrAcquireLock()): - return - self.releaseLock() - if self.filename == None: - filename=filedialog.asksaveasfilename(defaultextension=".asy",filetypes=[("asy files","*.asy")],initialfile="newDrawing.asy",parent=self.parent,title="Save File") - if type(filename) != type((0,)) and filename != None and filename != '': - self.filename = filename - if self.filename != None: - self.saveFile(self.filename) - - def fileSaveAsCmd(self): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - #print ("Save current file as") - filename=filedialog.asksaveasfilename(defaultextension=".asy",filetypes=[("asy files","*.asy")],initialfile="newDrawing.asy",parent=self.parent,title="Save File") - if type(filename) != type((0,)) and filename != None and filename != '': - self.filename = filename - self.saveFile(self.filename) - - #export the file - def exportEPS(self): - self.exportFile(self.filename,"eps") - - def exportPDF(self): - self.exportFile(self.filename,"pdf") - - def exportGIF(self): - self.exportFile(self.filename,"gif") - - def exportPNG(self): - self.exportFile(self.filename,"png") - - def exportSVG(self): - self.exportFile(self.filename,"svg") - - def exportFile(self,inFile, outFormat): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - if inFile == None: - if messagebox.askyesno("xasy","File has not been saved.\nSave?"): - self.fileSaveAsCmd() - inFile = self.filename - else: - return - elif self.undoRedoStack.changesMade(): - choice = messagebox._show("xasy","File has been modified.\nOnly saved changes can be exported.\nDo you want to save changes?",icon=messagebox.QUESTION,type=messagebox.YESNOCANCEL) - choice = str(choice) - if choice != messagebox.YES: - return - else: - self.fileSaveCmd() - name = os.path.splitext(os.path.basename(self.filename))[0]+'.'+outFormat - outfilename = filedialog.asksaveasfilename(defaultextension = '.'+outFormat,filetypes=[(outFormat+" files","*."+outFormat)],initialfile=name,parent=self.parent,title="Choose output file") - if type(outfilename)==type((0,)) or not outfilename or outfilename == '': - return - fullname = os.path.abspath(outfilename) - outName = os.path.basename(outfilename) - command=[xasyOptions.options['asyPath'],"-f"+outFormat,"-o"+fullname,inFile] - saver = subprocess.Popen(command,stdin=PIPE,stdout=PIPE,stderr=PIPE) - saver.wait() - if saver.returncode != 0: - messagebox.showerror("Export Error","Export Error:\n"+saver.stdout.read()+saver.stderr.read()) - self.status.config(text="Error exporting file") - else: - self.status.config(text="File exported successfully") - - def fileExitCmd(self): - #print ("Exit xasy") - self.canQuit() - - def editUndoCmd(self): - if not self.editor == None: - return - if(not self.testOrAcquireLock()): - return - self.undoOperation() - self.releaseLock() - - def editRedoCmd(self): - if not self.editor == None: - return - if(not self.testOrAcquireLock()): - return - self.redoOperation() - self.releaseLock() - - def helpHelpCmd(self): - print ("Get help on xasy") - - def helpAsyDocCmd(self): - #print ("Open documentation about Asymptote") - asyExecute("help;\n") - - def helpAboutCmd(self): - messagebox.showinfo("About xasy","A graphical interface for Asymptote "+xasyVersion) - - def updateSelectedButton(self,newB): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - #disable switching modes during an incomplete drawing operation - if self.inDrawingMode: - return - self.selectedButton.config(relief = RAISED) - if newB == self.toolSelectButton or self.selectedButton == self.toolSelectButton: - self.mainCanvas.delete("highlightBox") - if self.editor != None: - self.editor.endEdit() - if self.editor.modified: - self.undoRedoStack.add(editDrawnItemAction(self,self.itemBeingEdited,copy.deepcopy(self.editor.shape),self.fileItems.index(self.editor.shape))) - if newB not in (self.toolSelectButton,self.toolMoveButton,self.toolHorizMoveButton,self.toolVertiMoveButton,self.toolRotateButton): - self.clearSelection() - self.selectedButton = newB - self.selectedButton.config(relief = SUNKEN) - self.status.config(text=self.toolDocs[newB]) - - #toolbar commands - def toolSelectCmd(self): - self.updateSelectedButton(self.toolSelectButton) - def toolMoveCmd(self): - self.updateSelectedButton(self.toolMoveButton) - def toolRotateCmd(self): - self.updateSelectedButton(self.toolRotateButton) - def toolVertiMoveCmd(self): - self.updateSelectedButton(self.toolVertiMoveButton) - def toolHorizMoveCmd(self): - self.updateSelectedButton(self.toolHorizMoveButton) - def toolDrawLinesCmd(self): - self.updateSelectedButton(self.toolDrawLinesButton) - def toolDrawBeziCmd(self): - self.updateSelectedButton(self.toolDrawBeziButton) - def toolDrawPolyCmd(self): - self.updateSelectedButton(self.toolDrawPolyButton) - def toolFillPolyCmd(self): - self.updateSelectedButton(self.toolFillPolyButton) - def toolDrawEllipCmd(self): - self.updateSelectedButton(self.toolDrawEllipButton) - def toolFillEllipCmd(self): - self.updateSelectedButton(self.toolFillEllipButton) - def toolDrawShapeCmd(self): - self.updateSelectedButton(self.toolDrawShapeButton) - def toolFillShapeCmd(self): - self.updateSelectedButton(self.toolFillShapeButton) - def toolTextCmd(self): - self.updateSelectedButton(self.toolTextButton) - def toolAsyCmd(self): - # ignore the command if we are too busy to process it - if not self.testOrAcquireLock(): - return - self.updateSelectedButton(self.toolSelectButton) - self.clearSelection() - self.clearHighlight() - self.unbindGlobalEvents() - try: - self.getNewText("// enter your code here") - except Exception as e: - messagebox.showerror('xasy Error',e.message) - else: - self.addItemToFile(xasyScript(self.mainCanvas)) - text = self.newText - self.undoRedoStack.add(addScriptAction(self,self.fileItems[-1])) - self.fileItems[-1].setScript(text) - self.fileItems[-1].drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(self.fileItems[-1]) - self.bindGlobalEvents() - self.releaseLock() - def toolRaiseCmd(self): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - if not self.inDrawingMode and self.editor == None: - itemList = [] - indexList = [] - for ID in self.mainCanvas.find_withtag("selectedItem"): - item = self.findItem(ID) - if item not in itemList: - itemList.append(item) - indexList.append(self.fileItems.index(item)) - self.raiseSomething(item) - self.undoRedoStack.add(itemRaiseAction(self,itemList,indexList)) - def toolLowerCmd(self): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - if not self.inDrawingMode and self.editor == None: - itemList = [] - indexList = [] - for ID in self.mainCanvas.find_withtag("selectedItem"): - item = self.findItem(ID) - if item not in itemList: - itemList.append(item) - indexList.append(self.fileItems.index(item)) - self.lowerSomething(item) - self.undoRedoStack.add(itemLowerAction(self,itemList,indexList)) - def itemRaise(self,event): - self.mainCanvas.tag_raise(CURRENT) - def itemLower(self,event): - self.mainCanvas.tag_lower(CURRENT) - - #options bar commands - def setPenColCmd(self): - if not self.testOrAcquireLock(): - return - old = self.penColor - self.penColor = xasyColorDlg(self.parent).getColor(self.penColor) - if self.penColor != old: - self.tkPenColor = RGB255hex(RGBreal255(self.penColor)) - self.penColButton.config(activebackground=self.tkPenColor, - activeforeground=self.foregroundPenColor(self.tkPenColor)) - self.showCurrentPen() - self.releaseLock() - - def clearSelection(self): - self.hideSelectionBox() - self.mainCanvas.dtag("selectedItem","selectedItem") - - def hideSelectionBox(self): - self.mainCanvas.itemconfigure("outlineBox",width=1,outline=self.backColor) - self.mainCanvas.tag_lower("outlineBox") - self.mainCanvas.coords("outlineBox",self.mainCanvas.bbox(ALL)) - - def showSelectionBox(self): - self.mainCanvas.itemconfigure("outlineBox",width=2,outline="#801111") - self.mainCanvas.tag_raise("outlineBox") - - def setSelection(self,what): - self.mainCanvas.addtag_withtag("selectedItem",what) - self.updateSelection() - if self.selectedButton == self.toolSelectButton and len(self.mainCanvas.find_withtag("selectedItem")) > 0: - self.updateSelectedButton(self.toolMoveButton) - - def unSelect(self,what): - self.mainCanvas.dtag(what,"selectedItem") - self.updateSelection() - - def updateSelection(self): - self.clearHighlight() - theBbox = self.mainCanvas.bbox("selectedItem") - if theBbox != None: - theBbox = (theBbox[0]-2,theBbox[1]-2,theBbox[2]+2,theBbox[3]+2) - self.mainCanvas.coords("outlineBox",theBbox) - self.showSelectionBox() - else: - self.clearSelection() - - #event handlers - def updateZoom(self): - self.zoomMenu.config(state=DISABLED) - self.magnification = self.magVal.get()/100.0 - if self.magnification != self.previousZoom: - self.populateCanvasWithItems() - self.updateCanvasSize() - self.updateSelection() - self.drawAxes() - self.drawGrid() - self.previousZoom = self.magnification - self.zoomMenu.config(state=NORMAL) - - def zoomViewCmd(self,*args): - magnification = self.magVal.get()/100.0 - self.updateZoom(); - - def selectItem(self,item): - self.clearSelection() - if isinstance(item,xasyScript) or isinstance(item,xasyText): - for image in item.imageList: - self.setSelection(image.IDTag) - else: - self.setSelection(item.IDTag) - - def propSelect(self,event): - items = [int(a) for a in self.propList.curselection()] - if len(items)>0: - try: - self.selectItem(self.fileItems[len(self.fileItems)-items[0]-1]) - except: - raise - - def findItem(self,ID): - for item in self.fileItems: - if isinstance(item,xasyScript) or isinstance(item,xasyText): - for image in item.imageList: - if image.IDTag == ID: - return item - else: - if item.IDTag == ID: - return item - raise Exception("Illegal operation: Item with matching ID could not be found.") - - def findItemImageIndex(self,item,ID): - count = 0 - for image in item.imageList: - if image.IDTag == ID: - return count - else: - count += 1 - raise Exception("Illegal operation: Image with matching ID could not be found.") - return None - - def raiseSomething(self,item,force=False): - if self.fileItems[-1] != item or force: - index = len(self.fileItems)-self.fileItems.index(item)-1 - text = self.propList.get(index) - self.propList.delete(index) - self.propList.insert(0,text) - for i in range(self.fileItems.index(item),len(self.fileItems)-1): - self.fileItems[i] = self.fileItems[i+1] - self.fileItems[-1] = item - if isinstance(item,xasyScript) or isinstance(item,xasyText): - for im in item.imageList: - if im.IDTag != None: - self.mainCanvas.tag_raise(im.IDTag) - else: - if item.IDTag != None: - self.mainCanvas.tag_raise(item.IDTag) - - def lowerSomething(self,item): - if self.fileItems[0] != item: - index = len(self.fileItems)-self.fileItems.index(item)-1 - text = self.propList.get(index) - self.propList.delete(index) - self.propList.insert(END,text) - indices = range(self.fileItems.index(item)) - indices.reverse() - for i in indices: - self.fileItems[i+1] = self.fileItems[i] - self.fileItems[0] = item - if isinstance(item,xasyScript) or isinstance(item,xasyText): - item.imageList.reverse() - for im in item.imageList: - if im.IDTag != None: - self.mainCanvas.tag_lower(im.IDTag) - item.imageList.reverse() - else: - if item.IDTag != None: - self.mainCanvas.tag_lower(item.IDTag) - self.mainCanvas.tag_lower("axes || grid") - - def translateSomething(self,ID,translation,specificItem=None,specificIndex=None): - transform = asyTransform((translation[0],translation[1],1,0,0,1)) - if ID == -1: - item = specificItem - else: - item = self.findItem(ID) - if isinstance(item,xasyText) or isinstance(item,xasyScript): - if ID == -1: - index = specificIndex - else: - index = self.findItemImageIndex(item,ID) - try: - original = item.transform[index] - except: - original = identity() - item.transform[index] = transform*original - bbox = item.imageList[index].originalImage.bbox - item.imageList[index].originalImage.bbox = bbox[0]+translation[0],bbox[1]+translation[1],bbox[2]+translation[0],bbox[3]+translation[1] - else: - item.transform = [transform*item.transform[0]] - - def makeRotationMatrix(self,theta,origin): - rotMat = (math.cos(theta),-math.sin(theta),math.sin(theta),math.cos(theta)) - shift = asyTransform((0,0,1-rotMat[0],-rotMat[1],-rotMat[2],1-rotMat[3]))*origin - return asyTransform((shift[0],shift[1],rotMat[0],rotMat[1],rotMat[2],rotMat[3])) - - def rotateSomething(self,ID,theta,origin,specificItem=None,specificIndex=None): - #print ("Rotating by {} around {}".format(theta*180.0/math.pi,origin)) - rotMat = self.makeRotationMatrix(theta,(origin[0]/self.magnification,origin[1]/self.magnification)) - #print (rotMat) - if ID == -1: - item = specificItem - else: - item = self.findItem(ID) - if isinstance(item,xasyText) or isinstance(item,xasyScript): - #transform the image - if ID == -1: - index = specificIndex - else: - index = self.findItemImageIndex(item,ID) - try: - original = item.transform[index] - except: - original = identity() - oldBbox = item.imageList[index].originalImage.bbox - oldBbox = (oldBbox[0],-oldBbox[1],oldBbox[2],-oldBbox[3]) - item.transform[index] = rotMat*item.transform[index] - item.transform[index] = rotMat*original - item.imageList[index].originalImage.theta += theta - item.imageList[index].image = item.imageList[index].originalImage.rotate(item.imageList[index].originalImage.theta*180.0/math.pi,expand=True,resample=Image.BICUBIC) - item.imageList[index].itk = ImageTk.PhotoImage(item.imageList[index].image) - self.mainCanvas.itemconfigure(ID,image=item.imageList[index].itk) - #the image has been rotated in place - #now, compensate for any resizing and shift to the correct location - # - # p0 --- p1 p1 - # | | ---> / \ - # p2 --- p3 p0 p3 - # \ / - # p2 - # - rotMat2 = self.makeRotationMatrix(item.imageList[index].originalImage.theta,origin) - p0 = rotMat2*(oldBbox[0],-oldBbox[3])#switch to usual coordinates - p1 = rotMat2*(oldBbox[2],-oldBbox[3]) - p2 = rotMat2*(oldBbox[0],-oldBbox[1]) - p3 = rotMat2*(oldBbox[2],-oldBbox[1]) - newTopLeft = (min(p0[0],p1[0],p2[0],p3[0]),-max(p0[1],p1[1],p2[1],p3[1]))#switch back to screen coords - shift = (newTopLeft[0]-oldBbox[0],newTopLeft[1]-oldBbox[3]) - #print (theta*180.0/math.pi,origin,oldBbox,newTopLeft,shift) - #print (item.imageList[index].originalImage.size) - #print (item.imageList[index].image.size) - #print - self.mainCanvas.coords(ID,oldBbox[0]+shift[0],oldBbox[3]+shift[1]) - else: - #transform each point of the object - xform = rotMat*item.transform[0] - item.transform = [identity()] - for i in range(len(item.path.nodeSet)): - if item.path.nodeSet[i] != 'cycle': - item.path.nodeSet[i] = xform*item.path.nodeSet[i] - for i in range(len(item.path.controlSet)): - item.path.controlSet[i][0] = xform*item.path.controlSet[i][0] - item.path.controlSet[i][1] = xform*item.path.controlSet[i][1] - item.drawOnCanvas(self.mainCanvas,self.magnification) - - def deleteItem(self,item): - if isinstance(item,xasyScript) or isinstance(item,xasyText): - if isinstance(item,xasyScript): - self.undoRedoStack.add(deleteScriptAction(self,item,self.fileItems.index(item))) - else: - self.undoRedoStack.add(deleteLabelAction(self,item,self.fileItems.index(item))) - for image in item.imageList: - self.mainCanvas.delete(image.IDTag) - else: - if isinstance(item,xasyDrawnItem): - self.undoRedoStack.add(deleteDrawnItemAction(self,item,self.fileItems.index(item))) - self.mainCanvas.delete(item.IDTag) - self.fileItems.remove(item) - self.populatePropertyList() - self.clearSelection() - - def deleteSomething(self,ID): - self.clearSelection() - self.clearHighlight() - if self.editor != None: - self.editor.endEdit() - if self.editor.modified: - self.undoRedoStack.add(editDrawnItemAction(self,self.itemBeingEdited,copy.deepcopy(self.editor.shape),self.fileItems.index(self.editor.shape))) - item = self.findItem(ID) - #save an event on the undoredo stack - if isinstance(item,xasyScript): - index = self.findItemImageIndex(item,ID) - item.transform[index].deleted = True - else: - if isinstance(item,xasyText): - self.undoRedoStack.add(deleteLabelAction(self,item,self.fileItems.index(item))) - elif isinstance(item,xasyDrawnItem): - self.undoRedoStack.add(deleteDrawnItemAction(self,item,self.fileItems.index(item))) - self.fileItems.remove(item) - self.mainCanvas.delete(ID) - self.populatePropertyList() - - def scriptEditThread(self,oldText): - try: - self.newText = xasyCodeEditor.getText(oldText) - except: - self.newText = -1 - - def getNewText(self,oldText): - editThread = threading.Thread(target=self.scriptEditThread,args=(oldText,)) - editThread.start() - while editThread.isAlive(): - time.sleep(0.05) - self.parent.update() - editThread.join() - if type(self.newText)==type(-1): - self.newText = '' - raise Exception('Error launching external editor. Please check xasy options.') - - def itemEdit(self,item): - # are we too busy? - if not self.testOrAcquireLock(): - return - self.updateSelectedButton(self.toolSelectButton) - if isinstance(item,xasyScript): - self.unbindGlobalEvents() - oldText = item.script - try: - self.getNewText(oldText) - except Exception as e: - messagebox.showerror('xasy Error',e.message) - else: - if self.newText != oldText: - self.undoRedoStack.add(editScriptAction(self,item,self.newText,oldText)) - item.setScript(self.newText) - item.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(item) - self.bindGlobalEvents() - elif isinstance(item,xasyText): - theText = simpledialog.askstring(title="Xasy - Text",prompt="Enter text to display:",initialvalue=item.label.text,parent=self.parent) - if theText != None and theText != "": - self.undoRedoStack.add(editLabelTextAction(self,item,theText,item.label.text)) - item.label.text = theText - item.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(item) - elif isinstance(item,xasyShape): - self.clearSelection() - self.clearHighlight() - self.itemBeingEdited = copy.deepcopy(item) - self.editor = xasyBezierEditor(self,item,self.mainCanvas) - self.updateSelection() - self.releaseLock() - - def itemEditEvt(self,event): - if not self.inDrawingMode: - ID = self.mainCanvas.find_withtag(CURRENT)[0] - item = self.findItem(ID) - self.itemEdit(item) - - def itemDrag(self,event): - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - if self.selectedButton not in [self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton]: - return - if "selectedItem" in self.mainCanvas.gettags(CURRENT): - self.amDragging = True - for ID in self.mainCanvas.find_withtag("selectedItem"): - transform = identity() - if self.selectedButton == self.toolMoveButton: - translation = (x0-self.dragStartx,-(y0-self.dragStarty)) - elif self.selectedButton == self.toolVertiMoveButton: - translation = (0,-(y0-self.dragStarty)) - elif self.selectedButton == self.toolHorizMoveButton: - translation = (x0-self.dragStartx,0) - self.translateSomething(ID,(translation[0]/self.magnification,translation[1]/self.magnification)) - self.mainCanvas.move(ID,translation[0],-translation[1]) - self.updateSelection() - self.updateCanvasSize() - self.distanceDragged = (self.distanceDragged[0]+translation[0],self.distanceDragged[1]-translation[1]) - self.dragStartx,self.dragStarty = x0,y0 - - def itemMouseUp(self,event): - self.freeMouseDown = True - if self.amDragging: - IDList = self.mainCanvas.find_withtag("selectedItem") - itemList = [] - indexList = [] - for ID in IDList: - item = self.findItem(ID) - if item not in itemList: - itemList.append(item) - try: - indexList.append([self.findItemImageIndex(item,ID)]) - except: - indexList.append([None]) - else: - indexList[itemList.index(item)].append(self.findItemImageIndex(item,ID)) - self.undoRedoStack.add(translationAction(self,itemList,indexList,(self.distanceDragged[0],-self.distanceDragged[1]))) - self.amDragging = False - - def itemSelect(self,event): - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - self.dragStartx,self.dragStarty = x0,y0 - self.distanceDragged = (0,0) - if self.selectedButton in [self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton]: - self.freeMouseDown = False - if self.selectedButton == self.toolSelectButton or (len(self.mainCanvas.find_withtag("selectedItem"))<=1 and self.selectedButton in [self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton]): - self.clearSelection() - self.setSelection(CURRENT) - - def itemToggleSelect(self,event): - #print ("control click") - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - if self.selectedButton in [self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton]: - self.freeMouseDown = False - self.dragStartx,self.dragStarty = x0,y0 - if "selectedItem" in self.mainCanvas.gettags(CURRENT): - self.unSelect(CURRENT) - else: - self.setSelection(CURRENT) - - def itemDelete(self,event): - if(not self.testOrAcquireLock()): - return - itemList = [] - self.undoRedoStack.add(endActionGroup) - for ID in self.mainCanvas.find_withtag("selectedItem"): - item = self.findItem(ID) - if isinstance(item,xasyScript): - index = self.findItemImageIndex(item,ID) - if item not in itemList: - itemList.append([item,[index],[item.transform[index]]]) - else: - x = None - for i in itemList: - if i[0] == item: - x = i - x[1].append(index) - x[2].append(item.transform[index]) - self.deleteSomething(ID) - for entry in itemList: - self.undoRedoStack.add(deleteScriptItemAction(self,entry[0],entry[1],entry[2])) - self.undoRedoStack.add(beginActionGroup) - self.clearSelection() - self.releaseLock() - - def itemMotion(self,event): - pass - - def itemHighlight(self,event): - if self.selectedButton in [self.toolSelectButton] and self.editor == None: - box = self.mainCanvas.bbox(CURRENT) - box = (box[0]-2,box[1]-2,box[2]+2,box[3]+2) - if len(self.mainCanvas.find_withtag("highlightBox"))==0: - self.mainCanvas.create_rectangle(box,tags="highlightBox",width=2,outline="red") - else: - self.mainCanvas.tag_raise("highlightBox") - self.mainCanvas.coords("highlightBox",*box) - self.mainCanvas.tag_bind("highlightBox","<Leave>",self.itemUnHighlight) - - def itemUnHighlight(self,event): - self.clearHighlight() - - def clearHighlight(self): - self.mainCanvas.delete("highlightBox") - - def itemLeftDown(self,event): - pass - - def itemLeftUp(self,event): - pass - - def itemRightDown(self,event): - pass - - def itemRightUp(self,event): - pass - - def canvMotion(self,event): - self.coords.config( - text="(%.3f,%.3f)"%(self.mainCanvas.canvasx(event.x)/self.magnification,-self.mainCanvas.canvasy(event.y)/self.magnification) - ) - - def addItemToFile(self,item): - self.fileItems.append(item) - self.propList.insert(0,self.describeItem(item)) - self.updateCanvasSize() - - def startDraw(self,event): - # don't start if we can't finish - if not self.testOrAcquireLock() and not self.inDrawingMode: - return - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - #self.mainCanvas.create_oval(x,y,x,y,width=5) - if self.selectedButton == self.toolDrawEllipButton: - pass - elif self.selectedButton == self.toolFillEllipButton: - pass - elif self.selectedButton == self.toolTextButton: - theText = simpledialog.askstring(title="Xasy - Text",prompt="Enter text to display:",initialvalue="",parent=self.parent) - if theText != None and theText != "": - theItem = xasyText(theText,(x,-y),asyPen(self.penColor,self.penWidth,self.penOptions)) - theItem.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(theItem) - self.addItemToFile(theItem) - self.undoRedoStack.add(addLabelAction(self,theItem)) - self.releaseLock() - self.updateSelectedButton(self.toolSelectButton) - elif self.selectedButton in [self.toolDrawLinesButton,self.toolDrawBeziButton,self.toolDrawPolyButton,self.toolDrawShapeButton,self.toolFillPolyButton,self.toolFillShapeButton]: - self.inDrawingMode = True - try: - if len(self.itemBeingDrawn.path.nodeSet) == 0: - raise Exception - else: - if self.selectedButton in [self.toolDrawLinesButton,self.toolDrawPolyButton,self.toolFillPolyButton]: - self.itemBeingDrawn.appendPoint((x,-y),'--') - else:#drawBezi,drawShape,fillShape - self.itemBeingDrawn.appendPoint((x,-y),'..') - except: - path = asyPath() - if self.selectedButton == self.toolDrawLinesButton: - path.initFromNodeList([(x,-y),(x,-y)],['--']) - elif self.selectedButton == self.toolDrawBeziButton: - path.initFromNodeList([(x,-y),(x,-y)],['..']) - elif self.selectedButton == self.toolDrawPolyButton or self.selectedButton == self.toolFillPolyButton: - path.initFromNodeList([(x,-y),(x,-y),'cycle'],['--','--']) - elif self.selectedButton == self.toolDrawShapeButton or self.selectedButton == self.toolFillShapeButton: - path.initFromNodeList([(x,-y),(x,-y),'cycle'],['..','..']) - if self.selectedButton in [self.toolDrawLinesButton,self.toolDrawBeziButton,self.toolDrawPolyButton,self.toolDrawShapeButton]: - self.itemBeingDrawn = xasyShape(path,pen=asyPen(self.penColor,self.penWidth,self.penOptions)) - else: - if self.penOptions.find("fillrule") != -1 or self.penOptions.find("evenodd") != -1 or self.penOptions.find("zerowinding") != -1: - options = self.penOptions - else: - options = "evenodd" - self.itemBeingDrawn = xasyFilledShape(path,pen=asyPen(self.penColor,self.penWidth,options)) - self.itemBeingDrawn.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(self.itemBeingDrawn) - self.mainCanvas.bind("<Motion>",self.extendDraw) - - def extendDraw(self,event): - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - tags = self.mainCanvas.gettags("itemBeingDrawn") - self.itemBeingDrawn.setLastPoint((x,-y)) - self.itemBeingDrawn.drawOnCanvas(self.mainCanvas,self.magnification) - self.canvMotion(event) - - def endDraw(self,event): - if not self.inDrawingMode or self.itemBeingDrawn == None: - return - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - #if self.selectedButton in [self.toolDrawLinesButton,self.toolDrawPolyButton,self.toolFillPolyButton]: - #self.itemBeingDrawn.appendPoint((x,-y),'--') - #else: - #self.itemBeingDrawn.appendPoint((x,-y),'..') - - #only needed for certain key bindings when startDraw is triggered right before an endDraw - #e.g.: single click: startDraw, double click: endDraw - self.itemBeingDrawn.removeLastPoint() - self.itemBeingDrawn.setLastPoint((x,-y)) - self.itemBeingDrawn.drawOnCanvas(self.mainCanvas,self.magnification) - self.addItemToFile(self.itemBeingDrawn) - self.undoRedoStack.add(addDrawnItemAction(self,self.itemBeingDrawn)) - self.itemBeingDrawn = None - self.mainCanvas.dtag("itemBeingDrawn","itemBeingDrawn") - self.mainCanvas.bind("<Motion>",self.canvMotion) - self.inDrawingMode = False - self.releaseLock() - - def canvLeftDown(self,event): - #print ("Left Mouse Down") - self.selectDragStart = (self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)) - theBbox = self.mainCanvas.bbox("selectedItem") - if theBbox != None: - self.selectBboxMidpoint = (theBbox[0]+theBbox[2])/2.0,-(theBbox[1]+theBbox[3])/2.0 - if self.freeMouseDown and self.editor != None: - self.editor.endEdit() - if self.editor.modified: - self.undoRedoStack.add(editDrawnItemAction(self,self.itemBeingEdited,copy.deepcopy(self.editor.shape),self.fileItems.index(self.editor.shape))) - self.editor = None - elif self.selectedButton in (self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton): - if self.freeMouseDown: - self.clearSelection() - self.dragSelecting = False - else: - self.startDraw(event) - - def canvLeftUp(self,event): - #print ("Left Mouse Up") - # if we're busy, ignore it - if not self.testOrAcquireLock(): - return - self.freeMouseDown = True - if self.inRotatingMode: - for item in self.itemsBeingRotated: - item.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(item) - self.updateSelection() - self.itemsBeingRotated = [] - self.inRotatingMode = False - if self.dragSelecting: - self.hideSelectionBox() - self.dragSelecting = False - self.mainCanvas.addtag_enclosed("enclosed",self.selectDragStart[0],self.selectDragStart[1],self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y)) - for item in self.mainCanvas.find_withtag("enclosed"): - tags = self.mainCanvas.gettags(item) - if "drawn" not in tags and "image" not in tags: - self.mainCanvas.dtag(item,"enclosed") - self.mainCanvas.addtag_withtag("selectedItem","enclosed") - self.mainCanvas.dtag("enclosed","enclosed") - if self.selectedButton == self.toolSelectButton and len(self.mainCanvas.find_withtag("selectedItem")) > 0: - self.updateSelectedButton(self.toolMoveButton) - self.updateSelection() - self.releaseLock() - - def canvDrag(self,event): - x0,y0 = self.mainCanvas.canvasx(event.x),self.mainCanvas.canvasy(event.y) - x = x0/self.magnification - y = y0/self.magnification - if self.selectedButton == self.toolSelectButton and self.editor == None: - self.mainCanvas.coords("outlineBox",self.selectDragStart[0],self.selectDragStart[1],x0,y0) - self.showSelectionBox() - self.dragSelecting = True - elif self.selectedButton == self.toolRotateButton and self.editor == None: - bbox = self.mainCanvas.bbox("selectedItem") - if bbox != None: - p1 = self.selectDragStart[0]-self.selectBboxMidpoint[0],-self.selectDragStart[1]-self.selectBboxMidpoint[1] - mp1 = math.sqrt(p1[0]**2+p1[1]**2) - p2 = x0-self.selectBboxMidpoint[0],-y0-self.selectBboxMidpoint[1] - mp2 = math.sqrt(p2[0]**2+p2[1]**2) - if mp1 != 0: - t1 = math.acos(p1[0]/mp1) - if p1[1] < 0: - t1 *= -1 - else: - t1 = 0 - if mp2 != 0: - t2 = math.acos(p2[0]/mp2) - if p2[1] < 0: - t2 *= -1 - else: - t2 = 0 - theta = t2-t1 - self.selectDragStart = x0,y0 - self.itemsBeingRotated = [] - for ID in self.mainCanvas.find_withtag("selectedItem"): - self.rotateSomething(ID,theta,self.selectBboxMidpoint) - item = self.findItem(ID) - if not item in self.itemsBeingRotated: - self.itemsBeingRotated.append(item) - self.updateSelection() - self.updateCanvasSize() - if not self.inRotatingMode: - self.currentRotationAngle = theta - IDList = self.mainCanvas.find_withtag("selectedItem") - itemList = [] - indexList = [] - for ID in IDList: - item = self.findItem(ID) - if item not in itemList: - itemList.append(item) - try: - indexList.append([self.findItemImageIndex(item,ID)]) - except: - indexList.append([None]) - else: - indexList[itemList.index(item)].append(self.findItemImageIndex(item,ID)) - self.undoRedoStack.add(rotationAction(self,itemList,indexList,self.currentRotationAngle,self.selectBboxMidpoint)) - self.inRotatingMode = True - else: - self.currentRotationAngle += theta - self.undoRedoStack.undoStack[-1].angle = self.currentRotationAngle - - def canvEnter(self,event): - self.freeMouseDown = True - event.widget.focus_set() - - def canvLeave(self,event): - self.freeMouseDown = False - - def canvRightDown(self,event): - pass - #print ("Right Mouse Down") - - def canvRightUp(self,event): - pass - #print ("Right Mouse Up") - - def configEvt(self,event): - self.updateCanvasSize() - self.sizePane() - - def sizePane(self): - width = self.windowPane.winfo_width()-10 - cwidth = min(int(0.87*self.windowPane.winfo_width()),width-75) - if self.paneVisible: - self.windowPane.paneconfigure(self.canvFrame,minsize=cwidth) - else: - self.windowPane.paneconfigure(self.canvFrame,minsize=width) - self.windowPane.paneconfigure(self.propFrame,minsize=75) - - def togglePaneEvt(self,event): - self.paneVisible = not self.paneVisible - self.sizePane() - - def popupDelete(self): - self.deleteItem(self.itemPopupMenu.item) - - def popupEdit(self): - self.itemEdit(self.itemPopupMenu.item) - - def popupViewCode(self): - messagebox.showinfo("Item Code",self.itemPopupMenu.item.getCode()) - - def popupClearTransform(self): - self.undoRedoStack.add(clearItemTransformsAction(self,self.itemPopupMenu.item,copy.deepcopy(self.itemPopupMenu.item.transform))) - if isinstance(self.itemPopupMenu.item,xasyScript) or isinstance(self.itemPopupMenu.item,xasyText): - for i in range(len(self.itemPopupMenu.item.transform)): - self.itemPopupMenu.item.transform[i] = identity() - else: - self.itemPopupMenu.item.transform = [identity()] - self.popupRedrawItem() - - def popupRedrawItem(self): - if not self.testOrAcquireLock(): - return - self.clearSelection() - self.clearHighlight() - self.itemPopupMenu.item.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(self.itemPopupMenu.item) - self.updateCanvasSize() - self.releaseLock() - - def hidePopupMenu(self): - try: - self.itemPopupMenu.unpost() - except: - pass - - def itemMenuPopup(self,parent,item,x,y): - self.hidePopupMenu() - self.itemPopupMenu = Menu(parent,tearoff=False) - self.itemPopupMenu.add_command(label="Edit",command=self.popupEdit) - self.itemPopupMenu.add_command(label="Clear Transforms",command=self.popupClearTransform) - self.itemPopupMenu.add_command(label="Redraw",command=self.popupRedrawItem) - self.itemPopupMenu.add_command(label="View code",command=self.popupViewCode) - self.itemPopupMenu.add_separator() - self.itemPopupMenu.add_command(label="Delete",command=self.popupDelete) - self.itemPopupMenu.item = item - #self.itemPopupMenu.bind("<Leave>",lambda a:self.itemPopupMenu.unpost()) - #self.itemPopupMenu.bind("<FocusOut>",lambda a:self.itemPopupMenu.unpost()) - self.itemPopupMenu.post(x,y) - - def itemPropMenuPopup(self,event): - try: - item = self.fileItems[len(self.fileItems)-int(self.propList.curselection()[0])-1] - self.itemMenuPopup(self.propList,item,event.x_root,event.y_root) - except: - pass - - def itemCanvasMenuPopup(self,event): - if self.selectedButton in (self.toolSelectButton,self.toolMoveButton,self.toolVertiMoveButton,self.toolHorizMoveButton,self.toolRotateButton): - try: - item = self.findItem(self.mainCanvas.find_withtag(CURRENT)[0]) - except: - item = None - if item != None: - self.itemMenuPopup(self.mainCanvas,item,event.x_root,event.y_root) - - def editOptions(self): - if(not self.testOrAcquireLock()): - return - self.releaseLock() - xasyOptionsDialog.xasyOptionsDlg(self.parent) - self.applyOptions() - - def resetOptions(self): - xasyOptions.setDefaults() - self.applyOptions() - - def applyPenWidth(self): - self.pendingPenWidthChange = None - if self.validatePenWidth(): - old = self.penWidth - self.penWidth = float(self.penWidthEntry.get()) - if old != self.penWidth: - self.showCurrentPen() - - def validatePenWidth(self): - text = self.penWidthEntry.get() - try: - width = float(text) - if width <= 0: - return False - else: - return True - except: - return False - - def showCurrentPen(self): - mag = 1 - width = self.penWidth - while width > 10: - width /= 2 - mag *= 2 - self.penDisp.itemconfigure("penDisp",width=width,fill=self.tkPenColor) - self.penDisp.itemconfigure("penMag",text="x%d"%mag) - #apply the new pen to any selected items - IDs = self.mainCanvas.find_withtag("selectedItem") - madeAChange = False - for ID in IDs: - item = self.findItem(ID) - if not isinstance(item,xasyScript): - if not madeAChange: - self.undoRedoStack.add(endActionGroup) - madeAChange = True - if isinstance(item,xasyText): - temp = item.label.pen - item.label.pen = asyPen(self.penColor,self.penWidth,self.penOptions) - item.drawOnCanvas(self.mainCanvas,self.magnification) - self.bindItemEvents(item) - self.setSelection(item.imageList[0].IDTag) - self.undoRedoStack.add(editLabelPenAction(self,temp,asyPen(self.penColor,self.penWidth,self.penOptions),self.fileItems.index(item))) - else: - temp = copy.deepcopy(item) - item.pen = asyPen(self.penColor,self.penWidth,self.penOptions) - item.drawOnCanvas(self.mainCanvas,self.magnification) - self.undoRedoStack.add(editDrawnItemAction(self,temp,copy.deepcopy(item),self.fileItems.index(item))) - if madeAChange: - self.undoRedoStack.add(beginActionGroup) - - def applyPenWidthEvt(self,event): - if not self.testOrAcquireLock(): - return - self.applyPenWidth() - self.releaseLock() - - def penWidthChanged(self,event): - if self.pendingPenWidthChange is not None: - self.penWidthEntry.after_cancel(self.pendingPenWidthChange) - self.pendingPenWidthChange = self.penWidthEntry.after(1000,self.applyPenWidth) - - def applyPenOptEvt(self,event): - if not self.testOrAcquireLock(): - return - self.applyPenOpt() - self.releaseLock() - - def validatePenOpt(self): - try: - penTest = asyPen(self.penColor,self.penWidth,self.penOptEntry.get()) - return True - except: - self.penOptEntry.select_range(0,END) - self.penOptEntry.delete(0,END) - self.penOptEntry.insert(END,"Invalid Pen Options") - self.penOptEntry.after(5000,self.clearInvalidOptEntry) - self.penOptions = "" - return False - - def clearInvalidOptEntry(self): - self.penOptEntry.select_range(0,END) - self.penOptEntry.delete(0,END) - - def applyPenOpt(self): - if self.validatePenOpt(): - old = self.penOptions - self.penOptions = self.penOptEntry.get() - if old != self.penOptions: - self.showCurrentPen() - - def undoOperation(self): - self.undoRedoStack.undo() - - def redoOperation(self): - self.undoRedoStack.redo() - - def resetStacking(self): - for item in self.fileItems: - self.raiseSomething(item,force=True) diff --git a/Master/texmf-dist/asymptote/GUI/xasyOptions.py b/Master/texmf-dist/asymptote/GUI/xasyOptions.py index 927490b77aa..8f023b2eeb4 100755 --- a/Master/texmf-dist/asymptote/GUI/xasyOptions.py +++ b/Master/texmf-dist/asymptote/GUI/xasyOptions.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ########################################################################### # # xasyOptions provides a mechanism for storing and restoring a user's @@ -10,117 +10,149 @@ # ########################################################################### -import pickle -import sys,os -import errno - -defaultOptions = { - 'asyPath':'asy', - 'showDebug':False, - 'showGrid':False, - 'gridX':10, - 'gridY':10, - 'gridColor':'#eeeeee', - 'showAxes':True, - 'axisX':10, - 'axisY':10, - 'axesColor':'#cccccc', - 'tickColor':'#eeeeee', - 'defPenOptions':'', - 'defPenColor':'#000000', - 'defPenWidth':1.0, - 'externalEditor':'' - } - -if sys.platform[:3] == "win": - defaultOptions['externalEditor'] = "%PROGRAMFILES%\Windows NT\Accessories\wordpad.exe" -else: - defaultOptions['externalEditor'] = "emacs" - - -options = defaultOptions.copy() - -def settingsFileLocation(): - folder = "" - try: - folder = os.path.expanduser("~/.asy/") - except: +import json +import sys +import io +import os +import platform +import shutil + +import configs + +try: + import cson +except ModuleNotFoundError: + cson = None + +try: pass - return os.path.normcase(os.path.join(folder,"xasy.conf")) +# import yaml +except ModuleNotFoundError: + yaml = None + +class xasyOptions: + def defaultOptions(self): + if self._defaultOptions is None: + f = io.open(self._defualtOptLocation) + try: + opt = cson.loads(f.read()) + finally: + f.close() + self._defaultOptions = opt + return self._defaultOptions + + def overrideSettings(self): + settingsName = platform.system() + + if settingsName not in self.options: + return + + for key in self.options[settingsName]: + self.options[key] = self.options[settingsName][key] + + + def settingsFileLocation(self): + folder = os.path.expanduser("~/.asy/") + + searchOrder = ['.cson', '.yaml', '.json', ''] + + searchIndex = 0 + found = False + currentFile = '' + while searchIndex < len(searchOrder) and not found: + currentFile = os.path.join(folder, self.configName + searchOrder[searchIndex]) + if os.path.isfile(currentFile): + found = True + searchIndex += 1 + + if found: + return os.path.normcase(currentFile) + else: + return os.path.normcase(os.path.join(folder, self.configName + '.cson')) + def __init__(self, configName, defaultConfigLocation): + self.configName = configName + self.defaultConfigName = defaultConfigLocation + + self._defaultOptions = None + self._defualtOptLocation = os.path.join(defaultConfigLocation) + + self.options = self.defaultOptions() + self.load() + + def __getitem__(self, item): + return self.options[item] + + def __setitem__(self, key, value): + self.options[key] = value + + def load(self): + fileName = self.settingsFileLocation() + if not os.path.exists(fileName): + # make folder + thedir = os.path.dirname(fileName) + if not os.path.exists(thedir): + os.makedirs(thedir) + if not os.path.isdir(thedir): + raise Exception("Configuration folder path does not point to a folder") + self.setDefaults() + f = io.open(fileName, 'r') + try: + ext = os.path.splitext(fileName)[1] + if ext == '.cson': + if cson is None: + raise ModuleNotFoundError + newOptions = cson.loads(f.read()) + elif ext in {'.yml', '.yaml'}: + if yaml is None: + raise ModuleNotFoundError + newOptions = yaml.load(f) + else: + newOptions = json.loads(f.read()) + except (IOError, ModuleNotFoundError): + self.setDefaults() + else: + for key in self.options.keys(): + if key in newOptions: + assert isinstance(newOptions[key], type(self.options[key])) + else: + newOptions[key] = self.options[key] + self.options = newOptions + finally: + f.close() + + def setDefaults(self): + self.options = self.defaultOptions() + if sys.platform[:3] == 'win': # for windows, wince, win32, etc + # setAsyPathFromWindowsRegistry() + pass + folder = os.path.expanduser("~/.asy/") + defaultPath = os.path.join(folder, self.configName + '.cson') + shutil.copy2(self._defualtOptLocation, defaultPath) + + +# TODO: Figure out how to merge this back. +""" def setAsyPathFromWindowsRegistry(): - try: - import _winreg as registry - #test both registry locations - try: - key = registry.OpenKey(registry.HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Asymptote") - options['asyPath'] = registry.QueryValueEx(key,"Path")[0]+"\\asy.exe" - registry.CloseKey(key) - except: - key = registry.OpenKey(registry.HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Asymptote") - options['asyPath'] = registry.QueryValueEx(key,"InstallLocation")[0]+"\\asy.exe" - registry.CloseKey(key) - except: - #looks like asy is not installed or this isn't Windows - pass + if os.name == 'nt': + import _winreg as registry + # test both registry locations + try: + key = registry.OpenKey(registry.HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Asymptote") + options['asyPath'] = registry.QueryValueEx(key, "Path")[0] + "\\asy.exe" + registry.CloseKey(key) + except: + key = registry.OpenKey(registry.HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Asymptote") + options['asyPath'] = registry.QueryValueEx(key, "InstallLocation")[0] + "\\asy.exe" + registry.CloseKey(key) +""" + -def setDefaults(): - global options - options = defaultOptions.copy() - if sys.platform[:3] == 'win': #for windows, wince, win32, etc - setAsyPathFromWindowsRegistry() - save() - -def load(): - global options - fileName = settingsFileLocation() - if not os.path.exists(fileName): - #make folder - thedir = os.path.dirname(fileName) - if not os.path.exists(thedir): - try: - os.makedirs(thedir) - except: - raise Exception("Could not create configuration folder") - if not os.path.isdir(thedir): - raise Exception("Configuration folder path does not point to a folder") - setDefaults() - try: - f = open(fileName,"rb") - newOptions = pickle.load(f) - for key in options.keys(): - if type(newOptions[key]) != type(options[key]): - raise Exception("Bad type for entry in xasy settings") - options = newOptions - except: - setDefaults() - -def save(): - global options - fileName = settingsFileLocation() - try: - f = open(fileName,"wb") - pickle.dump(options,f) - f.close() - except: - raise Exception("Error saving preferences") - -load() - -if __name__=='__main__': - print (settingsFileLocation()) - print ("Current content") - load() - print ("Setting defaults") - setDefaults() - save() - load() - options['showAxes'] = options['showGrid'] = False - save() - print ("Set to False") - load() - options['showAxes'] = options['showGrid'] = True - save() - print ("Set to True") - load() - print (options) +class BasicConfigs: + _configPath = list(configs.__path__)[0] + defaultOpt = xasyOptions( + 'xasyconfig', os.path.join(_configPath, 'xasyconfig.cson')) + keymaps = xasyOptions('xasykeymap', os.path.join( + _configPath, 'xasykeymap.cson')) diff --git a/Master/texmf-dist/asymptote/GUI/xasyOptionsDialog.py b/Master/texmf-dist/asymptote/GUI/xasyOptionsDialog.py deleted file mode 100755 index 271070ea457..00000000000 --- a/Master/texmf-dist/asymptote/GUI/xasyOptionsDialog.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python -########################################################################### -# -# xasyOptionsDialog implements a dialog window to allow users to edit -# their preferences and specify program options -# -# -# Author: Orest Shardt -# Created: June 29, 2007 -# -########################################################################### - -import os -import sys -import xasyOptions -import xasyColorPicker - -if sys.version_info >= (3, 0): - from tkinter import * - from tkinter import simpledialog, messagebox, filedialog -else: - # python2 - from Tkinter import * - import tkSimpleDialog as simpledialog - import tkMessageBox as messagebox - import tkFileDialog as filedialog - # import tkColorChooser as colorchooser - -class xasyOptionsDlg(simpledialog.Dialog): - """A dialog to interact with users about their preferred settings""" - def __init__(self,master=None): - simpledialog.Dialog.__init__(self,master,"xasy Options") - - def body(self,master): - optFrame = Frame(master) - optFrame.grid(row=0,column=0,sticky=N+S+E+W) - - asyGrp = LabelFrame(optFrame,text="Asymptote",padx=5,pady=5) - asyGrp.grid(row=0,column=0,sticky=E+W) - asyGrp.rowconfigure(0,weight=1) - asyGrp.rowconfigure(1,weight=1) - asyGrp.columnconfigure(0,weight=1) - asyGrp.columnconfigure(0,weight=2) - Label(asyGrp,text="Command").grid(row=0,column=0,sticky=W) - self.ap = Entry(asyGrp) - self.ap.insert(END,xasyOptions.options['asyPath']) - self.ap.grid(row=0,column=1,sticky=E+W) - Button(asyGrp,text="...",command=self.findAsyPath).grid(row=0,column=2,sticky=E+W) - self.showDebug = BooleanVar() - self.showDebug.set(xasyOptions.options['showDebug']) - self.sd = Checkbutton(asyGrp,text="Show debugging info in console",var=self.showDebug) - self.sd.grid(row=1,column=0,columnspan=2,sticky=W) - - editGrp = LabelFrame(optFrame,text="External Editor",padx=5,pady=5) - editGrp.grid(row=1,column=0,sticky=E+W) - editGrp.rowconfigure(0,weight=1) - editGrp.rowconfigure(1,weight=1) - editGrp.columnconfigure(0,weight=1) - editGrp.columnconfigure(0,weight=2) - Label(editGrp,text="Program").grid(row=0,column=0,sticky=W) - self.ee = Entry(editGrp) - self.ee.insert(END,xasyOptions.options['externalEditor']) - self.ee.grid(row=0,column=1,sticky=E+W) - Button(editGrp,text="...",command=self.findEEPath).grid(row=0,column=2,sticky=E+W) - - penGrp = LabelFrame(optFrame,text="Default Pen",padx=5,pady=5) - penGrp.grid(row=2,column=0,sticky=E+W) - penGrp.rowconfigure(0,weight=1) - penGrp.rowconfigure(1,weight=1) - penGrp.rowconfigure(2,weight=1) - penGrp.columnconfigure(1,weight=1) - Label(penGrp,text="Color").grid(row=0,column=0,sticky=E) - self.pc = xasyOptions.options['defPenColor'] - Button(penGrp,text="Change",command=self.changePenColor).grid(row=0,column=1,sticky=W) - Label(penGrp,text="Width").grid(row=1,column=0,sticky=E) - self.pw = Entry(penGrp) - self.pw.insert(END,str(xasyOptions.options['defPenWidth'])) - self.pw.grid(row=1,column=1,sticky=E+W) - Label(penGrp,text="Options").grid(row=2,column=0,sticky=E) - self.po = Entry(penGrp) - self.po.insert(END,xasyOptions.options['defPenOptions']) - self.po.grid(row=2,column=1,sticky=E+W) - - dispGrp = LabelFrame(optFrame,text="Display Options",padx=5,pady=5) - dispGrp.grid(row=3,column=0,sticky=E+W) - dispGrp.rowconfigure(0,weight=1) - dispGrp.rowconfigure(1,weight=1) - dispGrp.rowconfigure(2,weight=1) - dispGrp.rowconfigure(3,weight=1) - dispGrp.columnconfigure(0,weight=1) - dispGrp.columnconfigure(1,weight=1) - dispGrp.columnconfigure(2,weight=1) - self.showAxes = BooleanVar() - self.showAxes.set(xasyOptions.options['showAxes']) - self.sa = Checkbutton(dispGrp,text="Show Axes",var=self.showAxes) - self.sa.grid(row=0,column=0,sticky=W) - self.ac = xasyOptions.options['axesColor'] - Button(dispGrp,text="Color...",command=self.changeAxesColor).grid(row=1,column=0) - Label(dispGrp,text="x").grid(row=0,column=1,padx=5,sticky=E) - self.axs = Entry(dispGrp,width=6) - self.axs.insert(END,xasyOptions.options['axisX']) - self.axs.grid(row=0,column=2,sticky=W+E) - Label(dispGrp,text="y").grid(row=1,column=1,padx=5,sticky=E) - self.ays = Entry(dispGrp,width=6) - self.ays.insert(END,xasyOptions.options['axisY']) - self.ays.grid(row=1,column=2,sticky=W+E) - - self.showGrid = BooleanVar() - self.showGrid.set(xasyOptions.options['showGrid']) - self.sg = Checkbutton(dispGrp,text="Show Grid",var=self.showGrid) - self.sg.grid(row=4,column=0,sticky=W) - self.gc = xasyOptions.options['gridColor'] - Button(dispGrp,text="Color...",command=self.changeGridColor).grid(row=3,column=0) - Label(dispGrp,text="x").grid(row=2,column=1,padx=5,sticky=E) - self.gxs = Entry(dispGrp,width=6) - self.gxs.insert(END,xasyOptions.options['gridX']) - self.gxs.grid(row=2,column=2,sticky=W+E) - Label(dispGrp,text="y").grid(row=3,column=1,padx=5,sticky=E) - self.gys = Entry(dispGrp,width=6) - self.gys.insert(END,xasyOptions.options['gridY']) - self.gys.grid(row=3,column=2,sticky=W+E) - - def findEEPath(self): - if sys.platform[:3] == 'win': #for windows, wince, win32, etc - file=filedialog.askopenfile(filetypes=[("Programs","*.exe"),("All files","*")],title="Choose External Editor",parent=self) - else: - file=filedialog.askopenfile(filetypes=[("All files","*")],title="Choose External Editor",parent=self) - if file != None: - name = os.path.abspath(file.name) - file.close() - self.ee.delete(0,END) - self.ee.insert(END,name) - self.validate() - - def findAsyPath(self): - if sys.platform[:3] == 'win': #for windows, wince, win32, etc - file=filedialog.askopenfile(filetypes=[("Programs","*.exe"),("All files","*")],title="Find Asymptote Executable",parent=self) - else: - file=filedialog.askopenfile(filetypes=[("All files","*")],title="Find Asymptote Executable",parent=self) - if file != None: - name = os.path.abspath(file.name) - file.close() - self.ap.delete(0,END) - self.ap.insert(END,name) - self.validate() - - def getAColor(self,color): - result = xasyColorPicker.xasyColorDlg(self).getColor(xasyColorPicker.makeRGBfromTkColor(color)) - return xasyColorPicker.RGB255hex(xasyColorPicker.RGBreal255(result)) - - def changeAxesColor(self): - self.ac = self.getAColor(self.ac) - - def changeGridColor(self): - self.gc = self.getAColor(self.gc) - - def changePenColor(self): - self.pc = self.getAColor(self.pc) - - def apply(self): - xasyOptions.options['externalEditor'] = self.ee.get() - xasyOptions.options['asyPath'] = self.ap.get() - xasyOptions.options['showDebug'] = bool(self.showDebug.get()) - - xasyOptions.options['defPenColor'] = self.pc - xasyOptions.options['defPenWidth'] = float(self.pw.get()) - xasyOptions.options['defPenOptions'] = self.po.get() - - xasyOptions.options['showAxes'] = bool(self.showAxes.get()) - xasyOptions.options['axesColor'] = self.ac - xasyOptions.options['tickColor'] = self.ac - xasyOptions.options['axisX'] = int(self.axs.get()) - xasyOptions.options['axisY'] = int(self.ays.get()) - xasyOptions.options['showGrid'] = bool(self.showGrid.get()) - xasyOptions.options['gridColor'] = self.gc - xasyOptions.options['gridX'] = int(self.gxs.get()) - xasyOptions.options['gridY'] = int(self.gys.get()) - xasyOptions.save() - - def validateAColor(self,color): - hexdigits = '0123456789abcdef' - if len(self.pc) != 7 or self.pc[0] != '#' or sum([1 for a in self.pc[1:] if a in hexdigits]) != 6: - return False - else: - return True - - def validate(self): - """Validate the data entered into the dialog""" - #validate the color - hexdigits = '0123456789abcdef' - if not self.validateAColor(self.pc): - messagebox.showerror("xasy Options","Invalid pen color.\r\n"+self.pc,parent=self) - return False - #validate the width - try: - test = float(self.pw.get()) - except: - messagebox.showerror("xasy Options","Pen width must be a number.",parent=self) - return False - - #validate the options - #nothing to do - - #validate the axis spacing - try: - test = int(self.axs.get()) - test = int(self.ays.get()) - except: - messagebox.showerror("xasy Options","Axes' x- and y-spacing must be numbers.",parent=self) - return False - - #validate the grid spacing - try: - test = int(self.gxs.get()) - test = int(self.gys.get()) - except: - messagebox.showerror("xasy Options","Grid's x- and y-spacing must be numbers.",parent=self) - return False - - if not self.validateAColor(self.ac): - messagebox.showerror("xasy Options","Invalid axis color.\r\n"+self.ac,parent=self) - return False - - if not self.validateAColor(self.gc): - messagebox.showerror("xasy Options","Invalid grid color.\r\n"+self.gc,parent=self) - return False - - return True - -if __name__ == '__main__': - root = Tk() - xasyOptions.load() - d = xasyOptionsDlg(root) - print (d.result) diff --git a/Master/texmf-dist/asymptote/GUI/xasyStrings.py b/Master/texmf-dist/asymptote/GUI/xasyStrings.py new file mode 100755 index 00000000000..53f83a64f14 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasyStrings.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +import gettext + +p = property + +class xasyString: + def __init__(self, lang=None): + s = self + if lang is None: + _ = lambda x: x + else: + lng = gettext.translation('base', localedir='GUI/locale', languages=[lang]) + lng.install() + _ = lng.gettext + + s.rotate = _('Rotate') + s.scale = _('Scale') + s.translate = _('Translate') + + s.fileOpenFailed = _('File Opening Failed.') + s.fileOpenFailedText = _('File could not be opened.') + s.asyfyComplete = _('Ready.') diff --git a/Master/texmf-dist/asymptote/GUI/xasySvg.py b/Master/texmf-dist/asymptote/GUI/xasySvg.py new file mode 100755 index 00000000000..a5325f9bdd6 --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasySvg.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +import PyQt5.QtGui as Qg +import PyQt5.QtWidgets as Qw +import io +import subprocess +import sys + +class SvgObject(): + def __init__(self, file: str): + self.file=file + + def render(self, dpi:int) -> Qg.QImage: + try: + rawDataProc = subprocess.Popen(['rsvg-convert', '--dpi-x', str(dpi), + '--dpi-y', str(dpi), self.file], + stdout=subprocess.PIPE) + except: + Qw.QMessageBox.about(None,'rsvg-convert missing','Please install rsvg-convert version >= 2.40 in your path.') + sys.exit(-1) + + return Qg.QImage.fromData(rawDataProc.stdout.read(), 'PNG') diff --git a/Master/texmf-dist/asymptote/GUI/xasyTransform.py b/Master/texmf-dist/asymptote/GUI/xasyTransform.py new file mode 100755 index 00000000000..928e9011f0a --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasyTransform.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import xasy2asy as x2a +import PyQt5.QtGui as Qg +import PyQt5.QtCore as Qc +import numpy as np +import math + + +class xasyTransform: + @classmethod + def makeRotTransform(cls, theta, origin): + if isinstance(origin, Qc.QPointF) or isinstance(origin, Qc.QPoint): + origin = (origin.x(), origin.y()) + rotMat = (math.cos(theta), -math.sin(theta), math.sin(theta), math.cos(theta)) + shift = x2a.asyTransform((0, 0, 1 - rotMat[0], -rotMat[1], -rotMat[2], 1 - rotMat[3])) * origin + return x2a.asyTransform((shift[0], shift[1], rotMat[0], rotMat[1], rotMat[2], rotMat[3])) + + @classmethod + def makeScaleTransform(cls, sx, sy, origin): + if isinstance(origin, Qc.QPointF) or isinstance(origin, Qc.QPoint): + origin = (origin.x(), origin.y()) + shiftMat = x2a.asyTransform((0, 0, 1 - sx, 0, 0, 1 - sy)) * origin + return x2a.asyTransform((shiftMat[0], shiftMat[1], sx, 0, 0, sy)) + diff --git a/Master/texmf-dist/asymptote/GUI/xasyUtils.py b/Master/texmf-dist/asymptote/GUI/xasyUtils.py new file mode 100755 index 00000000000..255e8b6addc --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasyUtils.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import re +import typing as ty +import math +import itertools + +def tuple2StrWOspaces(val: tuple) -> str: + newStr = ','.join(['{:.6g}'.format(value) for value in val]) + return '({0})'.format(newStr) + +def tryParse(val, typ=float): + try: + return typ(val) + except ValueError: + return None + +def funcOnList(list1: ty.Union[ty.List, ty.Tuple], list2: ty.Union[ty.List, ty.Tuple], func: ty.Callable) -> tuple: + """Returns [f(x[i], y[i]) : i in 1, ..., n - 1] in order with f as func + and x and y as list1 and 2. """ + + assert len(list1) == len(list2) + return tuple([func(list1[i], list2[i]) for i in range(len(list1))]) + + +def listize(str, typ, delim='()') -> list: + str = str.strip(delim) + raw_elem = str.split(',') + final_list = [] + if isinstance(typ, (list, tuple)): + for i in range(len(raw_elem)): + if i < len(typ): + curr_typ = typ[i] + else: + curr_typ = typ[-1] + final_list.append(curr_typ(raw_elem[i].strip())) + else: + for elem in raw_elem: + final_list.append(typ(elem.strip())) + return final_list + +def twonorm(vec: ty.Iterable[ty.Union[float, int]]) -> float: + rawSquared = sum(map(lambda x: x*x, vec)) + return math.sqrt(rawSquared) + +def tryParseKey(raw_key): + """Returns None if raw key is not in #.# format""" + # See https://regex101.com/r/6G9MZD/1/ + # for the regex data + return re.fullmatch(r'^(\d+)\.(\d+)$', raw_key) diff --git a/Master/texmf-dist/asymptote/GUI/xasyValidator.py b/Master/texmf-dist/asymptote/GUI/xasyValidator.py new file mode 100755 index 00000000000..264e105441d --- /dev/null +++ b/Master/texmf-dist/asymptote/GUI/xasyValidator.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +def validateFloat(text): + try: + float(text) + return True + except ValueError: + return False + + +if __name__ == '__main__': + assert validateFloat('0.5') + assert not validateFloat('.-') diff --git a/Master/texmf-dist/asymptote/GUI/xasyVersion.py b/Master/texmf-dist/asymptote/GUI/xasyVersion.py index bbd259cadcf..178490bbf6d 100755 --- a/Master/texmf-dist/asymptote/GUI/xasyVersion.py +++ b/Master/texmf-dist/asymptote/GUI/xasyVersion.py @@ -1,2 +1,2 @@ -#!/usr/bin/env python -xasyVersion = "2.44" +#!/usr/bin/env python3 +xasyVersion = "2.47" |