From 691c84085d29b41cb7e1dabb48fd7e4eb9310530 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 27 May 2012 23:04:04 +0200 Subject: [PATCH 01/69] 0000723: improper handling of qt specific comand line arguments --- src/App/Application.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 30033380a1..17b99601fb 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1475,6 +1475,28 @@ void Application::ParseOptions(int ac, char ** av) //x11.add_options() // ("display", boost::program_options::value< string >(), "set the X-Server") // ; + //0000723: improper handling of qt specific comand line arguments + std::vector args; + bool merge=false; + for (int i=1; i args; copy(tok.begin(), tok.end(), back_inserter(args)); // Parse the file and store the options - store( boost::program_options::command_line_parser(ac, av). + store( boost::program_options::command_line_parser(args). options(cmdline_options).positional(p).extra_parser(customSyntax).run(), vm); } From 6709378af34d52ba34cca3b900090ade1f54f87e Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 28 May 2012 16:02:26 -0300 Subject: [PATCH 02/69] Added FreeCADGui.doCommand() python command --- src/Gui/Application.h | 2 ++ src/Gui/ApplicationPy.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Gui/Application.h b/src/Gui/Application.h index 74b3dbb64b..44a73d0c67 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -231,6 +231,8 @@ public: PYFUNCDEF_S(sActiveDocument); PYFUNCDEF_S(sGetDocument); + PYFUNCDEF_S(sDoCommand); + static PyMethodDef Methods[]; private: diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 9ca9ee3afe..9a2a51af4e 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -128,6 +128,9 @@ PyMethodDef Application::Methods[] = { {"getDocument", (PyCFunction) Application::sGetDocument, 1, "getDocument(string) -> object\n\n" "Get a document by its name"}, + {"doCommand", (PyCFunction) Application::sDoCommand, 1, + "doCommand(string) -> None\n\n" + "Prints the given string in the python console and runs it"}, {NULL, NULL} /* Sentinel */ }; @@ -765,3 +768,12 @@ PyObject* Application::sRunCommand(PyObject * /*self*/, PyObject *args,PyObject return 0; } } + +PyObject* Application::sDoCommand(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) +{ + char *pstr=0; + if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C + return NULL; // NULL triggers exception + Command::doCommand(Command::Doc,pstr); + return Py_None; +} From 958aa92b8284273ca713ed18e7d9d129bb1e058a Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 28 May 2012 16:02:56 -0300 Subject: [PATCH 03/69] Testing python Gui.doCommand with the Draft Line tool --- src/Mod/Draft/DraftGui.py | 6 +++++- src/Mod/Draft/DraftTools.py | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 13517f4785..7e651a6986 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -78,7 +78,11 @@ class todo: try: name = str(name) FreeCAD.ActiveDocument.openTransaction(name) - func() + if isinstance(func,list): + for l in func: + FreeCADGui.doCommand(l) + else: + func() FreeCAD.ActiveDocument.commitTransaction() except: wrn = "[Draft.todo.commit] Unexpected error:", sys.exc_info()[0], "in ", f, "(", arg, ")" diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 885e781c64..81d1bc05d1 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -415,13 +415,26 @@ class Line(Creator): def finish(self,closed=False,cont=False): "terminates the operation and closes the poly if asked" if self.obj: + # remove temporary object, if any old = self.obj.Name todo.delay(self.doc.removeObject,old) self.obj = None if (len(self.node) > 1): + # building command string + if self.support: + sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' + else: + sup = 'None' + points='[' + for n in self.node: + if len(points) > 1: + points += ',' + points += 'FreeCAD.Vector('+str(n.x) + ',' + str(n.y) + ',' + str(n.z) + ')' + points += ']' self.commit(translate("draft","Create DWire"), - partial(Draft.makeWire,self.node,closed, - face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'points='+points, + 'Draft.makeWire(points,closed='+str(closed)+',face='+str(bool(self.ui.fillmode))+',support='+sup+')']) if self.ui: self.linetrack.finalize() self.constraintrack.finalize() From 3b65d2cc40e04e674d3d397d76b9d96458eb407f Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 28 May 2012 23:39:36 +0200 Subject: [PATCH 04/69] 0000726: Fails to compile GIT --- src/Mod/Mesh/App/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Mesh/App/Makefile.am b/src/Mod/Mesh/App/Makefile.am index 27537e0605..5ba606444c 100644 --- a/src/Mod/Mesh/App/Makefile.am +++ b/src/Mod/Mesh/App/Makefile.am @@ -359,7 +359,7 @@ Mesh_la_DEPENDENCIES = libMesh.la # set the include path found by configure AM_CXXFLAGS = -I$(top_srcdir)/src/3rdParty -I$(top_srcdir)/src -I$(top_builddir)/src $(GTS_CFLAGS) \ - $(all_includes) $(QT4_CORE_CXXFLAGS) + $(all_includes) -I$(EIGEN3_INC) $(QT4_CORE_CXXFLAGS) includedir = @includedir@/Mod/Mesh/App libdir = $(prefix)/Mod/Mesh From 5821a30addd1658540174325be890223fbf57c25 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 29 May 2012 11:40:51 +0200 Subject: [PATCH 05/69] 0000714: Merge project does not include DiffuseColor --- src/Gui/Document.cpp | 2 +- src/Gui/MergeDocuments.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 4a0880ffa8..0192d3e86e 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -748,7 +748,7 @@ void Document::exportObjects(const std::vector& obj, Base: << views.size() <<"\">" << std::endl; bool xml = writer.isForceXML(); - writer.setForceXML(true); + //writer.setForceXML(true); writer.incInd(); // indention for 'ViewProvider name' std::map::const_iterator jt; for (jt = views.begin(); jt != views.end(); ++jt) { diff --git a/src/Gui/MergeDocuments.cpp b/src/Gui/MergeDocuments.cpp index a1fc7172a7..7addd29bed 100644 --- a/src/Gui/MergeDocuments.cpp +++ b/src/Gui/MergeDocuments.cpp @@ -235,4 +235,8 @@ void MergeDocuments::RestoreDocFile(Base::Reader & reader) } xmlReader.readEndElement("Document"); + + // In the file GuiDocument.xml new data files might be added + if (!xmlReader.getFilenames().empty()) + xmlReader.readFiles(static_cast(reader)); } From a1d805b15728a34189731145ff6145a1f2c457fd Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 29 May 2012 22:17:12 -0300 Subject: [PATCH 06/69] Used Gui.doCommand() in all Draft commands --- src/Mod/Draft/DraftTools.py | 186 +++++++++++++++++++++++---------- src/Mod/Draft/DraftVecUtils.py | 4 + 2 files changed, 136 insertions(+), 54 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 81d1bc05d1..93fb74fcc5 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -380,8 +380,38 @@ class Creator: todo.delayCommit(self.commitList) self.commitList = [] + def getStrings(self): + "returns a couple of useful strings fro building python commands" + + # current plane rotation + p = plane.getRotation() + qr = p.Rotation.Q + qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')' + + # support object + if self.support: + sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' + else: + sup = 'None' + + # contents of self.node + points='[' + for n in self.node: + if len(points) > 1: + points += ',' + points += DraftVecUtils.toString(n) + points += ']' + + # fill mode + if self.ui: + fil = str(bool(self.ui.fillmode)) + else: + fil = "True" + + return qr,sup,points,fil + def commit(self,name,func): - "stores partial actions to be committed to the FreeCAD document" + "stores actions to be committed to the FreeCAD document" self.commitList.append((name,func)) class Line(Creator): @@ -421,20 +451,11 @@ class Line(Creator): self.obj = None if (len(self.node) > 1): # building command string - if self.support: - sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' - else: - sup = 'None' - points='[' - for n in self.node: - if len(points) > 1: - points += ',' - points += 'FreeCAD.Vector('+str(n.x) + ',' + str(n.y) + ',' + str(n.z) + ')' - points += ']' + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create DWire"), ['import Draft', - 'points='+points, - 'Draft.makeWire(points,closed='+str(closed)+',face='+str(bool(self.ui.fillmode))+',support='+sup+')']) + 'points='+pts, + 'Draft.makeWire(points,closed='+str(closed)+',face='+fil+',support='+sup+')']) if self.ui: self.linetrack.finalize() self.constraintrack.finalize() @@ -628,9 +649,12 @@ class BSpline(Line): old = self.obj.Name self.doc.removeObject(old) try: + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create BSpline"), - partial(Draft.makeBSpline,self.node,closed, - face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'points='+pts, + 'Draft.makeBSpline(points,closed='+str(closed)+',face='+fil+',support='+sup+')']) except: print "Draft: error delaying commit" if self.ui: @@ -741,12 +765,15 @@ class Rectangle(Creator): if abs(DraftVecUtils.angle(p4.sub(p1),plane.u,plane.axis)) > 1: length = -length height = p2.sub(p1).Length if abs(DraftVecUtils.angle(p2.sub(p1),plane.v,plane.axis)) > 1: height = -height - p = plane.getRotation() - p.move(p1) try: + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create Rectangle"), - partial(Draft.makeRectangle,length,height, - p,self.ui.fillmode,support=self.support)) + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(p1), + 'Draft.makeRectangle(length='+str(length)+',height='+str(height)+',placement=pl,face='+fil+',support='+sup+')']) except: print "Draft: error delaying commit" self.finish(cont=True) @@ -1013,23 +1040,30 @@ class Arc(Creator): def drawArc(self): "actually draws the FreeCAD object" - p = plane.getRotation() - p.move(self.center) + rot,sup,pts,fil = self.getStrings() if self.closedCircle: try: - self.commit(translate("draft","Create Circle"), - partial(Draft.makeCircle,self.rad,p, - self.ui.fillmode,support=self.support)) + # building command string + self.commit(translate("draft","Create Circle"), + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',support='+sup+')']) except: - print "Draft: error delaying commit" + print "Draft: error delaying commit" else: sta = math.degrees(self.firstangle) end = math.degrees(self.firstangle+self.angle) if end < sta: sta,end = end,sta try: - self.commit(translate("draft","Create Arc"), - partial(Draft.makeCircle,self.rad,p,self.ui.fillmode, - sta,end,support=self.support)) + # building command string + self.commit(translate("draft","Create Arc"), + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',startangle='+str(sta)+',endangle='+str(end)+',support='+sup+')']) except: print "Draft: error delaying commit" self.finish(cont=True) @@ -1257,11 +1291,14 @@ class Polygon(Creator): def drawPolygon(self): "actually draws the FreeCAD object" - p = plane.getRotation() - p.move(self.center) + rot,sup,pts,fil = self.getStrings() + # building command string self.commit(translate("draft","Create Polygon"), - partial(Draft.makePolygon,self.ui.numFaces.value(),self.rad, - True,p,face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makePolygon('+str(self.ui.numFaces.value())+',radius='+str(self.rad)+',inscribed=True,placement=pl,face='+fil+',support='+sup+')']) self.finish(cont=True) def numericInput(self,numx,numy,numz): @@ -1326,8 +1363,16 @@ class Text(Creator): def createObject(self): "creates an object in the current doc" - self.commit(translate("draft","Create Text"), - partial(Draft.makeText,self.text,self.node[0])) + tx = '' + for l in self.text: + if tx: + tx += ',' + tx += '"'+l+'"' +# self.commit(translate("draft","Create Text"), +# ['import Draft', +# 'Draft.makeText(['+tx+'],'+DraftVecUtils.toString(self.node[0])+')']) + self.commit(translate("draft","Create Text"),partial(Draft.makeText,self.text,self.node[0])) + self.finish(cont=True) def action(self,arg): @@ -1429,9 +1474,9 @@ class Dimension(Creator): pt = o.ViewObject.RootNode.getChildren()[1].getChildren()[0].getChildren()[0].getChildren()[3] p3 = Vector(pt.point.getValues()[2].getValue()) self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,p1,p2,p3)) - self.commit(translate("draft","Delete Measurement"), - partial(FreeCAD.ActiveDocument.removeObject,o.Name)) + ['import Draft', + 'Draft.makeDimension('+DraftVecUtils.toString(p1)+','+DraftVecUtils.toString(p2)+','+DraftVecUtils.toString(p3)+')', + 'FreeCAD.ActiveDocument.removeObject("'+o.Name+'")']) def createObject(self): "creates an object in the current doc" @@ -1449,8 +1494,8 @@ class Dimension(Creator): self.arcmode,self.node[2])) else: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.node[0],self.node[1], - self.node[2])) + ['import Draft', + 'Draft.makeDimension('+DraftVecUtils.toString(self.node[0])+','+DraftVecUtils.toString(self.node[1])+','+DraftVecUtils.toString(self.node[2])+')']) if self.ui.continueMode: self.cont = self.node[2] if not self.dir: @@ -1712,7 +1757,7 @@ class Modifier: self.commitList = [] def commit(self,name,func): - "stores partial actions to be committed to the FreeCAD document" + "stores actions to be committed to the FreeCAD document" # print "committing" self.commitList.append((name,func)) @@ -1768,10 +1813,20 @@ class Move(Modifier): def move(self,delta,copy=False): "moving the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: - self.commit(translate("draft","Copy"),partial(Draft.move,self.sel,delta,copy)) + self.commit(translate("draft","Copy"), + ['import Draft', + 'Draft.move('+sel+','+DraftVecUtils.toString(delta)+',copy='+str(copy)+')']) else: - self.commit(translate("draft","Move"),partial(Draft.move,self.sel,delta,copy)) + self.commit(translate("draft","Move"), + ['import Draft', + 'Draft.move('+sel+','+DraftVecUtils.toString(delta)+',copy='+str(copy)+')']) self.doc.recompute() def action(self,arg): @@ -1926,14 +1981,20 @@ class Rotate(Modifier): def rot (self,angle,copy=False): "rotating the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: self.commit(translate("draft","Copy"), - partial(Draft.rotate,self.sel, - math.degrees(angle),self.center,plane.axis,copy)) + ['import Draft', + 'Draft.rotate('+sel+','+str(math.degrees(angle))+','+DraftVecUtils.toString(self.center)+',axis='+DraftVecUtils.toString(plane.axis)+',copy='+str(copy)+')']) else: self.commit(translate("draft","Rotate"), - partial(Draft.rotate,self.sel, - math.degrees(angle),self.center,plane.axis,copy)) + ['import Draft', + 'Draft.rotate('+sel+','+str(math.degrees(angle))+','+DraftVecUtils.toString(self.center)+',axis='+DraftVecUtils.toString(plane.axis)+',copy='+str(copy)+')']) def action(self,arg): "scene event handler" @@ -2194,13 +2255,18 @@ class Offset(Modifier): occmode = self.ui.occOffset.isChecked() if hasMod(arg,MODALT) or self.ui.isCopy.isChecked(): copymode = True if self.npts: + print "offset:npts=",self.npts self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.npts,copymode,occ=False)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+DraftVecUtils.toString(self.ntps)+',copy='+str(copymode)+')']) elif self.dvec: + if isinstance(self.dvec,float): + d = str(self.dvec) + else: + d = DraftVecUtils.toString(self.dvec) self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.dvec,copymode,occ=occmode)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) if hasMod(arg,MODALT): self.extendedCopy = True else: @@ -2222,9 +2288,13 @@ class Offset(Modifier): copymode = False occmode = self.ui.occOffset.isChecked() if self.ui.isCopy.isChecked(): copymode = True + if isinstance(self.dvec,float): + d = str(self.dvec) + else: + d = DraftVecUtils.toString(self.dvec) self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.dvec,copymode,occ=occmode)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) self.finish() @@ -2946,12 +3016,20 @@ class Scale(Modifier): def scale(self,delta,copy=False): "moving the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: self.commit(translate("draft","Copy"), - partial(Draft.scale,self.sel,delta,self.node[0],copy)) + ['import Draft', + 'Draft.scale('+sel+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+')']) else: self.commit(translate("draft","Scale"), - partial(Draft.scale,self.sel,delta,self.node[0],copy)) + ['import Draft', + 'Draft.scale('+sel+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+')']) def action(self,arg): "scene event handler" diff --git a/src/Mod/Draft/DraftVecUtils.py b/src/Mod/Draft/DraftVecUtils.py index 1f10a2c7ea..678965e85a 100644 --- a/src/Mod/Draft/DraftVecUtils.py +++ b/src/Mod/Draft/DraftVecUtils.py @@ -40,6 +40,10 @@ def typecheck (args_and_types, name="?"): FreeCAD.Console.PrintWarning("typecheck[" + str(name) + "]: " + str(v) + " is not " + str(t) + "\n") raise TypeError("fcvec." + str(name)) +def toString(u): + "returns a string containing a python command to recreate this vector" + return "FreeCAD.Vector("+str(u.x)+","+str(u.y)+","+str(u.z)+")" + def tup(u,array=False): "returns a tuple (x,y,z) with the vector coords, or an array if array=true" typecheck ([(u,Vector)], "tup"); From 65117d872c264baebfe42d06208ae1e387471c77 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 29 May 2012 22:25:01 -0300 Subject: [PATCH 07/69] Draft: Small fix in Offset tool --- src/Mod/Draft/DraftGeomUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 16468ad0c1..7ce4a57f00 100755 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -765,7 +765,7 @@ def isReallyClosed(wire): def getNormal(shape): "finds the normal of a shape, if possible" n = Vector(0,0,1) - if shape.ShapeType == "Face": + if (shape.ShapeType == "Face") and hasattr(shape,"normalAt"): n = shape.normalAt(0.5,0.5) elif shape.ShapeType == "Edge": if isinstance(shape.Curve,Part.Circle): From e5dcc0029ba76da4e42ab104ba873df600188219 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 30 May 2012 15:55:06 +0200 Subject: [PATCH 08/69] Handle expections in PointsPy class --- src/Mod/Points/App/PointsPyImp.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Mod/Points/App/PointsPyImp.cpp b/src/Mod/Points/App/PointsPyImp.cpp index 75df742c8e..b0b2819ac8 100644 --- a/src/Mod/Points/App/PointsPyImp.cpp +++ b/src/Mod/Points/App/PointsPyImp.cpp @@ -59,14 +59,20 @@ int PointsPy::PyInit(PyObject* args, PyObject* /*kwd*/) *getPointKernelPtr() = *(static_cast(pcObj)->getPointKernelPtr()); } else if (PyList_Check(pcObj)) { - addPoints(args); + if (!addPoints(args)) + return -1; } else if (PyTuple_Check(pcObj)) { - addPoints(args); + if (!addPoints(args)) + return -1; } else if (PyString_Check(pcObj)) { getPointKernelPtr()->load(PyString_AsString(pcObj)); } + else { + PyErr_SetString(PyExc_TypeError, "optional argument must be list, tuple or string"); + return -1; + } return 0; } From bc3e3f0cd8c2f31ca0300d54ebd3d1c23b5068f3 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 30 May 2012 17:32:55 +0200 Subject: [PATCH 09/69] Add method to find segment of a facet index --- src/Mod/Mesh/App/Core/Segmentation.cpp | 10 ++++++++++ src/Mod/Mesh/App/Core/Segmentation.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/Mod/Mesh/App/Core/Segmentation.cpp b/src/Mod/Mesh/App/Core/Segmentation.cpp index 01d99fb11f..9d0d69f325 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.cpp +++ b/src/Mod/Mesh/App/Core/Segmentation.cpp @@ -46,6 +46,16 @@ void MeshSurfaceSegment::AddSegment(const std::vector& segm) } } +MeshSegment MeshSurfaceSegment::FindSegment(unsigned long index) const +{ + for (std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + if (std::find(it->begin(), it->end(), index) != it->end()) + return *it; + } + + return MeshSegment(); +} + // -------------------------------------------------------- MeshDistancePlanarSegment::MeshDistancePlanarSegment(const MeshKernel& mesh, unsigned long minFacets, float tol) diff --git a/src/Mod/Mesh/App/Core/Segmentation.h b/src/Mod/Mesh/App/Core/Segmentation.h index 1b29f4bff9..9416028b69 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.h +++ b/src/Mod/Mesh/App/Core/Segmentation.h @@ -46,6 +46,7 @@ public: virtual void AddFacet(const MeshFacet& rclFacet); void AddSegment(const std::vector&); const std::vector& GetSegments() const { return segments; } + MeshSegment FindSegment(unsigned long) const; protected: std::vector segments; From a016ab1298704814a96284e43aa6cf1528a5d485 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 30 May 2012 12:58:08 -0300 Subject: [PATCH 10/69] Draft: fixes in commands --- src/Mod/Draft/DraftTools.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 93fb74fcc5..9135588a76 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -1363,15 +1363,15 @@ class Text(Creator): def createObject(self): "creates an object in the current doc" - tx = '' + tx = '[' for l in self.text: - if tx: + if len(tx) > 1: tx += ',' - tx += '"'+l+'"' -# self.commit(translate("draft","Create Text"), -# ['import Draft', -# 'Draft.makeText(['+tx+'],'+DraftVecUtils.toString(self.node[0])+')']) - self.commit(translate("draft","Create Text"),partial(Draft.makeText,self.text,self.node[0])) + tx += '"'+str(l)+'"' + tx += ']' + self.commit(translate("draft","Create Text"), + ['import Draft', + 'Draft.makeText('+tx+',point='+DraftVecUtils.toString(self.node[0])+')']) self.finish(cont=True) @@ -1911,19 +1911,21 @@ class ApplyStyle(Modifier): if self.ui: self.sel = Draft.getSelection() if (len(self.sel)>0): + c = ['import Draft'] for ob in self.sel: if (ob.Type == "App::DocumentObjectGroup"): - self.formatGroup(ob) + c.extend(self.formatGroup(ob)) else: - self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob)) + c.append('Draft.formatObject(FreeCAD.ActiveDocument.'+ob.Name+')') + self.commit(translate("draft","Change Style"),c) def formatGroup(self,grpob): + c=[] for ob in grpob.Group: if (ob.Type == "App::DocumentObjectGroup"): - self.formatGroup(ob) + c.extend(self.formatGroup(ob)) else: - self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob)) - + c.append('Draft.formatObject(FreeCAD.ActiveDocument.'+ob.Name+')') class Rotate(Modifier): "The Draft_Rotate FreeCAD command definition" From bbc039b8b6c42f8f66750983746f9162e5910c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Tue, 1 May 2012 14:39:19 +0200 Subject: [PATCH 11/69] Removed faces as class stored variable (PropertyPythonObject::fromString warning). --- src/Mod/Ship/Instance.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Mod/Ship/Instance.py b/src/Mod/Ship/Instance.py index 64eecd5cc7..104d2f24eb 100644 --- a/src/Mod/Ship/Instance.py +++ b/src/Mod/Ship/Instance.py @@ -39,7 +39,6 @@ class Ship: """ Creates a new ship on active document. @param faces Ship faces (Part::Shape entities). """ - self.faces = faces # Add uniqueness property to identify Ship instances obj.addProperty("App::PropertyBool","IsShip","Ship", str(Translator.translate("True if is a valid ship instance"))).IsShip=True # Add main dimensions @@ -47,7 +46,7 @@ class Ship: obj.addProperty("App::PropertyLength","Beam","Ship", str(Translator.translate("Ship beam (B) [m]"))).Beam=0.0 obj.addProperty("App::PropertyLength","Draft","Ship", str(Translator.translate("Ship draft (T) [m]"))).Draft=0.0 # Add shapes - obj.addProperty("Part::PropertyPartShape","Shape","Ship", str(Translator.translate("Ship surfaces"))).Shape = Part.makeShell(self.faces) + obj.addProperty("Part::PropertyPartShape","Shape","Ship", str(Translator.translate("Ship surfaces"))).Shape = Part.makeShell(faces) obj.Proxy = self self.obj = obj @@ -55,12 +54,12 @@ class Ship: ''' Print the name of the property that has changed ''' # FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n") if prop == "Length" or prop == "Beam" or prop == "Draft": - fp.Shape = Part.makeShell(self.faces) + fp.Shape = Part.makeShell(obj.Shape.Faces) def execute(self, obj): ''' Print a short message when doing a recomputation, this method is mandatory ''' # FreeCAD.Console.PrintMessage("Recompute Ship\n") - obj.Shape = Part.makeShell(self.faces) + obj.Shape = Part.makeShell(obj.Shape.Faces) def lineFaceSection(self,line,surface): """ Returns the point of section of a line with a face @@ -147,7 +146,7 @@ class Ship: wire = wires[j].Edges for k in range(0,len(wire)): edges.append(wire[k]) - # Slice curves to get points (Length based) + # Slice curves to get points points = [] for k in range(0,nP): planePoints = [] From 6610fac36b54d43dff8ee82bf04ea4e279e1fb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Tue, 1 May 2012 17:08:07 +0200 Subject: [PATCH 12/69] Removed shape duplicities. --- src/Mod/Ship/Instance.py | 4 ++-- src/Mod/Ship/shipCreateShip/TaskPanel.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mod/Ship/Instance.py b/src/Mod/Ship/Instance.py index 104d2f24eb..16d77f8689 100644 --- a/src/Mod/Ship/Instance.py +++ b/src/Mod/Ship/Instance.py @@ -46,7 +46,7 @@ class Ship: obj.addProperty("App::PropertyLength","Beam","Ship", str(Translator.translate("Ship beam (B) [m]"))).Beam=0.0 obj.addProperty("App::PropertyLength","Draft","Ship", str(Translator.translate("Ship draft (T) [m]"))).Draft=0.0 # Add shapes - obj.addProperty("Part::PropertyPartShape","Shape","Ship", str(Translator.translate("Ship surfaces"))).Shape = Part.makeShell(faces) + obj.Shape = Part.makeShell(faces) obj.Proxy = self self.obj = obj @@ -54,7 +54,7 @@ class Ship: ''' Print the name of the property that has changed ''' # FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n") if prop == "Length" or prop == "Beam" or prop == "Draft": - fp.Shape = Part.makeShell(obj.Shape.Faces) + pass def execute(self, obj): ''' Print a short message when doing a recomputation, this method is mandatory ''' diff --git a/src/Mod/Ship/shipCreateShip/TaskPanel.py b/src/Mod/Ship/shipCreateShip/TaskPanel.py index bec74302ad..cbb5319c84 100644 --- a/src/Mod/Ship/shipCreateShip/TaskPanel.py +++ b/src/Mod/Ship/shipCreateShip/TaskPanel.py @@ -49,6 +49,7 @@ class TaskPanel: obj.Draft = self.form.draft.value() # Discretize it ship.discretize(self.form.nSections.value(), self.form.nPoints.value()) + App.ActiveDocument.recompute() return True def reject(self): From 9bbc2c3cf526945d2a288db87a8f2f1bac3cd310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Tue, 1 May 2012 17:13:44 +0200 Subject: [PATCH 13/69] Added tank creator. --- src/Mod/Ship/CMakeLists.txt | 20 +- src/Mod/Ship/Icons/Tank.png | Bin 0 -> 10163 bytes src/Mod/Ship/Icons/Tank.xcf | Bin 0 -> 74661 bytes src/Mod/Ship/Icons/Tank.xpm | 1736 +++++++++++++++++++ src/Mod/Ship/InitGui.py | 4 + src/Mod/Ship/Makefile.am | 11 +- src/Mod/Ship/ShipGui.py | 13 + src/Mod/Ship/TankInstance.py | 1886 +++++++++++++++++++++ src/Mod/Ship/shipLoadExample/TaskPanel.ui | 4 +- src/Mod/Ship/tankCreateTank/TaskPanel.py | 167 ++ src/Mod/Ship/tankCreateTank/TaskPanel.ui | 141 ++ src/Mod/Ship/tankCreateTank/__init__.py | 36 + 12 files changed, 4013 insertions(+), 5 deletions(-) create mode 100644 src/Mod/Ship/Icons/Tank.png create mode 100644 src/Mod/Ship/Icons/Tank.xcf create mode 100644 src/Mod/Ship/Icons/Tank.xpm create mode 100644 src/Mod/Ship/TankInstance.py create mode 100644 src/Mod/Ship/tankCreateTank/TaskPanel.py create mode 100644 src/Mod/Ship/tankCreateTank/TaskPanel.ui create mode 100644 src/Mod/Ship/tankCreateTank/__init__.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index 058b9aec1b..a362a0c6a6 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -2,6 +2,7 @@ SET(ShipMain_SRCS InitGui.py ShipGui.py Instance.py + TankInstance.py ) SOURCE_GROUP("" FILES ${ShipMain_SRCS}) @@ -32,6 +33,9 @@ SET(ShipIcons_SRCS Icons/ReparametrizeIco.xpm Icons/Ship.xcf Icons/Ship.xpm + Icons/Tank.png + Icons/Tank.xcf + Icons/Tank.xpm ) SOURCE_GROUP("shipicons" FILES ${ShipIcons_SRCS}) @@ -92,7 +96,14 @@ SET(ShipUtils_SRCS ) SOURCE_GROUP("shiputils" FILES ${ShipUtils_SRCS}) -SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS}) +SET(ShipCreateTank_SRCS + tankCreateTank/__init__.py + tankCreateTank/TaskPanel.py + tankCreateTank/TaskPanel.ui +) +SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) + +SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipCreateTank_SRCS}) ADD_CUSTOM_TARGET(Ship ALL SOURCES ${all_files} @@ -148,6 +159,12 @@ INSTALL( DESTINATION Mod/Ship/shipUtils ) +INSTALL( + FILES + ${ShipCreateTank_SRCS} + DESTINATION + Mod/Ship/tankCreateTank +) INSTALL( FILES ${ShipMain_SRCS} @@ -155,3 +172,4 @@ INSTALL( Mod/Ship ) + diff --git a/src/Mod/Ship/Icons/Tank.png b/src/Mod/Ship/Icons/Tank.png new file mode 100644 index 0000000000000000000000000000000000000000..d8efae6a5662cb8dc6f4e26e5f27df74bd1ea3fe GIT binary patch literal 10163 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 literal 0 HcmV?d00001 diff --git a/src/Mod/Ship/Icons/Tank.xcf b/src/Mod/Ship/Icons/Tank.xcf new file mode 100644 index 0000000000000000000000000000000000000000..bd306edc19a166e351a2da2d58cb4ac9ecb9fbc5 GIT binary patch literal 74661 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≤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 c #D6D6D6", +", c #D7D7D7", +"' c #CDCECE", +") c #CFCECE", +"! c #D0CFCF", +"~ c #D3D3D2", +"{ c #D3D3D4", +"] c #D5D4D5", +"^ c #D6D7D6", +"/ c #D8D8D8", +"( c #D8D8D9", +"_ c #D9D9DA", +": c #CCCCCB", +"< c #CCCCCC", +"[ c #CECECD", +"} c #CECECE", +"| c #CFCFCF", +"1 c #D1D2D2", +"2 c #D5D4D4", +"3 c #D8D9D9", +"4 c #DAD9DA", +"5 c #DADBDB", +"6 c #DBDCDB", +"7 c #CACACA", +"8 c #CACBCB", +"9 c #CBCBCB", +"0 c #CCCDCD", +"a c #CECDCD", +"b c #D2D2D1", +"c c #D2D3D3", +"d c #D4D3D3", +"e c #D9D9D8", +"f c #D9D9D9", +"g c #DADBDA", +"h c #DBDCDC", +"i c #DCDCDC", +"j c #DDDDDD", +"k c #DEDEDE", +"l c #C7C8C8", +"m c #C9C9C9", +"n c #CAC9CA", +"o c #CBCBCC", +"p c #CCCDCC", +"q c #CDCDCD", +"r c #D0D1D1", +"s c #D1D2D1", +"t c #D3D2D2", +"u c #D4D4D5", +"v c #D5D5D6", +"w c #D6D7D7", +"x c #D8D7D8", +"y c #D9D8D9", +"z c #DADAD9", +"A c #DBDADA", +"B c #DBDBDC", +"C c #DDDCDD", +"D c #DDDEDE", +"E c #DEDFDE", +"F c #DFE0E0", +"G c #E0E0E1", +"H c #C6C6C6", +"I c #C7C7C7", +"J c #C8C8C7", +"K c #C9C9C8", +"L c #CACAC9", +"M c #CBCACB", +"N c #D0CFD0", +"O c #D1D0D1", +"P c #D1D1D2", +"Q c #D4D4D4", +"R c #D6D5D6", +"S c #D7D6D7", +"T c #D7D8D8", +"U c #DAD9D9", +"V c #DADADA", +"W c #DBDBDB", +"X c #DCDBDC", +"Y c #DDDDDC", +"Z c #DFDEDF", +"` c #E0DFDF", +" . c #E0E0E0", +".. c #E1E1E1", +"+. c #E2E2E2", +"@. c #C5C4C4", +"#. c #C5C5C5", +"$. c #C8C8C8", +"%. c #C9C8C8", +"&. c #D5D5D4", +"*. c #D8D7D7", +"=. c #D9D8D8", +"-. c #DCDDDD", +";. c #DEDDDD", +">. c #E3E4E3", +",. c #E5E4E4", +"'. c #C4C4C4", +"). c #C6C7C6", +"!. c #C7C8C7", +"~. c #C8C8C9", +"{. c #CACACB", +"]. c #CCCBCB", +"^. c #D0D0D1", +"/. c #D5D6D6", +"(. c #D7D7D6", +"_. c #DFDFDF", +":. c #E1E2E2", +"<. c #E3E3E3", +"[. c #E4E3E4", +"}. c #E6E5E5", +"|. c #E6E6E6", +"1. c #E6E7E7", +"2. c #C4C5C5", +"3. c #C6C6C7", +"4. c #CFD0D0", +"5. c #D1D0D0", +"6. c #D6D5D5", +"7. c #DADADB", +"8. c #DCDDDC", +"9. c #DDDDDE", +"0. c #DEDFDF", +"a. c #E1E1E2", +"b. c #E3E2E2", +"c. c #E4E4E4", +"d. c #E5E5E5", +"e. c #E8E8E7", +"f. c #E9E8E9", +"g. c #EAE9EA", +"h. c #C5C5C4", +"i. c #C6C6C5", +"j. c #C7C7C6", +"k. c #CBCACA", +"l. c #CDCDCC", +"m. c #CECFCF", +"n. c #DBDADB", +"o. c #E0E0DF", +"p. c #E1E1E0", +"q. c #E1E2E1", +"r. c #E3E3E4", +"s. c #E4E5E5", +"t. c #E7E6E7", +"u. c #E7E7E8", +"v. c #E9E9E8", +"w. c #E9EAE9", +"x. c #EAEAEA", +"y. c #EBEBEA", +"z. c #C4C5C4", +"A. c #C6C5C5", +"B. c #C6C7C7", +"C. c #CCCCCD", +"D. c #CECECF", +"E. c #D9DAD9", +"F. c #DBDBDA", +"G. c #DEDEDD", +"H. c #E0DFE0", +"I. c #E0E1E0", +"J. c #E6E5E6", +"K. c #E7E7E6", +"L. c #E7E8E7", +"M. c #E8E8E9", +"N. c #E9E9E9", +"O. c #ECECEC", +"P. c #ECEDED", +"Q. c #EDEDEE", +"R. c #010101", +"S. c #C5C4C5", +"T. c #CBCCCB", +"U. c #CDCCCC", +"V. c #CECFCE", +"W. c #CFCFCE", +"X. c #CFCFD0", +"Y. c #D7D8D7", +"Z. c #DEDDDE", +"`. c #E1E0E0", +" + c #E5E6E6", +".+ c #EBEBEB", +"++ c #ECEBEC", +"@+ c #EDEDED", +"#+ c #EEEEEE", +"$+ c #EFEFEF", +"%+ c #F0F0F0", +"&+ c #C4C4C5", +"*+ c #CFCECF", +"=+ c #D2D1D1", +"-+ c #D6D6D5", +";+ c #D8D9D8", +">+ c #DDDEDD", +",+ c #DFDEDE", +"'+ c #E2E3E3", +")+ c #E7E7E7", +"!+ c #E8E8E8", +"~+ c #EBECEC", +"{+ c #ECEDEC", +"]+ c #EFEFF0", +"^+ c #F1F0F1", +"/+ c #F1F1F1", +"(+ c #C7C6C6", +"_+ c #C9C8C9", +":+ c #C9CAC9", +"<+ c #CACBCA", +"[+ c #D2D2D3", +"}+ c #D4D5D5", +"|+ c #DCDCDB", +"1+ c #E3E3E2", +"2+ c #E4E3E3", +"3+ c #E4E4E5", +"4+ c #E5E5E6", +"5+ c #EAEAE9", +"6+ c #ECEBEB", +"7+ c #F0F0EF", +"8+ c #F0F0F1", +"9+ c #C8C7C8", +"0+ c #C8C9C9", +"a+ c #CBCBCA", +"b+ c #CCCBCC", +"c+ c #D7D6D6", +"d+ c #DDDCDC", +"e+ c #E2E2E1", +"f+ c #E2E3E2", +"g+ c #E3E4E4", +"h+ c #E8E7E8", +"i+ c #E9E8E8", +"j+ c #EAE9E9", +"k+ c #EDECEC", +"l+ c #EEEEED", +"m+ c #EFEEEF", +"n+ c #EFF0F0", +"o+ c #F1F0F0", +"p+ c #C5C6C6", +"q+ c #C7C6C7", +"r+ c #D0D0CF", +"s+ c #D4D5D4", +"t+ c #E1E0E1", +"u+ c #E8E9E9", +"v+ c #EDECED", +"w+ c #EEEDED", +"x+ c #EEEFEF", +"y+ c #F0EFF0", +"z+ c #F0F1F0", +"A+ c #D7D7D8", +"B+ c #C8C7C7", +"C+ c #C9C9CA", +"D+ c #D2D1D2", +"E+ c #D9DADA", +"F+ c #E2E1E1", +"G+ c #E9E9EA", +"H+ c #EAEBEB", +"I+ c #D8D8D7", +"J+ c #CBCCCC", +"K+ c #CDCCCD", +"L+ c #D6D6D7", +"M+ c #E6E6E5", +"N+ c #E7E6E6", +"O+ c #E8E7E7", +"P+ c #EFEEEE", +"Q+ c #CDCECD", +"R+ c #D1D1D0", +"S+ c #D2D3D2", +"T+ c #DFE0DF", +"U+ c #E3E2E3", +"V+ c #E4E5E4", +"W+ c #E5E6E5", +"X+ c #ECECED", +"Y+ c #D4D3D4", +"Z+ c #CECDCE", +"`+ c #E6E6E7", +" @ c #EBEAEB", +".@ c #ECECEB", +"+@ c #F0F1F1", +"@@ c #DCDCDD", +"#@ c #EBEAEA", +"$@ c #F0EFEF", +"%@ c #D3D4D3", +"&@ c #E6E7E6", +"*@ c #EEEDEE", +"=@ c #ADADAD", +"-@ c #565656", +";@ c #DCDBDB", +">@ c #DFDFE0", +",@ c #E7E8E8", +"'@ c #E8E9E8", +")@ c #EDEDEC", +"!@ c #EBEBEC", +"~@ c #EEEFEE", +"{@ c #B7B6B7", +"]@ c #E5E4E5", +"^@ c #EDEEED", +"/@ c #B6B6B6", +"(@ c #B6B7B7", +"_@ c #B7B7B6", +":@ c #B7B6B6", +"<@ c #E5E5E4", +"[@ c #B5B6B5", +"}@ c #B5B6B6", +"|@ c #B5B5B5", +"1@ c #B6B5B5", +"2@ c #B6B5B6", +"3@ c #E2E1E2", +"4@ c #E4E4E3", +"5@ c #B4B4B4", +"6@ c #B4B5B5", +"7@ c #B5B5B4", +"8@ c #B6B6B5", +"9@ c #E2E2E3", +"0@ c #B3B4B4", +"a@ c #B4B4B5", +"b@ c #B4B5B4", +"c@ c #B5B4B4", +"d@ c #B3B3B3", +"e@ c #B3B3B4", +"f@ c #B4B3B3", +"g@ c #B4B4B3", +"h@ c #B5B4B5", +"i@ c #D0D1D0", +"j@ c #B3B3B2", +"k@ c #B3B2B3", +"l@ c #B4B3B4", +"m@ c #90B9D9", +"n@ c #91BAD9", +"o@ c #C6C5C6", +"p@ c #B1B2B2", +"q@ c #B2B2B2", +"r@ c #B2B3B2", +"s@ c #B2B3B3", +"t@ c #8FB7D8", +"u@ c #8EB7D7", +"v@ c #8FB8D8", +"w@ c #90B8D8", +"x@ c #91BBD9", +"y@ c #91BCDA", +"z@ c #C2C2C2", +"A@ c #C3C4C3", +"B@ c #C3C4C4", +"C@ c #C8C9C8", +"D@ c #CDCDCE", +"E@ c #B1B1B1", +"F@ c #B1B2B1", +"G@ c #B2B1B2", +"H@ c #B2B2B3", +"I@ c #B3B2B2", +"J@ c #8CB4D6", +"K@ c #8DB4D6", +"L@ c #8DB5D7", +"M@ c #8EB6D7", +"N@ c #8EB7D8", +"O@ c #8FB7D7", +"P@ c #90B9D8", +"Q@ c #91BBDA", +"R@ c #92BCD9", +"S@ c #92BCDA", +"T@ c #C1C1C0", +"U@ c #C1C1C1", +"V@ c #C2C2C1", +"W@ c #C3C3C2", +"X@ c #C3C3C3", +"Y@ c #C3C3C4", +"Z@ c #CAC9C9", +"`@ c #B1B0B0", +" # c #B2B2B1", +".# c #8BB2D5", +"+# c #8BB2D6", +"@# c #8CB3D6", +"## c #8CB3D7", +"$# c #8DB5D6", +"%# c #8EB5D7", +"&# c #8FB8D7", +"*# c #92BBD9", +"=# c #92BBDA", +"-# c #BFC0C0", +";# c #C0C1C0", +"># c #C2C1C1", +",# c #C3C2C2", +"'# c #C4C4C3", +")# c #C5C6C5", +"!# c #C5C5C6", +"~# c #B0B0B0", +"{# c #B0B1B0", +"]# c #B1B1B2", +"^# c #B2B1B1", +"/# c #0E3459", +"(# c #0E355A", +"_# c #0F355A", +":# c #0F365A", +"<# c #8AB2D5", +"[# c #8CB3D5", +"}# c #8BB3D6", +"|# c #8FB6D7", +"1# c #8FB9D7", +"2# c #90B9D7", +"3# c #90BAD8", +"4# c #90BBD9", +"5# c #92BDDA", +"6# c #93BEDA", +"7# c #BEBEBE", +"8# c #BEBFBE", +"9# c #BFBFBE", +"0# c #BFBFC0", +"a# c #C0C0BF", +"b# c #C2C3C2", +"c# c #C4C3C3", +"d# c #AFB0B0", +"e# c #AFAFB0", +"f# c #B0AFB0", +"g# c #B0B1B1", +"h# c #B0B0B1", +"i# c #B1B0B1", +"j# c #7A9EC5", +"k# c #799FC5", +"l# c #7A9FC5", +"m# c #7AA0C6", +"n# c #0E345A", +"o# c #0F3559", +"p# c #8BB1D5", +"q# c #8CB4D5", +"r# c #8FB9D8", +"s# c #90BAD7", +"t# c #91BBD8", +"u# c #91BCD9", +"v# c #91BDD9", +"w# c #92BEDA", +"x# c #93BFDA", +"y# c #BDBDBD", +"z# c #BDBEBD", +"A# c #BEBEBD", +"B# c #BEBEBF", +"C# c #BEBFBF", +"D# c #BFBFBF", +"E# c #C0C0C0", +"F# c #C1C0C0", +"G# c #C0C1C1", +"H# c #C3C2C3", +"I# c #C4C3C4", +"J# c #AFAEAF", +"K# c #AFAFAF", +"L# c #B0AFAF", +"M# c #789DC5", +"N# c #789EC5", +"O# c #799EC5", +"P# c #7AA1C5", +"Q# c #7BA1C5", +"R# c #0F3659", +"S# c #10375A", +"T# c #8DB6D6", +"U# c #8EB8D7", +"V# c #92BDD9", +"W# c #143F5B", +"X# c #174A6A", +"Y# c #84B0CB", +"Z# c #85B0CC", +"`# c #BCBCBC", +" $ c #BDBCBC", +".$ c #C0C0C1", +"+$ c #C1C2C1", +"@$ c #C2C3C3", +"#$ c #AEAEAE", +"$$ c #AEAFAF", +"%$ c #AFAFAE", +"&$ c #779BC4", +"*$ c #779CC4", +"=$ c #789CC4", +"-$ c #789DC4", +";$ c #789EC4", +">$ c #799FC4", +",$ c #7AA0C5", +"'$ c #10385A", +")$ c #8DB7D7", +"!$ c #8EB7D6", +"~$ c #90BBD8", +"{$ c #133E5B", +"]$ c #83AEC9", +"^$ c #84AFCA", +"/$ c #85B1CB", +"($ c #85B2CB", +"_$ c #BDBCBD", +":$ c #BEBDBD", +"<$ c #C0BFBF", +"[$ c #C1C1C2", +"}$ c #C9CACA", +"|$ c #AEAEAD", +"1$ c #AEAFAE", +"2$ c #AEAEAF", +"3$ c #7599C3", +"4$ c #7699C3", +"5$ c #7699C2", +"6$ c #769AC3", +"7$ c #779BC3", +"8$ c #789BC4", +"9$ c #799EC4", +"0$ c #79A0C4", +"a$ c #8CB5D5", +"b$ c #8CB5D6", +"c$ c #8DB7D6", +"d$ c #426D8A", +"e$ c #16486A", +"f$ c #7AA5C1", +"g$ c #83ADC9", +"h$ c #84B1CB", +"i$ c #85B1CC", +"j$ c #BCBDBD", +"k$ c #BDBDBC", +"l$ c #BFBEBF", +"m$ c #BFC0BF", +"n$ c #ADADAC", +"o$ c #AEADAD", +"p$ c #ADAEAD", +"q$ c #ADAEAE", +"r$ c #7496C2", +"s$ c #7597C2", +"t$ c #7598C2", +"u$ c #769BC2", +"v$ c #779BC2", +"w$ c #779CC3", +"x$ c #789DC3", +"y$ c #7AA0C4", +"z$ c #7AA1C4", +"A$ c #7AA2C5", +"B$ c #7BA2C5", +"C$ c #10395A", +"D$ c #8EB6D6", +"E$ c #8EB8D6", +"F$ c #133D5A", +"G$ c #81ABC8", +"H$ c #82ACC9", +"I$ c #82ADC9", +"J$ c #82AEC9", +"K$ c #83AFC9", +"L$ c #83B0CA", +"M$ c #BDBEBE", +"N$ c #C1C0C1", +"O$ c #C7C7C8", +"P$ c #ADACAC", +"Q$ c #AFAEAE", +"R$ c #7395C1", +"S$ c #7395C2", +"T$ c #7496C1", +"U$ c #7599C2", +"V$ c #769BC3", +"W$ c #779DC3", +"X$ c #789EC3", +"Y$ c #7AA2C4", +"Z$ c #7BA3C4", +"`$ c #7BA3C5", +" % c #11395A", +".% c #113A5A", +"+% c #123C5A", +"@% c #164A6E", +"#% c #4C7694", +"$% c #80AAC7", +"%% c #80ABC8", +"&% c #81ACC9", +"*% c #81ACC8", +"=% c #81ADC9", +"-% c #83AFCA", +";% c #84B0CA", +">% c #84B1CA", +",% c #86B3CB", +"'% c #87B4CC", +")% c #BFBEBE", +"!% c #ACACAC", +"~% c #ABACAC", +"{% c #ACACAD", +"]% c #7294C0", +"^% c #7194C0", +"/% c #7294C1", +"(% c #7394C1", +"_% c #7396C1", +":% c #7497C2", +"<% c #7498C2", +"[% c #799FC3", +"}% c #7CA3C5", +"|% c #123B5A", +"1% c #88B1D0", +"2% c #7EA8C6", +"3% c #7FA9C6", +"4% c #7FAAC7", +"5% c #81ADC8", +"6% c #85B1CA", +"7% c #86B2CB", +"8% c #86B4CB", +"9% c #87B5CB", +"0% c #BCBCBD", +"a% c #C0BFC0", +"b% c #ABABAB", +"c% c #ACABAC", +"d% c #ABABAC", +"e% c #ACADAC", +"f% c #ACADAD", +"g% c #7193C0", +"h% c #7293C0", +"i% c #7193C1", +"j% c #7293C1", +"k% c #7498C1", +"l% c #789FC4", +"m% c #79A1C3", +"n% c #79A1C4", +"o% c #7BA2C4", +"p% c #7CA4C5", +"q% c #7CA4C4", +"r% c #16496F", +"s% c #7DA7C5", +"t% c #7FAAC6", +"u% c #80ABC7", +"v% c #80ACC7", +"w% c #82AFC9", +"x% c #84B1C9", +"y% c #85B2CA", +"z% c #87B5CC", +"A% c #88B6CC", +"B% c #BDBDBE", +"C% c #C1C2C2", +"D% c #AAABAB", +"E% c #AAAAAA", +"F% c #ABAAAB", +"G% c #ABACAB", +"H% c #ACACAB", +"I% c #7192C0", +"J% c #7292C0", +"K% c #7598C1", +"L% c #7599C1", +"M% c #759AC1", +"N% c #769AC2", +"O% c #779CC2", +"P% c #779DC2", +"Q% c #789DC2", +"R% c #78A0C3", +"S% c #7CA3C4", +"T% c #7AA2C2", +"U% c #7CA5C4", +"V% c #7DA5C4", +"W% c #7DA6C5", +"X% c #7EA8C5", +"Y% c #82ADC8", +"Z% c #83B0C9", +"`% c #85B3CA", +" & c #86B5CB", +".& c #87B6CC", +"+& c #BCBDBC", +"@& c #C2C1C2", +"#& c #C2C2C3", +"$& c #AAA9AA", +"%& c #AAABAA", +"&& c #ABAAAA", +"*& c #7091BF", +"=& c #7092BF", +"-& c #7191C0", +";& c #7192BF", +">& c #7092C0", +",& c #7295C0", +"'& c #7497C1", +")& c #759AC2", +"!& c #769CC2", +"~& c #789FC3", +"{& c #79A0C3", +"]& c #79A2C4", +"^& c #7AA4C3", +"/& c #7BA4C4", +"(& c #7DA8C5", +"_& c #7EA9C6", +":& c #80AAC6", +"<& c #81ADC7", +"[& c #82ADC7", +"}& c #82AEC8", +"|& c #88B7CC", +"1& c #A9A9A9", +"2& c #A9AAAA", +"3& c #A9A9AA", +"4& c #A9AAA9", +"5& c #7090BE", +"6& c #7091BE", +"7& c #7092BE", +"8& c #7191BF", +"9& c #7397C1", +"0& c #7499C1", +"a& c #7AA1C3", +"b& c #7AA2C3", +"c& c #79A2C2", +"d& c #7AA3C3", +"e& c #7BA5C4", +"f& c #80ABC6", +"g& c #81ACC7", +"h& c #81AEC8", +"i& c #82AFC8", +"j& c #83B1CA", +"k& c #85B3CB", +"l& c #85B4CA", +"m& c #87B6CB", +"n& c #88B8CC", +"o& c #A8A8A8", +"p& c #6F90BE", +"q& c #6F91BE", +"r& c #7192BE", +"s& c #7193BF", +"t& c #769BC1", +"u& c #779EC3", +"v& c #789FC2", +"w& c #79A1C2", +"x& c #7AA3C2", +"y& c #7BA4C3", +"z& c #7CA6C5", +"A& c #7DA7C4", +"B& c #7EA7C5", +"C& c #7EA9C5", +"D& c #7EAAC6", +"E& c #7FACC6", +"F& c #81AEC7", +"G& c #82AFC7", +"H& c #83B1C9", +"I& c #84B2C9", +"J& c #84B3C9", +"K& c #86B4CA", +"L& c #89B7CC", +"M& c #89B8CD", +"N& c #A8A8A7", +"O& c #6F90BD", +"P& c #6E90BD", +"Q& c #6E90BE", +"R& c #7293BF", +"S& c #7396C0", +"T& c #7397C0", +"U& c #7498C0", +"V& c #759BC1", +"W& c #769CC1", +"X& c #779EC2", +"Y& c #789EC2", +"Z& c #78A0C1", +"`& c #78A1C2", +" * c #7BA6C3", +".* c #7BA6C4", +"+* c #7CA6C4", +"@* c #7FABC6", +"#* c #82AEC7", +"$* c #83AFC8", +"%* c #83B0C8", +"&* c #88B7CB", +"** c #89B9CC", +"=* c #89BACD", +"-* c #6E8FBD", +";* c #6F91BD", +">* c #7093BF", +",* c #7194BF", +"'* c #7294BF", +")* c #7395C0", +"!* c #7398C0", +"~* c #779FC1", +"{* c #77A0C1", +"]* c #78A1C1", +"^* c #7AA4C2", +"/* c #7CA7C4", +"(* c #7DA8C4", +"_* c #7DA9C5", +":* c #7FAAC5", +"<* c #80ACC6", +"[* c #80ADC7", +"}* c #84B1C8", +"|* c #88B8CB", +"1* c #89BACC", +"2* c #BEBDBE", +"3* c #12285A", +"4* c #768CBE", +"5* c #768DBE", +"6* c #6E91BD", +"7* c #7193BE", +"8* c #7295BF", +"9* c #7396BF", +"0* c #7398BF", +"a* c #7499C0", +"b* c #749AC1", +"c* c #779DC1", +"d* c #789FC1", +"e* c #779EC0", +"f* c #769EC0", +"g* c #769FC0", +"h* c #77A0C0", +"i* c #79A2C1", +"j* c #79A3C2", +"k* c #7BA4C2", +"l* c #7CA7C3", +"m* c #7EAAC5", +"n* c #80ADC6", +"o* c #82B0C7", +"p* c #85B2C9", +"q* c #85B3C9", +"r* c #85B4C9", +"s* c #86B5CA", +"t* c #87B6CA", +"u* c #88B9CC", +"v* c #8AB9CC", +"w* c #8ABACC", +"x* c #8BBBCC", +"y* c #758BBE", +"z* c #758CBE", +"A* c #768CBF", +"B* c #6E91BE", +"C* c #7093BE", +"D* c #7397BF", +"E* c #7497BF", +"F* c #749AC0", +"G* c #759AC0", +"H* c #769BC0", +"I* c #769CC0", +"J* c #779EC1", +"K* c #769DC0", +"L* c #759DBF", +"M* c #769DBE", +"N* c #77A1C0", +"O* c #7AA5C2", +"P* c #7DA7C3", +"Q* c #7EA9C4", +"R* c #7EAAC4", +"S* c #7FABC5", +"T* c #81ADC6", +"U* c #81AFC7", +"V* c #82B0C8", +"W* c #83B1C8", +"X* c #84B2C8", +"Y* c #86B6CB", +"Z* c #87B7CA", +"`* c #87B7CB", +" = c #84AFCB", +".= c #143F5A", +"+= c #12275A", +"@= c #758BBD", +"#= c #768BBE", +"$= c #6E8FBC", +"%= c #6E8EBD", +"&= c #7296BF", +"*= c #7297BF", +"== c #779CC1", +"-= c #749BBE", +";= c #749CBF", +">= c #759EBE", +",= c #769FBF", +"'= c #76A0C0", +")= c #779FC0", +"!= c #7BA5C3", +"~= c #7CA6C3", +"{= c #7CA8C4", +"]= c #7DA9C3", +"^= c #7FAAC4", +"/= c #81AFC6", +"(= c #82B1C7", +"_= c #85B5C9", +":= c #86B6CA", +"<= c #82AECA", +"[= c #758ABD", +"}= c #748ABD", +"|= c #758CBD", +"1= c #6D8FBC", +"2= c #759CC1", +"3= c #769DC1", +"4= c #759BBF", +"5= c #739ABE", +"6= c #759DBE", +"7= c #769EBE", +"8= c #769EBF", +"9= c #779FBF", +"0= c #78A1C0", +"a= c #78A2C1", +"b= c #79A2C0", +"c= c #7AA3C1", +"d= c #7CA8C3", +"e= c #7DA9C4", +"f= c #82B0C6", +"g= c #83B2C8", +"h= c #84B3C8", +"i= c #143E5B", +"j= c #11265A", +"k= c #7489BC", +"l= c #748ABC", +"m= c #758ABC", +"n= c #6D8EBC", +"o= c #6E8EBC", +"p= c #6D8FBD", +"q= c #6F8FBD", +"r= c #7092BD", +"s= c #7294BE", +"t= c #7194BE", +"u= c #7295BE", +"v= c #7196BF", +"w= c #759BC0", +"x= c #749ABE", +"y= c #7399BC", +"z= c #729ABD", +"A= c #739ABD", +"B= c #749CBD", +"C= c #769DBF", +"D= c #779EBF", +"E= c #78A2C0", +"F= c #79A3C1", +"G= c #7AA4C1", +"H= c #7BA6C2", +"I= c #7CA9C4", +"J= c #7EABC4", +"K= c #7FACC4", +"L= c #80ADC5", +"M= c #80AEC6", +"N= c #81AEC6", +"O= c #83B1C7", +"P= c #7EA9C7", +"Q= c #7FAAC8", +"R= c #80AAC8", +"S= c #7388BC", +"T= c #7488BC", +"U= c #7389BC", +"V= c #7489BD", +"W= c #6E8EBB", +"X= c #6D8EBD", +"Y= c #7195BE", +"Z= c #7499BF", +"`= c #749ABF", +" - c #749BC0", +".- c #7198BB", +"+- c #7298BC", +"@- c #7299BC", +"#- c #729ABC", +"$- c #739BBD", +"%- c #749BBD", +"&- c #749DBE", +"*- c #76A0BF", +"=- c #77A2C0", +"-- c #78A3C1", +";- c #79A4C1", +">- c #7CA8C2", +",- c #7DAAC4", +"'- c #7FACC5", +")- c #7DA7C6", +"!- c #7FA9C7", +"~- c #133E5A", +"{- c #7387BC", +"]- c #6F92BD", +"^- c #7094BE", +"/- c #7296BE", +"(- c #7399BD", +"_- c #0E3559", +":- c #7096BB", +"<- c #7197BB", +"[- c #7199BB", +"}- c #739BBC", +"|- c #749CBE", +"1- c #749EBE", +"2- c #769FBE", +"3- c #77A0BF", +"4- c #77A1BF", +"5- c #78A3C0", +"6- c #79A4C0", +"7- c #7AA6C2", +"8- c #7BA7C2", +"9- c #7DA8C3", +"0- c #7EACC5", +"a- c #7CA5C5", +"b- c #10255A", +"c- c #7286BB", +"d- c #7287BC", +"e- c #6D8EBB", +"f- c #7093BD", +"g- c #7094BD", +"h- c #7195BF", +"i- c #7399BF", +"j- c #739ABF", +"k- c #7398BE", +"l- c #6F95BA", +"m- c #7096BA", +"n- c #7097BB", +"o- c #739ABC", +"p- c #739CBD", +"q- c #759EBF", +"r- c #78A2BF", +"s- c #7CA7C2", +"t- c #10245A", +"u- c #7185BB", +"v- c #7286BC", +"w- c #7387BB", +"x- c #7487BC", +"y- c #6E90BC", +"z- c #6F93BE", +"A- c #7196BE", +"B- c #7297BE", +"C- c #7298BF", +"D- c #7297BD", +"E- c #6E94BA", +"F- c #6F95B9", +"G- c #6F96BA", +"H- c #7097BA", +"I- c #729ABB", +"J- c #749DBD", +"K- c #77A0BE", +"L- c #78A4C0", +"M- c #7AA5C0", +"N- c #7CA7C5", +"O- c #7185BA", +"P- c #7286BA", +"Q- c #7285BB", +"R- c #7386BB", +"S- c #7288BC", +"T- c #7589BD", +"U- c #6E91BC", +"V- c #6F91BC", +"W- c #6E92BC", +"X- c #6F93BD", +"Y- c #7194BD", +"Z- c #7195BD", +"`- c #7298BE", +" ; c #7297BC", +".; c #6D93B8", +"+; c #6E94B8", +"@; c #6E95B9", +"#; c #7096B9", +"$; c #739CBC", +"%; c #76A0BE", +"&; c #78A1BF", +"*; c #79A1C1", +"=; c #79A4C2", +"-; c #7BA3C3", +";; c #7084BA", +">; c #7184BB", +",; c #7085BB", +"'; c #7186BB", +"); c #7287BB", +"!; c #7388BB", +"~; c #6C8EBB", +"{; c #6D8FBB", +"]; c #6F92BC", +"^; c #7095BD", +"/; c #7196BD", +"(; c #7195BC", +"_; c #0E3359", +":; c #6C92B7", +"<; c #6D94B8", +"[; c #6F96B9", +"}; c #6F97BA", +"|; c #7299BB", +"1; c #729BBB", +"2; c #749DBC", +"3; c #759EBD", +"4; c #759FBE", +"5; c #10235A", +"6; c #7083BA", +"7; c #7084BB", +"8; c #7085BA", +"9; c #7093BC", +"0; c #7095BC", +"a; c #6C91B7", +"b; c #6E95B8", +"c; c #7198BA", +"d; c #739BBB", +"e; c #78A2C2", +"f; c #0F225A", +"g; c #6F82B9", +"h; c #7083B9", +"i; c #6F83B9", +"j; c #6F83BA", +"k; c #7184BA", +"l; c #6C8DBB", +"m; c #6C8FBB", +"n; c #6D90BB", +"o; c #6E91BB", +"p; c #6F93BC", +"q; c #7094BC", +"r; c #6F94BB", +"s; c #0D3259", +"t; c #6A8FB6", +"u; c #6B91B7", +"v; c #6E93B8", +"w; c #7098BA", +"x; c #7099BA", +"y; c #7199BA", +"z; c #729BBC", +"A; c #6E81B9", +"B; c #6E82B9", +"C; c #6E90BB", +"D; c #6E93BA", +"E; c #698EB6", +"F; c #6A8EB6", +"G; c #6A90B6", +"H; c #6B90B6", +"I; c #6B92B7", +"J; c #6D93B7", +"K; c #6E96B8", +"L; c #6F97B9", +"M; c #7098BB", +"N; c #78A0C0", +"O; c #6E80B8", +"P; c #6E81B8", +"Q; c #6F81B8", +"R; c #6F81B9", +"S; c #6F82B8", +"T; c #6F92BB", +"U; c #6E92BA", +"V; c #0D3159", +"W; c #688DB4", +"X; c #698EB5", +"Y; c #698FB5", +"Z; c #6A90B7", +"`; c #6C93B7", +" > c #518FC8", +".> c #6F96B8", +"+> c #123A5A", +"@> c #0F215A", +"#> c #6D80B8", +"$> c #6D81B8", +"%> c #6C8FBA", +"&> c #6E90BA", +"*> c #6E92BB", +"=> c #6E93BC", +"-> c #0C3159", +";> c #678CB4", +">> c #678DB4", +",> c #688EB4", +"'> c #698EB4", +")> c #6A90B5", +"!> c #6B91B6", +"~> c #6D94B7", +"{> c #6D95B8", +"]> c #6E96B9", +"^> c #0E2159", +"/> c #6C7FB8", +"(> c #6D7FB7", +"_> c #6D7FB8", +":> c #507FC9", +"<> c #6D8FBA", +"[> c #6D91BB", +"}> c #6D91B9", +"|> c #0C3059", +"1> c #678AB3", +"2> c #678BB3", +"3> c #678DB3", +"4> c #6B91B5", +"5> c #6B92B5", +"6> c #6C92B6", +"7> c #6B92B6", +"8> c #6C93B6", +"9> c #0E2059", +"0> c #6C7EB7", +"a> c #6D7EB7", +"b> c #6E80B7", +"c> c #6E82B8", +"d> c #7084B9", +"e> c #6F84BA", +"f> c #6C90B9", +"g> c #668AB2", +"h> c #698FB4", +"i> c #6A90B4", +"j> c #719ABB", +"k> c #749BBC", +"l> c #6B7EB7", +"m> c #6B7DB6", +"n> c #6C7DB7", +"o> c #6C7FB7", +"p> c #6D80B7", +"q> c #6F84B9", +"r> c #7186BA", +"s> c #6C8EB9", +"t> c #6589B1", +"u> c #678CB2", +"v> c #678DB2", +"w> c #688EB3", +"x> c #6A8FB5", +"y> c #6A91B5", +"z> c #6C92B5", +"A> c #6C94B7", +"B> c #6D95B7", +"C> c #759DBD", +"D> c #0E1F5A", +"E> c #6A7CB6", +"F> c #6A7DB6", +"G> c #6C7EB6", +"H> c #7082B9", +"I> c #7285BA", +"J> c #7085B9", +"K> c #0E335A", +"L> c #658BB1", +"M> c #668BB1", +"N> c #668CB1", +"O> c #678CB1", +"P> c #688DB3", +"Q> c #688FB3", +"R> c #698FB3", +"S> c #6990B4", +"T> c #7097B9", +"U> c #7197BA", +"V> c #739CBE", +"W> c #0E1F59", +"X> c #6A7BB6", +"Y> c #6B7CB6", +"Z> c #6C7DB6", +"`> c #6B7DB7", +" , c #6F82B6", +"., c #648AB0", +"+, c #658AB1", +"@, c #658BB0", +"#, c #668CB2", +"$, c #688FB4", +"%, c #6B90B5", +"&, c #0D1E59", +"*, c #697BB5", +"=, c #6A7CB7", +"-, c #6B7EB6", +";, c #6C7EB8", +">, c #6E82B7", +",, c #6389B0", +"', c #6489B0", +"), c #658AB0", +"!, c #668AB1", +"~, c #688DB2", +"{, c #6990B5", +"], c #697AB5", +"^, c #697BB6", +"/, c #6A7BB5", +"(, c #6D7EB8", +"_, c #6D81B6", +":, c #6388AE", +"<, c #6388AF", +"[, c #6389AF", +"}, c #6489AF", +"|, c #668BB0", +"1, c #6979B4", +"2, c #6879B5", +"3, c #6879B4", +"4, c #6979B5", +"5, c #6A7AB5", +"6, c #6B7CB7", +"7, c #6D80B5", +"8, c #6287AE", +"9, c #6288AE", +"0, c #0D1D59", +"a, c #6878B4", +"b, c #6778B4", +"c, c #6C7EB5", +"d, c #6186AC", +"e, c #6186AD", +"f, c #6286AE", +"g, c #668DB1", +"h, c #678EB3", +"i, c #0D1C59", +"j, c #6777B3", +"k, c #6777B4", +"l, c #697AB4", +"m, c #6A7CB5", +"n, c #6E81B7", +"o, c #6084AC", +"p, c #6185AC", +"q, c #6086AD", +"r, c #6187AE", +"s, c #6387AE", +"t, c #0C1C59", +"u, c #6676B3", +"v, c #6776B3", +"w, c #6877B4", +"x, c #687AB5", +"y, c #697CB6", +"z, c #6A7DB7", +"A, c #6B7DB5", +"B, c #5F83AC", +"C, c #6083AC", +"D, c #6185AD", +"E, c #6288AD", +"F, c #668BB2", +"G, c #6A91B4", +"H, c #6675B3", +"I, c #6575B3", +"J, c #6677B3", +"K, c #6677B4", +"L, c #6A7DB4", +"M, c #5E82AB", +"N, c #5F83AB", +"O, c #5F84AC", +"P, c #6085AD", +"Q, c #6085AC", +"R, c #10365A", +"S, c #0C1B59", +"T, c #6575B2", +"U, c #6675B2", +"V, c #6676B2", +"W, c #6676B4", +"X, c #6776B4", +"Y, c #687AB4", +"Z, c #5D81AA", +"`, c #5D81AB", +" ' c #5D82AA", +".' c #5E83AB", +"+' c #6083AB", +"@' c #6286AD", +"#' c #648AB1", +"$' c #6473B3", +"%' c #6474B2", +"&' c #697CB3", +"*' c #5D80A9", +"=' c #5E82AA", +"-' c #5F84AB", +";' c #6287AD", +">' c #6389AE", +",' c #0B1A59", +"'' c #6473B2", +")' c #6574B2", +"!' c #6574B3", +"~' c #6778B3", +"{' c #6779B4", +"]' c #687BB2", +"^' c #5B7FA9", +"/' c #5B80A9", +"(' c #5C80A9", +"_' c #5D81A9", +":' c #5E81AA", +"<' c #6184AC", +"[' c #638AB0", +"}' c #678DB1", +"|' c #0B1A58", +"1' c #6372B1", +"2' c #6472B2", +"3' c #6472B1", +"4' c #6473B1", +"5' c #6573B2", +"6' c #6576B3", +"7' c #6878B3", +"8' c #687AB3", +"9' c #5B7EA7", +"0' c #5B7FA8", +"a' c #5B80A8", +"b' c #5C81AA", +"c' c #5F82AB", +"d' c #668DB2", +"e' c #11268E", +"f' c #11268D", +"g' c #0B1958", +"h' c #6373B2", +"i' c #6877B3", +"j' c #6879B2", +"k' c #0C2F59", +"l' c #5A7DA7", +"m' c #5A7EA7", +"n' c #5B7FA7", +"o' c #5B7EA8", +"p' c #5C7FA8", +"q' c #5F83AA", +"r' c #6084AB", +"s' c #6589B0", +"t' c #0C196C", +"u' c #152064", +"v' c #11258D", +"w' c #6371B1", +"x' c #6271B1", +"y' c #6372B2", +"z' c #6778B1", +"A' c #0C2E59", +"B' c #597CA6", +"C' c #5A7CA6", +"D' c #597DA6", +"E' c #5D80AA", +"F' c #5E83AA", +"G' c #0E355B", +"H' c #0C1A6F", +"I' c #131E62", +"J' c #414C90", +"K' c #10258E", +"L' c #6271B0", +"M' c #6272B1", +"N' c #6373B1", +"O' c #6677B1", +"P' c #0B2E59", +"Q' c #577BA5", +"R' c #587BA6", +"S' c #587CA6", +"T' c #5A7EA8", +"U' c #5D82AB", +"V' c #6289AE", +"W' c #1A5FA3", +"X' c #0D1C75", +"Y' c #131E61", +"Z' c #404B8E", +"`' c #475296", +" ) c #10258D", +".) c #6270B0", +"+) c #6272B0", +"@) c #6576B2", +"#) c #6576B1", +"$) c #577AA4", +"%) c #577BA4", +"&) c #587BA5", +"*) c #5C81A9", +"=) c #648AAF", +"-) c #0E345B", +";) c #0D1D79", +">) c #101B5F", +",) c #3C478B", +"') c #11258E", +")) c #6371B0", +"!) c #6575B1", +"~) c #0B2E58", +"{) c #5679A3", +"]) c #5779A4", +"^) c #567AA4", +"/) c #577AA5", +"() c #577BA6", +"_) c #597BA6", +":) c #175797", +"<) c #0E1F82", +"[) c #101B5E", +"}) c #384487", +"|) c #0B1858", +"1) c #6170B0", +"2) c #6575B0", +"3) c #5578A2", +"4) c #5678A3", +"5) c #5679A4", +"6) c #5A7DA6", +"7) c #102394", +"8) c #111C60", +"9) c #141F63", +"0) c #293578", +"a) c #0A1859", +"b) c #616FAF", +"c) c #616FB0", +"d) c #6270AF", +"e) c #6372B0", +"f) c #6474AF", +"g) c #0B2D58", +"h) c #5576A2", +"i) c #5577A2", +"j) c #5577A3", +"k) c #5578A4", +"l) c #5578A3", +"m) c #587CA5", +"n) c #597DA7", +"o) c #5D82A9", +"p) c #6187AD", +"q) c #154F8D", +"r) c #1227A5", +"s) c #0D185C", +"t) c #1B266A", +"u) c #313C80", +"v) c #445093", +"w) c #10248D", +"x) c #6170AF", +"y) c #6474B1", +"z) c #6373AF", +"A) c #5375A1", +"B) c #5476A2", +"C) c #587AA4", +"D) c #5C7FA9", +"E) c #6084AD", +"F) c #102393", +"G) c #0C196A", +"H) c #0E1A5D", +"I) c #242F73", +"J) c #3A4589", +"K) c #626FAF", +"L) c #626FB0", +"M) c #6475B2", +"N) c #6272AE", +"O) c #0A2C59", +"P) c #5275A1", +"Q) c #5376A2", +"R) c #5477A2", +"S) c #144A84", +"T) c #0E1E7F", +"U) c #121D61", +"V) c #2B3679", +"W) c #0A1858", +"X) c #6370B0", +"Y) c #6271AF", +"Z) c #0A2C58", +"`) c #5173A0", +" ! c #5274A1", +".! c #5274A0", +"+! c #0D1B74", +"@! c #10248E", +"#! c #6171AE", +"$! c #51729F", +"%! c #51739F", +"&! c #5273A0", +"*! c #5476A1", +"=! c #5576A3", +"-! c #114379", +";! c #102291", +">! c #0C196B", +",! c #1F2A6E", +"'! c #364185", +")! c #465195", +"!! c #10238D", +"~! c #616EAF", +"{! c #616FAD", +"]! c #0A2B59", +"^! c #50729E", +"/! c #50729F", +"(! c #5375A0", +"_! c #134C8D", +":! c #0E1E81", +"~ c #323D81", +",~ c #0A2D5D", +"'~ c #1354AE", +")~ c #10228F", +"!~ c #212C70", +"~~ c #374286", +"{~ c #0F228B", +"]~ c #1251A7", +"^~ c #283377", +"/~ c #3E4A8D", +"(~ c #0F4085", +"_~ c #0B2F62", +":~ c #1328A8", +"<~ c #0D1C77", +"[~ c #303B7F", +"}~ c #104289", +"|~ c #0B3063", +"1~ c #112497", +"2~ c #1C276A", +"3~ c #0C175B", +"4~ c #0F438A", +"5~ c #0B3266", +"6~ c #0F2088", +"7~ c #0C3367", +"8~ c #365885", +"9~ c #0B3369", +"0~ c #0E1D7C", +"a~ c #0B2F60", +"b~ c #0C366E", +" ", +" ", +" ", +" ", +" ", +" . . ", +" . . . . . . . ", +" . . . . + @ # $ % . . . . ", +" . . . . & * + @ = - % ; > , . . . . ", +" . . . . ' ) ! * + @ ~ { ] % ^ , / ( _ . . . ", +" . . . . : < [ } | * + 1 = $ 2 ; > , / 3 4 5 6 . . . . ", +" . . . . 7 8 9 0 a } | * + b c d 2 % > , / e f g h i j k . . . . ", +" . . . . l m n 9 o p q } | * r s t # u % v w x y z A B i C D E F G . . . ", +" . . . . H I J K L M 9 < q ' | N O P t # Q % R S T / U V W X Y D Z ` ...+.. . . . ", +" . . . . @.#.H I $.%.L M 9 < q } } N r + @ # Q &.R S *.=.f V W X -.;.k ` ...+.+.>.,.. . . . ", +" . . . . '.'.'.#.H ).!.~.n {.].< 0 ' } | ^.b @ # Q ] /.(., / f 4 5 B i D k _. ...:.<.[.,.}.|.1.. . . . ", +" . . . . '.'.'.'.'.2.H 3.J %.m 7 9 < p } ) 4.5.+ @ ~ Q Q 6.> , / 3 4 7.X 8.9.k 0. ...a.b.<.c.d.|.1.e.f.g.. . . ", +" . . . . . '.'.'.'.'.'.'.h.i.j.$.$.m 7 k.< l.a m.| * + @ c Q Q % > , / f f n.B i j k _.o.p.q.+.r.c.s.|.t.u.v.w.x.y.. . . . ", +" . . . . . '.'.'.'.'.'.'.'.z.A.B.I $.m 7 9 ].C.q D.| * + P t # Q 6.> (.T f E.F.6 i j G.0.H.I...+.<.c.d.J.K.L.M.N.x.y.O.P.Q.. . . . ", +" R.. . '.'.'.'.'.'.'.'.'.'.'.S.A.3.I $.m n k.T.U.q V.W.X.+ + t # Q % /.S Y./ z F.W i -.Z.k _.`.a.+.<.r.s. +|.L.M.N.g..+++O.@+#+$+%+. . . ", +" . . . . . '.'.'.'.'.'.'.'.'.&+i.H I $.K n 8 < < q } *+* ^.=+@ { Q ] -+, , ;+_ V W B 8.>+,+_.G ..+.'+c.d.d.t.)+!+M.x.x.~+{+Q.#+$+]+^+/+. . . . ", +" . . =.f . . . . . '.'.'.'.'.'.2.A.H (+$._+:+<+].< q } | N ^.s [+# Q }+6.> *./ f V 5 |+C D 0._. ...q.1+2+3+4+|.1.!+N.5+y.6+O.Q.#+$+7+8+/+/+/+/+. . . . ", +" . . / / y e . . . . . '.'.'.'.&+#.H I 9+0+m a+9 b+l.} m.X.5.+ @ = $ % > c+Y./ f U W X d+k k _. ...e+f+g+c.d.J.)+h+i+j+x.6+k+@+l+m+n+o+/+/+/+/+/+/+/+. . . . ", +" . . S , *.x T / / 3 . . . . . '.'.#.p+q+!.~.m 7 9 < 0 ' } r+* P @ # { s+% w , / e V 5 B -.j k _. .t+e+1+2+3+d.|.K.u.u+N.x..+O.v+w+x+y+z+/+/+/+/+/+/+/+/+/+/+. . . ", +" . . ; > > ^ , A+/ / e y ( . . . . . i.).B+$.C+7 9 : l.a } | * + D+t d Q % > , / f E+5 6 d+j k _.o.p.F+<.r.c.d.J.)+h+!+G+x.H+O.@+@+$+y+%+/+/+/+/+/+/+/+/+/+/+/+/+. . . . ", +" . . 2 % % /.-+> ^ S , Y.I+/ =.e . . . . . I $.C+L 9 J+K+a } & 4.+ P c d Q v L+S / f f g |+i j k ,+H.t+:.+.<.c.s.M+N+O+i+N.x..+~+@+#+P+]+%+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+. . . . ", +" . . # $ Q 2 s+6./.> > , , , , / / ;+y . . . . . 7 a+< U.Q+} | N R+P S+# u % > , / ( f V W i -.Z._.T+I...+.U+c.V+W+t.O+!+N.x.y.++X+@+#+$+%+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+. . . . ", +" . . @ t # Y+Y+Q Q &.% v v > c+c+S , , / =.( f . . . . . < q Z+) X.^.D+c Q Q % /., Y./ 4 V W i d+k Z T+`.q.+.<.r.d.}.`+)+!+N.x. @.+k+@+#+x+%+z+/+/+/+/+/+/+/+/+/+/+/+/+/+. . . . f f . ", +" . . + D+@ c # # $ Q Q Q }+% % /./.S S , *.T / ;+;+f . . . . . m.4.5.b @ # Q % R > , e f V W i 8.j k _.G ..+.'+<.c.4+`+)+h+v.w.y..@O.@+#+x+n++@/+/+/+/+/+/+/+/+/+/+/+. . . . f f f f f . ", +" . . . ^.+ + P b @ c t # - - u u &.% /.R c+c+S , A+/ / y f . . . . . 5.+ @ # Q ] /.L+A+=.f V W |+@@9.k _. .t++.b.g+c.W+|.1.!+u+j+#@~+O.@+l+#+$@8+/+/+/+/+/+/+/+/+/+. . . . f f f f f f f f . ", +" . . | N * * + + =+=+D+@ S+# %@%@- Q 2 % 6.v /.> , , , / T y f f . . . . . # Q % % > , / f E+V B -.9.k _.o.p.F+f+r.c.d.|.&@O+u+N.x..+++@+*@#+]+%+/+/+/+/+/+/+/+. . . =@f f f f f f f f f f -@. ", +" . . . W.D.| | 4.* 5.r + P b @ S+# # Y+- Q Q }+% ; -+> w w S , I+/ / 3 f . . . . . ; > , A+f V g ;@i j k _.>@`...+.r.c.d.|.&@,@'@G+x..+O.)@@+x+$+z+/+/+/+/+/+. . . T / ( y f f f f f f f f f f . . ", +" . . . a } V.} W.| r+4.* ^.* + D+@ @ @ # # - Q Q ] 2 ; /.-+> w (., A+Y./ f f . . . . . , / / U V X @@j k Z .t+a.+.<.c.d.|.K.O+!+G+x.y.!@P.Q.~@$+8+/+/+/+. . . ; > w ^ , T / 3 f f f f f f f f f . . ", +" . {@. . . . Z+} ) D.m.| r+* * 5.r + D+@ S+c # # - Q Q s+% % -+> c+(., , / / / / f . . . . . f g B i j k _._.`.q.+.<.c.]@ +1.O+!+N.x..+!@)@^@#+$+%+%+. . . # - Q % % % > L+w Y./ / / f f f f f f f . . ", +" . /@(@_@:@. . . . [ } } W.| N ! * * R++ + @ @ c c # # # Q u 2 % % ; > L+, , *./ / =.;+f . . . . . i j 9.E >@G ..+.'+c.<@4+&@)+!+N.G+.+O.O.l+#+@+. . . O s @ c c $ Q Q 2 % % > c+, I+x ;+f f f f f f . ", +" . [@/@}@/@/@/@. . . . q } } W.V.! ! N * + O + b b [+t c # $ Q u 2 ] % v R > > ^ *.A+I+/ e f f . . . . 9.E _. ...+.'+[.V+}.|.)+!+v.x..+6+{+.+. . . *+X.r+* r + s @ S+c { - Q s+% > > (., *./ f f f f f . ", +" . |@|@|@1@}@2@/@}@/@. . . . Q+[ } W.| N & * * R++ D+=+@ S+~ # # %@Q Q ] % R -+> ^ , w , , / =.;+f . . . . . ...3@<.4@3+}.|.)+!+u+w.x.)+. . . 0 q } [ V.| & 4.+ R++ b @ # # Q Q &.% 6.> > , A+x ( f f . ", +" . 5@5@6@|@7@|@|@8@}@}@2@/@. . . . ' } ) | | X.| * * ^.+ P 1 @ @ ~ # d Y+Q Q % % v > v w , , *.T / =.f f . . . . . 9@2+,.d.|.t.!+!+b.. . . <+9 o b+p 0 q Z+} ) | N * O + s @ t = d - Q % % -+> ^ , *./ . ", +" . 0@5@a@a@b@c@6@7@|@|@[@8@|@8@. . . . . } ) W.W.N & r+* * r + s 1 @ # # # { - Q u % % ; ; > c+, , , I+/ ( f f . . . . . d.M+t.j . . . $.0+m :+7 M 9 9 < U.q Z+} ) *+& 4.* O + @ @ c # { - }+% % R ^ , . ", +" . d@e@f@f@g@5@5@h@b@5@h@|@b@|@|@|@|@. . . . Z+} } | | & X.r+5.i@+ s D+@ [+t # # Q Q 2 &.% % /.R > c+, , T T / y f . . . . . . . . H 3.I !.$.%.~.C+7 {.a+T.: C.< q q } D.| r+* ^.+ =+1 @ c # - Q s+% v . ", +" . j@k@d@d@d@d@g@f@e@d@l@5@5@a@b@6@c@6@|@|@. . . . [ } } m.| | r+* * + + =+D+@ ~ ~ # m@n@n@Q &.% % v > c+(., , Y./ / =.f . . . '.h.#.#.o@H I I J $.K m C+7 M M ].< U.p q ' D.m.| ! * O + @ @ c @ d d 2 . ", +" . p@q@q@r@r@r@j@s@d@d@f@e@5@l@0@5@5@b@b@c@7@7@7@. . . . } V.V.) | & * * * + s t@u@v@v@w@m@n@x@y@u % v % > > (., , *.x / Y.. . z@A@B@'.2.#.#.p+j.I !.$.C@_+:+C+<+8 9 o < U.0 D@} W.m.| r+* O + s @ t # . ", +" . E@E@F@F@G@q@q@H@H@I@d@H@d@d@d@d@5@f@5@5@5@5@5@c@h@. . . . [ Z+) ) m.| 4.J@K@L@M@N@O@v@w@m@P@x@Q@R@S@] % 6.6.> > w , , . . T@U@V@W@X@Y@X@h.2.#.#.H B.I !.$.$._+Z@7 7 9 T.o < l.q q } } | | X.^.r D+=+. ", +" . `@E@E@E@E@F@G@ #p@q@q@q@q@d@k@d@H@d@d@g@f@g@d@5@0@5@5@6@. . . . Z+} .#+#@###$#L@%#M@O@O@&#P@P@n@n@*#=#S@&.}+% 6.-+> > . . -#T@;#U@>#z@,#X@Y@'#@.#.)#!#H I I I $.K m 7 7 7 9 9 T.< q q } } D.r+4.* 5.. ", +" . ~#~#~#{#~#E@E@E@]#G@^#^# #p@q@q@q@j@k@j@k@d@d@d@d@0@0@e@l@0@5@/#(#_#:#<#.#[#}#K@K@L@M@|#O@#2#3#4#4#x@R@5#6#% }+v . . 7#8#9#0#a#U@T@U@U@,#b#X@c#'.#.#.)#H 3.I l $.$.m m 7 7 M 9 9 < 0 q D@} *+| | . ", +" . d#e#f#~#~#~#g#h#{#{#E@i#E@E@q@F@p@]#q@q@q@q@q@j@d@k@d@d@d@j#k#l#m#n#o#:#:#p#+#}#q#$#$#$#M@M@u@v@1#r#s#3#t#u#v#5#w#x#. . y#z#A#B#C#D#a#E#F#G#V@z@H#X@I#'.@.#.#.H H B.I $.$.0+K :+7 M M : : < q q ' } . ", +" . J#K#K#K#K#L#~#~#~#~#~#~#{#{#i#E@E@^#]#G@]#q@q@^#q@I@H@M#M#N#O#k#k#l#P#Q#_#R#:#S#[#[#J@$#$#T#T#u@U#1#1#P@3#t#t#u#V#W#X#Y#Z#`# $y#z#7#7#D#D#-#.$U@U@+$z@@$H#'#'.2.2.#.p+j.I I l $.%.m m 7 7 9 ].< C.q . ", +" . #$J#K#$$$$%$K#K#L#K#L#f#~#f#~#~#`@`@E@E@E@E@E@E@q@&$*$=$=$-$-$;$O#>$>$,$,$Q#:#:#S#S#'$q#J@$#$#T#)$!$U#1#1#s#~$t#t#{$W#]$^$^$/$($`#_$y#:$7#9#D#<$a#E#U@+$[$b#X@X@A@'.2.#.p+H H B.!.!.~._+m }$7 a+9 ].. ", +" . =@|$#$#$#$1$2$K##$%$K#K#L#e#f#e#~#L#~#~#~#{#3$4$5$6$7$7$8$&$*$-$-$9$9$>$0$0$P#P#Q#S#S#'$'$'$a$b$T#c$!$!$#2#3#d$e$f$g$]$^$^$h$/$i$`#j$k$y#7#7#l$<$m$E#;#U@+$z@z@H#'.'.@.#.#.i.H q+I J $.K K :+C+7 . ", +" . n$o$p$=@o$q$#$q$#$#$#$J##$$$%$$$K#K#e#e#r$s$s$t$4$5$5$u$v$7$w$w$w$-$x$9$>$>$0$y$P#z$A$B$'$'$'$C$b$T#D$D$!$E$#F$F$G$H$I$J$K$^$L$Y#h$($($`#`#y#y#M$7#C#D#E#E#N$U@>#z@b#X@'#'.z.#.#.p+H j.!.O$K 0+m . ", +" . P$P$n$=@=@=@=@p$p$o$#$#$#$#$#$Q$K#Q$R$R$S$T$r$s$t$t$U$5$6$V$6$w$w$W$x$-$X$;$>$0$y$y$z$z$Y$Z$`$'$ % %.%T#T#!$u@+%@%#%$%%%&%*%=%J$K$-%;%>%>%($,%'%`#`#y#z#7#)%D#0#E#;#N$U@z@,#@$X@Y@'.S.#.o@H I j.I J . ", +" . !%!%~%P${%!%n$!%=@=@=@=@=@p$#$]%^%/%/%/%(%S$T$_%:%s$<%t$5$5$5$6$u$7$w$w$-$X$X$>$[%>$y$y$Y$Y$A$`$}%C$ %.%.%|%1%+%+%2%3%4%$%%%*%5%I$J$K$^$;%>%6%7%,%8%9%0%k$:$7#8#8#D#a%E#.$U@[$z@z@X@Y@Y@S.@.#.A.H q+. ", +" . b%b%c%b%d%d%!%!%!%e%f%e%n$g%g%g%^%h%h%i%j%/%R$R$T$_%:%s$k%U$U$4$6$u$V$w$7$w$x$X$X$l%l%0$m%n%z$A$o%`$p%q%.%.%|%r%+%s%2%2%3%t%u%v%*%5%J$]$w%L$x%y%6%7%,%'%z%A%`#y#B%7#l$l$D#E#E#T@[$C%z@H#X@A@I#z.#.A.. ", +" . D%E%F%F%b%b%G%b%~%~%H%I%I%I%J%J%J%g%I%I%g%]%(%R$_%R$T$T$K%k%K%U$L%M%N%u$w$O%P%Q%X$X$X$[%R%0$n%n%z$Y$Z$Z$S%q%T%|%U%V%W%s%X%3%t%4%u%v%*%Y%J$J$Z%;%;%y%y%`%8% &9%.&`#+&y#7#7#B#D#0#E#E#U@U@@&#&W@X@B@&+. ", +" . $&E%E%E%%&E%&&b%b%*&=&-&;&I%;&I%>&I%I%I%I%g%h%]%/%,&_%_%'&'&k%k%L%U$M%)&N%u$!&O%x$x$W$X$~&~&{&{&0$]&z$Y$Z$Z$T%.%^&/&U%W%s%(&2%_&3%:&u%v%<&[&}&K$K$;%>%y%y%,%8%9%9%z%|&`#y#:$7#7#C#m$E#E#.$U@U@z@@$X@. ", +" . 1&$&E%2&3&4&5&6&6&*&7&6&=&=&*&=&>&-&8&-&I%I%J%g%h%]%,&(%R$_%9&9&'&k%0&L%)&)&u$v$O%!&W$P%W$X$~&~&[%a&z$Y$b&o%c&.%d&d&e&/&U%W%s%X%2%3%t%f&f&g&<&<&h&i&Z%Z%j&y%y%k&l& &m&.&|&n&0%y#z#A#)%D#<$E#;#T@>#@&. ", +" . o&1&1&1&p&p&q&q&p&6&6&6&6&6&*&*&r&8&7&=&8&8&I%;&s&g%]%]%]%,&,&_%9&'&:%K%L%L%U$t&)&!&!&!&W$P%u&v&~&[%{&{&a&Y$w& %T%T%x&y&y&U%z&A&A&B&C&D&D&E&v%v%v%F&G&i&Z%H&x%I&J&K&8%9%m&.&|&L&M& $y#M$7#8#D#m$a%U@. ", +" . N&o&O&O&P&Q&q&q&p&6&q&q&6&6&5&6&6&6&6&=&7&*&*&=&;&;&g%R&^%^%,&,&S&T&T&U&U&k%L%L%M%N%V&W&O%!&X&u&v&Y&v&{&m%m%Z&C$Z&`&c&x&x&y&y& *.*+*s%2%C&D&t%@*f&v%<&#*G&$*%*H&x%y%`%l&8%9%m&A%&*n&**=*y#y#7#7#9#m$. ", +" . -*-*P&O&P&P&P&P&O&p&;*Q&p&p&p&q&q&5&6&q&6&6&6&6&*&=&=&>*s&s&,*'*,&)*S&T&T&!*k%k%L%M%M%V&t&!&O%P%P%X&Y&v&v&{&~*'$~*{*]*]*c&x&x&^*^& *+*/*(*_*D&t%:*E&<*[*F&#*G&$*%*}*I&I&`%K& &9%m&&*|*n&**1* $y#2*2*. ", +" 3*4*5*-*-*-*-*O&-*P&O&P&6*P&P&Q&p&p&q&p&6&5&q&6&6&6&q&6&*&7*7*>*s&,*8*8*9*S&T&U&0*U&a*b*M%M%V&W&W&c*c*X&X&Y&d*e*'$f*g*h*{*]*i*j*x&x&k* * *l*(*(*_*C&m*@*f&n*[*F&#*G&o*%*}*p*q*r*K&s*t*m&&*|*u*v*w*x*k$. ", +" 3*y*z*4*4*A*-*-*-*-*-*-*P&P&P&O&P&p&p&P&P&;*B*p&p&q&q&q&6&6&7&=&C*7*,*8*8*8*9*T&D*E*!*a*a*F*G*H*V&I*W&W&c*J*~*K*'$L*M*f*g*h*N*]*i*c&x&^*y&O* *l*P*(*Q*R*m*S*E&<*T*F&U*G&V*W*X*X*q*r*l&s*Y*Z*`*|***** =.= ", +" +=y*@=@=#=4*4*4*5*$=%=-*-*-*-*O&O&P&-*P&P&Q&Q&P&q&p&;*q&q&q&7&q&7&C*s&,*,*'*8*9*&=*=D*!*U&a*F*G*M%V&t&W&W&==c*I*S#-=;=L*>=,='=)=h*]*i*j*j*^*O*!=~=/*{=]=Q*^=S*S*<*<*T*F&/=o*(=W*X*X*J&r*_=s*:=`*I$<=-%.= ", +" +=[=}=}=[=y*@=y*z*|=z*4*1=1=-*$=-*%=-*-*-*P&P&-*P&P&P&Q&p&q&q&p&q&7&7&C*7*,*,*8*8*D*&=D*!*!*a*a*a*F*G*V&2=W&3=4=:#5=-=-=6=7=>=8=9=h*0=a=b=c=^*O*O*!=l*d={=e=R*R*R*S*E&n*F&U*U*f=o*W*g=X*h=r*G$G$&%H$I$i= ", +" j=k=l=m=l=}=}=}=}=[=@=#=@=4*#=n=1=$=o=-*1=p=-*-*P&q=-*q=-*P&O&O&;*q&6&r=C*7*7*s=t=u=&=v=9*D*0*0*a*a*F*w=w=w=2=x=:#y=z=A=B=B=6=8=C=D='=h*N*E=F=F=G=^*H=H=l*d={=I=e=R*J=K=L=L=M=N=U*o*O=P=4%Q=R=R=G$*%H${$ ", +" j=S=T=k=U=k=V=k=k=}=m=[=[=@=@=y*y*@=n=W=1=X=X=$=-*-*p=-*-*-*q=-*-*O&;*;*7&7&7&C*t=t=Y=v=&=&=*=D*D*0*a*Z=G*`= -5=_#.-+-@-#-$-%-B=&-6=7=,=*-N*=-E=----;-O*O* *l*>-d=]=,-J='-'-L=n*N=)-B&X%X%_&_&!-Q=R=R=~- ", +" j={-S={-S=S=T=S=T=k=k=V=k=V=l=[=[=[=y*y*y*n=n=o=o=n=$=p=1=%=%=$=-*-*P&O&;*;*]-r=r&C*^-t=t=/-&=/-0*0*0*0*Z=`=F*(-_-:-<-.-[-#-A=}-$-|-&-1-2-*-3-4-0=E=5-6-;-f$7-8-l*d=9-]=,-J=0-U%a-z&W%W%s%2%2%2%_&3%4%F$ ", +" b-c-c-c-d-{-d-{-S=S=S=S=T=T=k=k=V=k=V=}=[=[=y*@=n=o=e-n=1=o=1=$=p=%=-*-*P&O&;*r=]-7&f-g-t=t=Y=h-&=D**=0*0*i-j-k-(#l-m-n-.-[-@-#-o-$-p-B=&-q-2-2-3-4-r-E=5-;-G=f$H=s-8-d=x&x&y&y&q%U%U%z&W%s%s%X%2%_&2%F$ ", +" t-c-u-c-c-c-v-d-{-w-{-w-S=x-S=S=S=U=k=l=k=k=V=}=}=[=e-e-e-W=e-e-o=o=n=$=$=-*y-O&;*]-]-z-C*g-^-Y=Y=A-&=B-B-C-i-D-n#E-F-l-G-H-.-.-@-I-}-}-B=J-1->=2-3-K-4-r-5-L-6-M-f$]*]*i*c&j*x&^*^*y&e&q%z&z&N-A&s%B&+% ", +" t-O-u-u-u-u-P-Q-c-c-c-v-c-R-{-S-{-S=S=T=S=k=S=k=k=T-}=m=}=e-e-e-e-e-n=1=n=$=$=y-U-6*V-W-X-f-f-g-Y-Z-A-/-B-B-`- ;/#.;+;@;l-#;H-H-.-[-I-I-}-$;p-J->=>=2-%;4-&;r-,=3-,={*Z&]**;a=i*c&=;x&-;y&!=/&U%a-+*s%+% ", +" t-;;>;,;;;O-u-u-';Q-u-Q-Q-P-c-d-);d-);w-!;S=S=U=T=T=k=T=k=V=}=l=e-~;e-e-e-n=$={;y-$=y-U-V-];]-f-f-Y-^;Z-/;/;/;(;_;:;.;.;<;@;l-[;};n-.-|;I-1;}-$;2;J-3;4;%;6=6=6=8=8=,=3-3-h*]*a=a=i*c&j*x&x&^&!=y&U%+*+% ", +" 5;6;;;;;;;;;;;7;8;O-u-O-O-';';';';Q-c-c-);{-);w-{-{-S=k=S=U=T=k=V=k=m=e-e-~;e-e-{;1={;U-U-];V-];9;9;g-Y-^;^;/;0;_;a;a;:;.;.;b;@;[;G-H-c;[-|;I-d;$;$;J-o-}-B=B=|-&-6=8=8=,=9=3-h*0=]*`&e;a=c&x&d&^&^&y&|% ", +" f;g;g;h;h;i;j;j;;;;;8;>;k;O-u-u-O-c-u-u-';c-c-c-);c-d-w-w-S=S=S=S=T=U=k=k=V=l;l;~;m;e-1=n;o;V-U-];p;9;g-q;g-0;r;s;t;u;u;:;:;.;v;+;b;[;};w;w;x;y;[-|;|;I-o-z;}-$-%-B=&-6=6=8=8=,=*-h*h*0=]*]*i*j*j*j*x&|% ", +" f;A;B;g;g;g;g;g;h;j;6;6;;;k;;;;;O-;;O-k;u-u-c-Q-';';';c-v-);c-w-);d-S=S=U=S=S=T=S=l;~;m;{;{;C;y-y-V-];9;p;p;g-D;s;E;F;G;H;I;:;:;J;<;<;K;[;L;L;H-H-M;y;[-|;I-I-}-}-$-%-p-B=6=6=C=7=8=,=)=h*N;0=]*]*i*c&|% ", +" f;O;P;Q;R;P;Q;S;g;g;g;g;g;i;j;j;;;;;;;;;7;k;O-O-u-u-u-P-';P-u-c-c-);d-w-S={-S=!;S=S=U=k=~;{;{;n;y-o;U-T;p;p;p;U;V;W;X;Y;Y;G;Z;u;:;`;J; >b;b;.>[;};H-n-n-c;[-[-I-#-#-o-$-$-p-|-&-6=6=q-8=8=9=9=h*h*0=]*+> ", +" @>O;#>O;$>#>O;P;P;P;P;S;g;g;g;g;i;h;j;6;6;6;;;;;;;,;>;k;u-u-u-u-';u-';c-c-c-);{-);{-{-S-S=S=U=%>{;&>&>o;*>W-=>U;->;>>>,>'>Y;)>)>!>`;J;~>~>{>{>K;@;]>[;G-H-M;w;y;|;[-@-I-d;z;$-p-B=B=|-6=6=M*8=9=9=h*h*.% ", +" ^>/>(>_>(>_>_>O;O;O;$>O;A;A;P;P;S;R;g;g;i;i;i;6;6;;;;;;;;;k;k;u-O-u-O-u-Q-u-c-c-';c-c-);w-d-S=S-S-:><>n;[>[>o;}>|>1>2>3>W;'>'>)>4>5>6>7>8>J;~>~>{>{>b;K;[;[;H-H-.-c;y;[-[-#-#-}-}-p-$-B=|-&-6=>=8=,=,=.% ", +" 9>0>0>0>a>a>(>/>_>_>#>b>O;O;P;$>P;P;P;Q;g;S;c>g;g;h;i;d>j;6;e>;;;;;;k;u-;;O-u-O-Q-Q-';c-';c-);););{-S-S=n;n;[>f>|>g>g>2>'>h>h>h>h>i>)>!>7>7>8>`;`;~>~>b;b;K;.>[;[;m-w;w;c;.-[-j>I-#-z;}-k>B=B=B=&->=>=.% ", +" 9>l>m>n>0>0>0>0>a>o>(>(>_>_>p>#>p>O;O;P;P;Q;P;Q;g;g;g;g;i;g;i;h;e>j;q>;;;;;;8;7;8;>;u-O-u-u-O-';r>c-c-););w-);s>|>t>u>v>3>w>,>,>'>h>x>)>i>y>5>z>7>`;`;A>~>B>{>K;b;[;L;};H-<-w;c;[-|;#-I-#-}-}-$-B=B=C> % ", +" D>E>F>m>m>m>G>0>0>n>0>0>0>o>_>o>(>_>_>#>#>#>O;#>P;P;P;A;P;B;g;B;g;i;H>i;i;6;;;h;;;;;8;7;O-O-O-u-u-Q-I>';Q-c-c-J>K>L>M>N>O>u>v>w>P>w>Q>R>S>h>)>)>4>!>7>6>`;`;J;~>b;{>b;.>K;[;T>H-U>c;c;[-|;@-#-#-$-}-V> % ", +" W>X>E>E>m>Y>m>Y>Z>`>n>0>0>0>l>0>0>0>(>0>(>/>(>_>_>#>#>O;P;P;P;P;A;B;A;R;g;g;g;j;i;i;i;;;;;;;;;;;8;k;8;O-u-O-u- ,K>.,+,@,M>M>#,u>v>3>P>,>Q>$,$,h>)>i>%,4>6>7>6>`;J;~>~>+;b;]>]>[;L;H-w;w;y;.-[-|;#-#-o-'$ ", +" &,X>X>*,X>X>X>X>E>E>=,m>`>m>`>l>-,l>n>0>0>0>;,;,a>o>(>_>_>O;P;#>#>P;O;P;c>B;P;c>B;g;i;i;i;6;;;e>6;;;7;;;,;O-O->,_;,,',.,),!,L>N>N>#,v>v>~,w>w>w>h>h>{,i>y>4>4>7>6>:;`;J;~>{>{>b;]>.>[;L;w;w;c;[-.-[-#-'$ ", +" &,],],],],^,*,],/,X>X>E>E>E>Y>m>m>m>`>n>`>l>G>l>0>0>0>(,;,o>(>_>(>#>O;p>#>#>P;P;P;c>A;S;g;g;g;g;i;j;i;;;;;6;;;_,s;:,<,[,},.,),+,|,L>M>N>#,v>v>P>w>w>$,h>h>S>)>)>4>7>7>6>`;A>~>{>b;b;b;]>.>[;L;w;H-c;[-'$ ", +" &,1,2,3,4,2,],],],5,^,^,X>X>E>E>E>F>6,E>Y>`>`>l>`>`>`>n>l>0>o>a>_>(>_>_>_>#>p>O;O;P;$>P;P;P;g;c>g;c>g;g;i;q>i;7,s;8,8,9,:,[,[,',),.,),M>N>N>N>v>v>v>w>w>,>Q>$,Y;i>)>y>!>5>6>:;`;`;~>{>{>b;K;[;[;};H-U>'$ ", +" 0,a,a,b,a,3,3,2,3,4,],],],*,^,],^,X>X>E>E>E>E>E>6,`>m>m>`>Z>G>`>l>0>0>0>a>_>(>p>(>#>#>#>$>P;#>P;P;P;P;P;B;g;i;c,s;d,e,f,8,:,<,:,[,[,',',),@,M>N>M>#,g,v>h,P>w>'>$,h>S>)>y>y>4>6>7>`;`;~>B>b;b;b;]>[;L;S# ", +" i,j,k,k,b,j,b,a,3,3,3,2,l,3,],l,],],*,/,/,X>m,E>X>Y>Y>=,F>F>F>m>-,-,l>n>`>0>0>0>o>o>o>p>(>#>p>#>#>b>O;P;n,P;c>c,V;o,p,q,e,r,r,s,9,9,<,,,},.,.,+,L>M>M>N>O>v>v>3>w>Q>'>h>h>{,)>y>4>4>6>6>`;`;~>~>b;b;b;S# ", +" t,u,u,j,v,k,k,j,k,a,w,a,b,a,a,3,1,4,x,],l,],],/,X>X>^,y,E>Y>=,=,m>z,`>`>m>l>n>n>l>0>G>0>0>o>(>/>_>#>#>#>O;$>$>A,V;B,C,o,p,D,e,e,r,8,E,9,<,[,},',.,.,!,M>M>F,O>O>~,3>P>w>$,Q>h>S>Y;G,y>4>7>6>8>`;J;~>~>:# ", +" t,H,I,u,u,J,u,j,j,K,k,j,j,k,k,b,b,a,a,3,1,4,4,],4,],],/,/,^,X>/,*,E>E>E>E>`>m>`>`>`>`>m>0>-,0>0>0>0>(>/>_>/>p>L,|>M,N,N,C,O,P,Q,D,e,f,e,8,9,9,<,[,',.,),),|,M>N>N>u>u>v>P>w>w>,>$,h>S>)>y>4>4>7>:;8>`;R, ", +" S,T,H,H,I,U,V,V,u,v,W,u,X,j,k,k,b,b,b,b,a,a,a,3,3,3,Y,x,x,],],],],],X>X>E>E>E>E>=,F>`>`>z,`>m>`>0>G>l>0>0>0>0>L,|>Z,`, '.'N,N,+'o,P,P,e,e,@'8,9,8,:,<,[,,,#'),),M>M>F,N>u>v>3>w>,>,>h>h>h>S>i>y>4>4>6>:# ", +" S,$'%'%'T,T,I,U,I,V,U,H,u,u,J,J,u,v,v,j,j,k,b,b,a,a,b,3,3,1,l,3,],l,x,*,],*,*,/,m,X>m,E>F>Y>`>z,m>m>`>`>m>-,G>&'|>*'Z,Z,Z,=' 'M,N,B,-'o,o,d,D,e,;'8,8,9,>'[,,,',),),+,M>M>N>N>v>v>h,w>$,h>h>h>{,)>)>%,:# ", +" ,''''''''')')')'!')'I,T,T,I,H,u,u,u,u,v,W,v,j,j,j,j,k,~'~'{'a,3,3,3,2,x,4,],],],],/,*,y,/,X>m,E>z,E>F>F>Y>`>`>]'|>^'/'('*'Z,_':'M,M,M,N,o,o,<'P,D,D,e,8,s,9,:,<,[,['.,.,),M>@,M>#,O>}'v>3>w>w>h>h>h>S>_# ", +" |'1'2'3'4'''''''5'''%'T,)'T,T,I,H,u,I,6'u,u,u,v,u,u,X,v,j,j,j,7'b,a,a,b,3,3,3,Y,l,],Y,],*,*,*,*,*,X>X>E>E>E>Y>8'|>9'9'0'a'('('b'*'Z,='M,N,c'B,o,o,Q,P,Q,q,e,8,8,9,<,<,[,.,.,),),M>M>F,N>d'u>h,w>w>,>$,o# ", +" e'f'g'1'1'1'2'h'4'''4'''''''%'%'%'I,T,T,I,T,u,6'u,u,u,u,J,v,j,J,j,k,k,j,i'b,a,a,3,3,2,],4,x,],],],],^,/,/,*,X>j'k'l'l'm'n'o'p'/'('('*'_'Z,='M,q'.'-'r'o,Q,D,e,e,8,8,8,9,<,[,',',s'),L>M>M>N>O>v>3>3>w>/# ", +" t'u'v'v'e'w'x'w'1'y'1'3'3'4'4'''''5'%''')')'T,T,T,T,I,u,u,u,u,u,v,J,K,j,v,k,k,b,b,a,a,b,3,3,3,3,1,l,l,],*,*,],z'A'B'C'D'l'l'9'n'0'^'('('E'*'_'='='='F'N,O,o,P,P,P,D,e,f,8,9,:,[,[,,,.,),L>@,M>N>O>u>u>G' ", +" H'I'J'K'K'v'g'L'L'w'w'M'1'1'y'''3'N'2'''%'%'%')'%'T,T,T,T,I,T,I,u,H,u,u,u,u,j,j,k,~'b,b,~'7'3,7'3,3,3,2,1,Y,O'P'Q'R'S'B'B'l'l'o'o'T'0'p'('*'E'_' 'U'='.'N,N,O,o,D,e,D,e,f,8,8,:,V'[,[,.,',),@,|,M>/#W' ", +" X'Y'Z'`'`' ) ) )g'L'.)L'+)1'w'1'1'3'1'2'''''''''%'%'%'!')'T,)'I,T,H,@)u,H,u,u,u,J,j,j,j,k,k,b,b,a,a,b,a,3,#)P'$)%)&)R'R'S'B'D'l'l'9'9'0'0'p'('*)Z,Z, 'U'='.'N,N,r'P,D,P,D,@'e,8,s,9,<,[,=)',),@,-) ", +" ;)>),)`'`'`'`'') )g'.).).)L'))w'w'))w'1'1'4'1'3'4'N'''''%'%')'%'T,T,T,T,I,T,V,H,u,u,u,u,K,v,J,J,k,~'b,~'!)~){)])^)/)/)()R'_)S'B'l'l'm'9'n'0'('('('b'Z,Z,=' 'M,N,N,C,o,o,p,e,e,e,8,8,9,:,<,[,_;:) ", +" <)[)})`'`'`'`'`' ) ) )|)1).).)L'L'L'L'L'w'))1'1'1'3'2'3'h'h'''%'%')')'T,!'T,T,T,T,6'u,u,H,u,u,u,j,J,j,2)P'3)4){)5)5)$)$)Q'Q'S'S'B'D'6)l'T'o'o'0'0'('('*)Z,Z,M,M,.'N,-'o,o,o,P,d,e,;'8,8,:,K> ", +" 7)8)9)0)J'`'`'`'`'`' ) )a)b)c)d).).)d).)L'.)e)1'))x'1'1'1'4'''''''''%'%')'%'!')')'I,I,U,U,V,u,I,u,u,f)g)h)i)j)4)k)l)5)])$)&)&)m)S'B'D'n)6)l'9'n'o'0'p'('E'_'o)Z,M,.'F'N,r'o,P,o,e,D,p)s;q) ", +" r)X's)t)u)v)`'`'`'`'K'w)K'a)b)b)x)1).).).).).)L'w'))))1'1'1'3'1'N'1'4'''y)''%')')'T,T,T,T,6'I,V,z)g)A)A)B)h)i)i)l){){)$)$)/)C)()R'S'B'B'C'l'm'9'o'0'a'D)('_'Z,_'M,='F'N,N,r'o,P,E)s; ", +" F)G)H)I)J)`'`'`'`'`'w) )w)b)b)b)c)K)L)d).).)d).)L'))))w'e)e)1'1'y'2'''4'4'%''')'%'M)T,T,T,N)O)P)A)A)Q)B)B)R)j)j){)5)5)5)$)$)Q'&)S'S'B'D'l'l'9'0'0'p'p'('('*'Z,:'M,F'M,N,-'V;S) ", +" T)U)I'V)J'`'`'`'`'w)w)w)W)c)c)b)c)c)c)L)b)d).).).)X)L'+)w'e)e)1'1'1'h'2'4'''''%')'%'Y)Z)`) !.!P)A)A)A)B)i)j)4)4)4)4)5)$)$)/)Q'&)S'S'B'D'l'm'9'0'0'p'a'('*'_'Z,Z,:'M,V; ", +" r)+!s)t)u)v)`'`'`'`'w)w)@!W)b)b)b)c)b)c)b)L)K).).)d)d)L'L'L'w'w'1'e)1'1'1'3'4'''#!Z)$!$!%!&!.! ! !P)A)*!B)=!i)l)l)4)5)5)$)$)Q'R'S'S'B'6)l'l'l'9'n'0'D)('*)E'_'|>-! ", +" ;!>!s),!'!)!`'`'`'`'!!w)W)b)~!b)b)b)c)b)b)b)c)b)K).).)L'.).)L'))))e)+)1'1'{!]!^!/!$!$!%!%!&!.! !P)(!Q)B)B)h)j)l){)5)^)$)$)Q'&)R'S'S'B'D'6)m'm'9'9'p'|>|>_! ", +" :!)[!}!`'`'`'`'!!!!!!|!~!~!~!c)b)b)c)c)b)b)c)K)x)L).)1).)X)L'L'))1!2!3!3!4!^!/!/!$!%!5!&!.!.! !A)A)B)h)i)i)l)4)5)5)$)$)%)R'&)R'S'B'n)l'k'6!7! ", +" 8!9![)0!a!b!`'`'`'`'!!!!!!|!~!~!b)~!c)c)b)b)b)c)b)c)c)L)d).).).)c!2!d!e!3!f!4!4!g!/!%!`)`)&!.!.!P)A)h!B)B)i)i!4)4){)5)])$)$)%)&)S'k'j!k! ", +" l!H's)m!n!o!`'`'`'`'!!!!|!~!~!~!~!p!b)b)c)c)b)b)b)b)b)b)K)q!2!r!r!s!e!e!3!3!4!4!/!/!`)%!`)`).!P)t!A)Q)B)u!i)j)l)l)4)5)5)P'v! ", +" w!x![)y!z!`'`'`'`'!!!!!!A!~!~!~!~!~!p!~!b)b)c)b)b)c)q!2!B!B!B!B!r!C!e!e!3!f!D!4!/!$!%!`)E!.!.!P)A)F!B)B)G!R)P'P'H! ", +" I!>)J!K!L!`'`'`'`'!!!!|!~!~!~!~!~!~!~!b)~!b)c)M!2!B!B!B!B!B!r!r!N!C!e!3!O!3!4!^!/!/!%!P!`).!.!t!(!Q!g)R! ", +" S!T!s)U!V!o!`'`'`'!!!!!!|!W!~!~!W!~!~!~!~!X!2!B!B!B!B!B!B!B!r!B!r!Y!e!Z!e!`!D!4!^!$!$!%!5!Z)O) ~ ", +" .~+~@~I)#~`'`'`'`'!!!!A!|!W!~!W!W!W!X!2!B!B!B!B!B!B!B!B!B!B!r!r!r!r!e!Z!3!D!D!g!Z)O)$~ ", +" %~8)u'&~L!`'`'`'`'!!!!|!W!~!W!X!2!B!B!B!B!B!B!B!B!B!B!B!B!B!B!r!r!d!3!2!*~=~ ", +" -~T!s);~>~v)`'`'`'!!!!!!A!X!2!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!2!,~'~ ", +" )~t)s)!~~~)!`'`'`'!!{~2!B!B!B!B!B!B!B!B!B!B!B!B!B!B!2!,~]~ ", +" :!U)U)^~/~`'`'`'(~2!B!B!B!B!B!B!B!B!B!B!B!2!_~ ", +" :~<~@~+~[~b!`'}~2!B!B!B!B!B!B!B!B!2!|~ ", +" 1~2~3~,!'!4~2!B!B!B!B!B!2!5~ ", +" 6~x!@~7~2!B!8~2!9~ ", +" 0~a~2!b~ ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/src/Mod/Ship/InitGui.py b/src/Mod/Ship/InitGui.py index b20c552af2..05dbfecb5f 100644 --- a/src/Mod/Ship/InitGui.py +++ b/src/Mod/Ship/InitGui.py @@ -34,9 +34,13 @@ class ShipWorkbench ( Workbench ): # ToolBar list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendToolbar("Ship design",list) + list = ["Ship_CreateTank"] + self.appendToolbar("Loading",list) # Menu list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendMenu("Ship design",list) + list = ["Ship_CreateTank"] + self.appendToolbar("Loading",list) Gui.addWorkbench(ShipWorkbench()) diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index 380d8aed84..0251da41d6 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -4,7 +4,8 @@ datadir = $(prefix)/Mod/Ship data_DATA = \ InitGui.py \ ShipGui.py \ - Instance.py + Instance.py \ + TankInstance.py nobase_data_DATA = \ Icons/AreaCurveIco.png \ @@ -33,6 +34,9 @@ nobase_data_DATA = \ Icons/ReparametrizeIco.xpm \ Icons/Ship.xcf \ Icons/Ship.xpm \ + Icons/Tank.png \ + Icons/Tank.xcf \ + Icons/Tank.xpm \ Examples/s60.fcstd \ Examples/barehull5415.fcstd \ Examples/s60_katamaran.fcstd \ @@ -61,7 +65,10 @@ nobase_data_DATA = \ shipUtils/__init__.py \ shipUtils/Math.py \ shipUtils/Paths.py \ - shipUtils/Translator.py + shipUtils/Translator.py \ + tankCreateTank/__init__.py \ + tankCreateTank/TaskPanel.py \ + tankCreateTank/TaskPanel.ui CLEANFILES = $(BUILT_SOURCES) diff --git a/src/Mod/Ship/ShipGui.py b/src/Mod/Ship/ShipGui.py index 8f50b4be07..f62bebfb4d 100644 --- a/src/Mod/Ship/ShipGui.py +++ b/src/Mod/Ship/ShipGui.py @@ -84,8 +84,21 @@ class Hydrostatics: ToolTip = str(Translator.translate('Plot ship hydrostatics')) return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} +class CreateTank: + def Activated(self): + import tankCreateTank + tankCreateTank.load() + + def GetResources(self): + from shipUtils import Paths, Translator + IconPath = Paths.iconsPath() + "/Tank.png" + MenuText = str(Translator.translate('Create a new tank')) + ToolTip = str(Translator.translate('Create a new ship tank')) + return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} + FreeCADGui.addCommand('Ship_LoadExample', LoadExample()) FreeCADGui.addCommand('Ship_CreateShip', CreateShip()) FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw()) FreeCADGui.addCommand('Ship_AreasCurve', AreasCurve()) FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics()) +FreeCADGui.addCommand('Ship_CreateTank', CreateTank()) diff --git a/src/Mod/Ship/TankInstance.py b/src/Mod/Ship/TankInstance.py new file mode 100644 index 0000000000..584aa0f974 --- /dev/null +++ b/src/Mod/Ship/TankInstance.py @@ -0,0 +1,1886 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import time + +# COIN +from pivy.coin import * +from pivy import coin + +# FreeCAD +import FreeCAD,FreeCADGui +from FreeCAD import Part, Base, Vector + +# Ship design module +from shipUtils import Paths, Translator, Math + +class ShipTank: + def __init__(self, obj, solid, level=0, density=998.0): + """ Creates a new tank on active document. + @param obj Created Part::FeaturePython object. + @param solid Solid shape that represent the tank. + @param level Tank filling level. + @param density Fluid density. + """ + # Add uniqueness property to identify Tank instances + obj.addProperty("App::PropertyBool","IsShipTank","ShipTank", str(Translator.translate("True if is a valid ship tank instance"))).IsShipTank=True + # Add general options + obj.addProperty("App::PropertyFloat","Level","ShipTank", str(Translator.translate("Filling level"))).Level=level + obj.addProperty("App::PropertyFloat","Density","ShipTank", str(Translator.translate("Inside fluid density"))).Density=density + # Add shapes + shape = self.computeShape(solid) + if not shape: + obj.IsShipTank=False + return + # obj.addProperty("Part::PropertyPartShape","Shape","ShipTank", str(Translator.translate("Tank solid"))).Shape = shape + obj.Shape = shape + obj.Proxy = self + self.obj = obj + + def onChanged(self, fp, prop): + """ Property changed, tank must be recomputed """ + if prop == "IsShipTank": + FreeCAD.Console.PrintWarning("Ussually you don't want to modify manually this option.\n") + elif prop == "Level": + if fp.Level > 100.0: + fp.Level = 100.0 + elif fp.Level < 0.0: + fp.Level = 0.0 + + def execute(self, obj): + """ Shape recomputation called """ + obj.Shape = self.computeShape(obj.Shape) + + def computeShape(self, solid): + """ Create faces shape. This method also calls to generate boxes. + @param solid Solid shape that represent the tank. + @return Computed solid shape. None if can't build it. + """ + # Study input to try to build a solid + if solid.isDerivedFrom('Part::Feature'): + # Get shape + shape = solid.Shape + if not shape: + return None + solid = shape + if not solid.isDerivedFrom('Part::TopoShape'): + return None + # Get shells + shells = solid.Shells + if not shells: + return None + # Build solids + solids = [] + for s in shells: + solid = Part.Solid(s) + if solid.Volume < 0.0: + solid.reverse() + solids.append(solid) + # Create compound + shape = Part.CompSolid(solids) + return shape + +class ViewProviderShipTank: + def __init__(self, obj): + """ Set this object to the proxy object of the actual view provider """ + obj.Proxy = self + + def attach(self, obj): + """ Setup the scene sub-graph of the view provider, this method is mandatory """ + return + + def updateData(self, fp, prop): + """ If a property of the handled feature has changed we have the chance to handle this here """ + return + + def getDisplayModes(self,obj): + ''' Return a list of display modes. ''' + modes=[] + return modes + + def getDefaultDisplayMode(self): + ''' Return the name of the default display mode. It must be defined in getDisplayModes. ''' + return "Shaded" + + def setDisplayMode(self,mode): + ''' Map the display mode defined in attach with those defined in getDisplayModes. + Since they have the same names nothing needs to be done. This method is optinal. + ''' + return mode + + def onChanged(self, vp, prop): + ''' Print the name of the property that has changed ''' + # FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n") + + def __getstate__(self): + ''' When saving the document this object gets stored using Python's cPickle module. + Since we have some un-pickable here -- the Coin stuff -- we must define this method + to return a tuple of all pickable objects or None. + ''' + return None + + def __setstate__(self,state): + ''' When restoring the pickled object from document we have the chance to set some + internals here. Since no data were pickled nothing needs to be done here. + ''' + return None + + def getIcon(self): + return """ + /* XPM */ + static char * Tank_xpm[] = { + "128 128 1605 2", + " c None", + ". c #000000", + "+ c #D1D1D1", + "@ c #D2D2D2", + "# c #D3D3D3", + "$ c #D3D4D4", + "% c #D5D5D5", + "& c #CFD0CF", + "* c #D0D0D0", + "= c #D3D2D3", + "- c #D4D4D3", + "; c #D5D6D5", + "> c #D6D6D6", + ", c #D7D7D7", + "' c #CDCECE", + ") c #CFCECE", + "! c #D0CFCF", + "~ c #D3D3D2", + "{ c #D3D3D4", + "] c #D5D4D5", + "^ c #D6D7D6", + "/ c #D8D8D8", + "( c #D8D8D9", + "_ c #D9D9DA", + ": c #CCCCCB", + "< c #CCCCCC", + "[ c #CECECD", + "} c #CECECE", + "| c #CFCFCF", + "1 c #D1D2D2", + "2 c #D5D4D4", + "3 c #D8D9D9", + "4 c #DAD9DA", + "5 c #DADBDB", + "6 c #DBDCDB", + "7 c #CACACA", + "8 c #CACBCB", + "9 c #CBCBCB", + "0 c #CCCDCD", + "a c #CECDCD", + "b c #D2D2D1", + "c c #D2D3D3", + "d c #D4D3D3", + "e c #D9D9D8", + "f c #D9D9D9", + "g c #DADBDA", + "h c #DBDCDC", + "i c #DCDCDC", + "j c #DDDDDD", + "k c #DEDEDE", + "l c #C7C8C8", + "m c #C9C9C9", + "n c #CAC9CA", + "o c #CBCBCC", + "p c #CCCDCC", + "q c #CDCDCD", + "r c #D0D1D1", + "s c #D1D2D1", + "t c #D3D2D2", + "u c #D4D4D5", + "v c #D5D5D6", + "w c #D6D7D7", + "x c #D8D7D8", + "y c #D9D8D9", + "z c #DADAD9", + "A c #DBDADA", + "B c #DBDBDC", + "C c #DDDCDD", + "D c #DDDEDE", + "E c #DEDFDE", + "F c #DFE0E0", + "G c #E0E0E1", + "H c #C6C6C6", + "I c #C7C7C7", + "J c #C8C8C7", + "K c #C9C9C8", + "L c #CACAC9", + "M c #CBCACB", + "N c #D0CFD0", + "O c #D1D0D1", + "P c #D1D1D2", + "Q c #D4D4D4", + "R c #D6D5D6", + "S c #D7D6D7", + "T c #D7D8D8", + "U c #DAD9D9", + "V c #DADADA", + "W c #DBDBDB", + "X c #DCDBDC", + "Y c #DDDDDC", + "Z c #DFDEDF", + "` c #E0DFDF", + " . c #E0E0E0", + ".. c #E1E1E1", + "+. c #E2E2E2", + "@. c #C5C4C4", + "#. c #C5C5C5", + "$. c #C8C8C8", + "%. c #C9C8C8", + "&. c #D5D5D4", + "*. c #D8D7D7", + "=. c #D9D8D8", + "-. c #DCDDDD", + ";. c #DEDDDD", + ">. c #E3E4E3", + ",. c #E5E4E4", + "'. c #C4C4C4", + "). c #C6C7C6", + "!. c #C7C8C7", + "~. c #C8C8C9", + "{. c #CACACB", + "]. c #CCCBCB", + "^. c #D0D0D1", + "/. c #D5D6D6", + "(. c #D7D7D6", + "_. c #DFDFDF", + ":. c #E1E2E2", + "<. c #E3E3E3", + "[. c #E4E3E4", + "}. c #E6E5E5", + "|. c #E6E6E6", + "1. c #E6E7E7", + "2. c #C4C5C5", + "3. c #C6C6C7", + "4. c #CFD0D0", + "5. c #D1D0D0", + "6. c #D6D5D5", + "7. c #DADADB", + "8. c #DCDDDC", + "9. c #DDDDDE", + "0. c #DEDFDF", + "a. c #E1E1E2", + "b. c #E3E2E2", + "c. c #E4E4E4", + "d. c #E5E5E5", + "e. c #E8E8E7", + "f. c #E9E8E9", + "g. c #EAE9EA", + "h. c #C5C5C4", + "i. c #C6C6C5", + "j. c #C7C7C6", + "k. c #CBCACA", + "l. c #CDCDCC", + "m. c #CECFCF", + "n. c #DBDADB", + "o. c #E0E0DF", + "p. c #E1E1E0", + "q. c #E1E2E1", + "r. c #E3E3E4", + "s. c #E4E5E5", + "t. c #E7E6E7", + "u. c #E7E7E8", + "v. c #E9E9E8", + "w. c #E9EAE9", + "x. c #EAEAEA", + "y. c #EBEBEA", + "z. c #C4C5C4", + "A. c #C6C5C5", + "B. c #C6C7C7", + "C. c #CCCCCD", + "D. c #CECECF", + "E. c #D9DAD9", + "F. c #DBDBDA", + "G. c #DEDEDD", + "H. c #E0DFE0", + "I. c #E0E1E0", + "J. c #E6E5E6", + "K. c #E7E7E6", + "L. c #E7E8E7", + "M. c #E8E8E9", + "N. c #E9E9E9", + "O. c #ECECEC", + "P. c #ECEDED", + "Q. c #EDEDEE", + "R. c #010101", + "S. c #C5C4C5", + "T. c #CBCCCB", + "U. c #CDCCCC", + "V. c #CECFCE", + "W. c #CFCFCE", + "X. c #CFCFD0", + "Y. c #D7D8D7", + "Z. c #DEDDDE", + "`. c #E1E0E0", + " + c #E5E6E6", + ".+ c #EBEBEB", + "++ c #ECEBEC", + "@+ c #EDEDED", + "#+ c #EEEEEE", + "$+ c #EFEFEF", + "%+ c #F0F0F0", + "&+ c #C4C4C5", + "*+ c #CFCECF", + "=+ c #D2D1D1", + "-+ c #D6D6D5", + ";+ c #D8D9D8", + ">+ c #DDDEDD", + ",+ c #DFDEDE", + "'+ c #E2E3E3", + ")+ c #E7E7E7", + "!+ c #E8E8E8", + "~+ c #EBECEC", + "{+ c #ECEDEC", + "]+ c #EFEFF0", + "^+ c #F1F0F1", + "/+ c #F1F1F1", + "(+ c #C7C6C6", + "_+ c #C9C8C9", + ":+ c #C9CAC9", + "<+ c #CACBCA", + "[+ c #D2D2D3", + "}+ c #D4D5D5", + "|+ c #DCDCDB", + "1+ c #E3E3E2", + "2+ c #E4E3E3", + "3+ c #E4E4E5", + "4+ c #E5E5E6", + "5+ c #EAEAE9", + "6+ c #ECEBEB", + "7+ c #F0F0EF", + "8+ c #F0F0F1", + "9+ c #C8C7C8", + "0+ c #C8C9C9", + "a+ c #CBCBCA", + "b+ c #CCCBCC", + "c+ c #D7D6D6", + "d+ c #DDDCDC", + "e+ c #E2E2E1", + "f+ c #E2E3E2", + "g+ c #E3E4E4", + "h+ c #E8E7E8", + "i+ c #E9E8E8", + "j+ c #EAE9E9", + "k+ c #EDECEC", + "l+ c #EEEEED", + "m+ c #EFEEEF", + "n+ c #EFF0F0", + "o+ c #F1F0F0", + "p+ c #C5C6C6", + "q+ c #C7C6C7", + "r+ c #D0D0CF", + "s+ c #D4D5D4", + "t+ c #E1E0E1", + "u+ c #E8E9E9", + "v+ c #EDECED", + "w+ c #EEEDED", + "x+ c #EEEFEF", + "y+ c #F0EFF0", + "z+ c #F0F1F0", + "A+ c #D7D7D8", + "B+ c #C8C7C7", + "C+ c #C9C9CA", + "D+ c #D2D1D2", + "E+ c #D9DADA", + "F+ c #E2E1E1", + "G+ c #E9E9EA", + "H+ c #EAEBEB", + "I+ c #D8D8D7", + "J+ c #CBCCCC", + "K+ c #CDCCCD", + "L+ c #D6D6D7", + "M+ c #E6E6E5", + "N+ c #E7E6E6", + "O+ c #E8E7E7", + "P+ c #EFEEEE", + "Q+ c #CDCECD", + "R+ c #D1D1D0", + "S+ c #D2D3D2", + "T+ c #DFE0DF", + "U+ c #E3E2E3", + "V+ c #E4E5E4", + "W+ c #E5E6E5", + "X+ c #ECECED", + "Y+ c #D4D3D4", + "Z+ c #CECDCE", + "`+ c #E6E6E7", + " @ c #EBEAEB", + ".@ c #ECECEB", + "+@ c #F0F1F1", + "@@ c #DCDCDD", + "#@ c #EBEAEA", + "$@ c #F0EFEF", + "%@ c #D3D4D3", + "&@ c #E6E7E6", + "*@ c #EEEDEE", + "=@ c #ADADAD", + "-@ c #565656", + ";@ c #DCDBDB", + ">@ c #DFDFE0", + ",@ c #E7E8E8", + "'@ c #E8E9E8", + ")@ c #EDEDEC", + "!@ c #EBEBEC", + "~@ c #EEEFEE", + "{@ c #B7B6B7", + "]@ c #E5E4E5", + "^@ c #EDEEED", + "/@ c #B6B6B6", + "(@ c #B6B7B7", + "_@ c #B7B7B6", + ":@ c #B7B6B6", + "<@ c #E5E5E4", + "[@ c #B5B6B5", + "}@ c #B5B6B6", + "|@ c #B5B5B5", + "1@ c #B6B5B5", + "2@ c #B6B5B6", + "3@ c #E2E1E2", + "4@ c #E4E4E3", + "5@ c #B4B4B4", + "6@ c #B4B5B5", + "7@ c #B5B5B4", + "8@ c #B6B6B5", + "9@ c #E2E2E3", + "0@ c #B3B4B4", + "a@ c #B4B4B5", + "b@ c #B4B5B4", + "c@ c #B5B4B4", + "d@ c #B3B3B3", + "e@ c #B3B3B4", + "f@ c #B4B3B3", + "g@ c #B4B4B3", + "h@ c #B5B4B5", + "i@ c #D0D1D0", + "j@ c #B3B3B2", + "k@ c #B3B2B3", + "l@ c #B4B3B4", + "m@ c #90B9D9", + "n@ c #91BAD9", + "o@ c #C6C5C6", + "p@ c #B1B2B2", + "q@ c #B2B2B2", + "r@ c #B2B3B2", + "s@ c #B2B3B3", + "t@ c #8FB7D8", + "u@ c #8EB7D7", + "v@ c #8FB8D8", + "w@ c #90B8D8", + "x@ c #91BBD9", + "y@ c #91BCDA", + "z@ c #C2C2C2", + "A@ c #C3C4C3", + "B@ c #C3C4C4", + "C@ c #C8C9C8", + "D@ c #CDCDCE", + "E@ c #B1B1B1", + "F@ c #B1B2B1", + "G@ c #B2B1B2", + "H@ c #B2B2B3", + "I@ c #B3B2B2", + "J@ c #8CB4D6", + "K@ c #8DB4D6", + "L@ c #8DB5D7", + "M@ c #8EB6D7", + "N@ c #8EB7D8", + "O@ c #8FB7D7", + "P@ c #90B9D8", + "Q@ c #91BBDA", + "R@ c #92BCD9", + "S@ c #92BCDA", + "T@ c #C1C1C0", + "U@ c #C1C1C1", + "V@ c #C2C2C1", + "W@ c #C3C3C2", + "X@ c #C3C3C3", + "Y@ c #C3C3C4", + "Z@ c #CAC9C9", + "`@ c #B1B0B0", + " # c #B2B2B1", + ".# c #8BB2D5", + "+# c #8BB2D6", + "@# c #8CB3D6", + "## c #8CB3D7", + "$# c #8DB5D6", + "%# c #8EB5D7", + "&# c #8FB8D7", + "*# c #92BBD9", + "=# c #92BBDA", + "-# c #BFC0C0", + ";# c #C0C1C0", + "># c #C2C1C1", + ",# c #C3C2C2", + "'# c #C4C4C3", + ")# c #C5C6C5", + "!# c #C5C5C6", + "~# c #B0B0B0", + "{# c #B0B1B0", + "]# c #B1B1B2", + "^# c #B2B1B1", + "/# c #0E3459", + "(# c #0E355A", + "_# c #0F355A", + ":# c #0F365A", + "<# c #8AB2D5", + "[# c #8CB3D5", + "}# c #8BB3D6", + "|# c #8FB6D7", + "1# c #8FB9D7", + "2# c #90B9D7", + "3# c #90BAD8", + "4# c #90BBD9", + "5# c #92BDDA", + "6# c #93BEDA", + "7# c #BEBEBE", + "8# c #BEBFBE", + "9# c #BFBFBE", + "0# c #BFBFC0", + "a# c #C0C0BF", + "b# c #C2C3C2", + "c# c #C4C3C3", + "d# c #AFB0B0", + "e# c #AFAFB0", + "f# c #B0AFB0", + "g# c #B0B1B1", + "h# c #B0B0B1", + "i# c #B1B0B1", + "j# c #7A9EC5", + "k# c #799FC5", + "l# c #7A9FC5", + "m# c #7AA0C6", + "n# c #0E345A", + "o# c #0F3559", + "p# c #8BB1D5", + "q# c #8CB4D5", + "r# c #8FB9D8", + "s# c #90BAD7", + "t# c #91BBD8", + "u# c #91BCD9", + "v# c #91BDD9", + "w# c #92BEDA", + "x# c #93BFDA", + "y# c #BDBDBD", + "z# c #BDBEBD", + "A# c #BEBEBD", + "B# c #BEBEBF", + "C# c #BEBFBF", + "D# c #BFBFBF", + "E# c #C0C0C0", + "F# c #C1C0C0", + "G# c #C0C1C1", + "H# c #C3C2C3", + "I# c #C4C3C4", + "J# c #AFAEAF", + "K# c #AFAFAF", + "L# c #B0AFAF", + "M# c #789DC5", + "N# c #789EC5", + "O# c #799EC5", + "P# c #7AA1C5", + "Q# c #7BA1C5", + "R# c #0F3659", + "S# c #10375A", + "T# c #8DB6D6", + "U# c #8EB8D7", + "V# c #92BDD9", + "W# c #143F5B", + "X# c #174A6A", + "Y# c #84B0CB", + "Z# c #85B0CC", + "`# c #BCBCBC", + " $ c #BDBCBC", + ".$ c #C0C0C1", + "+$ c #C1C2C1", + "@$ c #C2C3C3", + "#$ c #AEAEAE", + "$$ c #AEAFAF", + "%$ c #AFAFAE", + "&$ c #779BC4", + "*$ c #779CC4", + "=$ c #789CC4", + "-$ c #789DC4", + ";$ c #789EC4", + ">$ c #799FC4", + ",$ c #7AA0C5", + "'$ c #10385A", + ")$ c #8DB7D7", + "!$ c #8EB7D6", + "~$ c #90BBD8", + "{$ c #133E5B", + "]$ c #83AEC9", + "^$ c #84AFCA", + "/$ c #85B1CB", + "($ c #85B2CB", + "_$ c #BDBCBD", + ":$ c #BEBDBD", + "<$ c #C0BFBF", + "[$ c #C1C1C2", + "}$ c #C9CACA", + "|$ c #AEAEAD", + "1$ c #AEAFAE", + "2$ c #AEAEAF", + "3$ c #7599C3", + "4$ c #7699C3", + "5$ c #7699C2", + "6$ c #769AC3", + "7$ c #779BC3", + "8$ c #789BC4", + "9$ c #799EC4", + "0$ c #79A0C4", + "a$ c #8CB5D5", + "b$ c #8CB5D6", + "c$ c #8DB7D6", + "d$ c #426D8A", + "e$ c #16486A", + "f$ c #7AA5C1", + "g$ c #83ADC9", + "h$ c #84B1CB", + "i$ c #85B1CC", + "j$ c #BCBDBD", + "k$ c #BDBDBC", + "l$ c #BFBEBF", + "m$ c #BFC0BF", + "n$ c #ADADAC", + "o$ c #AEADAD", + "p$ c #ADAEAD", + "q$ c #ADAEAE", + "r$ c #7496C2", + "s$ c #7597C2", + "t$ c #7598C2", + "u$ c #769BC2", + "v$ c #779BC2", + "w$ c #779CC3", + "x$ c #789DC3", + "y$ c #7AA0C4", + "z$ c #7AA1C4", + "A$ c #7AA2C5", + "B$ c #7BA2C5", + "C$ c #10395A", + "D$ c #8EB6D6", + "E$ c #8EB8D6", + "F$ c #133D5A", + "G$ c #81ABC8", + "H$ c #82ACC9", + "I$ c #82ADC9", + "J$ c #82AEC9", + "K$ c #83AFC9", + "L$ c #83B0CA", + "M$ c #BDBEBE", + "N$ c #C1C0C1", + "O$ c #C7C7C8", + "P$ c #ADACAC", + "Q$ c #AFAEAE", + "R$ c #7395C1", + "S$ c #7395C2", + "T$ c #7496C1", + "U$ c #7599C2", + "V$ c #769BC3", + "W$ c #779DC3", + "X$ c #789EC3", + "Y$ c #7AA2C4", + "Z$ c #7BA3C4", + "`$ c #7BA3C5", + " % c #11395A", + ".% c #113A5A", + "+% c #123C5A", + "@% c #164A6E", + "#% c #4C7694", + "$% c #80AAC7", + "%% c #80ABC8", + "&% c #81ACC9", + "*% c #81ACC8", + "=% c #81ADC9", + "-% c #83AFCA", + ";% c #84B0CA", + ">% c #84B1CA", + ",% c #86B3CB", + "'% c #87B4CC", + ")% c #BFBEBE", + "!% c #ACACAC", + "~% c #ABACAC", + "{% c #ACACAD", + "]% c #7294C0", + "^% c #7194C0", + "/% c #7294C1", + "(% c #7394C1", + "_% c #7396C1", + ":% c #7497C2", + "<% c #7498C2", + "[% c #799FC3", + "}% c #7CA3C5", + "|% c #123B5A", + "1% c #88B1D0", + "2% c #7EA8C6", + "3% c #7FA9C6", + "4% c #7FAAC7", + "5% c #81ADC8", + "6% c #85B1CA", + "7% c #86B2CB", + "8% c #86B4CB", + "9% c #87B5CB", + "0% c #BCBCBD", + "a% c #C0BFC0", + "b% c #ABABAB", + "c% c #ACABAC", + "d% c #ABABAC", + "e% c #ACADAC", + "f% c #ACADAD", + "g% c #7193C0", + "h% c #7293C0", + "i% c #7193C1", + "j% c #7293C1", + "k% c #7498C1", + "l% c #789FC4", + "m% c #79A1C3", + "n% c #79A1C4", + "o% c #7BA2C4", + "p% c #7CA4C5", + "q% c #7CA4C4", + "r% c #16496F", + "s% c #7DA7C5", + "t% c #7FAAC6", + "u% c #80ABC7", + "v% c #80ACC7", + "w% c #82AFC9", + "x% c #84B1C9", + "y% c #85B2CA", + "z% c #87B5CC", + "A% c #88B6CC", + "B% c #BDBDBE", + "C% c #C1C2C2", + "D% c #AAABAB", + "E% c #AAAAAA", + "F% c #ABAAAB", + "G% c #ABACAB", + "H% c #ACACAB", + "I% c #7192C0", + "J% c #7292C0", + "K% c #7598C1", + "L% c #7599C1", + "M% c #759AC1", + "N% c #769AC2", + "O% c #779CC2", + "P% c #779DC2", + "Q% c #789DC2", + "R% c #78A0C3", + "S% c #7CA3C4", + "T% c #7AA2C2", + "U% c #7CA5C4", + "V% c #7DA5C4", + "W% c #7DA6C5", + "X% c #7EA8C5", + "Y% c #82ADC8", + "Z% c #83B0C9", + "`% c #85B3CA", + " & c #86B5CB", + ".& c #87B6CC", + "+& c #BCBDBC", + "@& c #C2C1C2", + "#& c #C2C2C3", + "$& c #AAA9AA", + "%& c #AAABAA", + "&& c #ABAAAA", + "*& c #7091BF", + "=& c #7092BF", + "-& c #7191C0", + ";& c #7192BF", + ">& c #7092C0", + ",& c #7295C0", + "'& c #7497C1", + ")& c #759AC2", + "!& c #769CC2", + "~& c #789FC3", + "{& c #79A0C3", + "]& c #79A2C4", + "^& c #7AA4C3", + "/& c #7BA4C4", + "(& c #7DA8C5", + "_& c #7EA9C6", + ":& c #80AAC6", + "<& c #81ADC7", + "[& c #82ADC7", + "}& c #82AEC8", + "|& c #88B7CC", + "1& c #A9A9A9", + "2& c #A9AAAA", + "3& c #A9A9AA", + "4& c #A9AAA9", + "5& c #7090BE", + "6& c #7091BE", + "7& c #7092BE", + "8& c #7191BF", + "9& c #7397C1", + "0& c #7499C1", + "a& c #7AA1C3", + "b& c #7AA2C3", + "c& c #79A2C2", + "d& c #7AA3C3", + "e& c #7BA5C4", + "f& c #80ABC6", + "g& c #81ACC7", + "h& c #81AEC8", + "i& c #82AFC8", + "j& c #83B1CA", + "k& c #85B3CB", + "l& c #85B4CA", + "m& c #87B6CB", + "n& c #88B8CC", + "o& c #A8A8A8", + "p& c #6F90BE", + "q& c #6F91BE", + "r& c #7192BE", + "s& c #7193BF", + "t& c #769BC1", + "u& c #779EC3", + "v& c #789FC2", + "w& c #79A1C2", + "x& c #7AA3C2", + "y& c #7BA4C3", + "z& c #7CA6C5", + "A& c #7DA7C4", + "B& c #7EA7C5", + "C& c #7EA9C5", + "D& c #7EAAC6", + "E& c #7FACC6", + "F& c #81AEC7", + "G& c #82AFC7", + "H& c #83B1C9", + "I& c #84B2C9", + "J& c #84B3C9", + "K& c #86B4CA", + "L& c #89B7CC", + "M& c #89B8CD", + "N& c #A8A8A7", + "O& c #6F90BD", + "P& c #6E90BD", + "Q& c #6E90BE", + "R& c #7293BF", + "S& c #7396C0", + "T& c #7397C0", + "U& c #7498C0", + "V& c #759BC1", + "W& c #769CC1", + "X& c #779EC2", + "Y& c #789EC2", + "Z& c #78A0C1", + "`& c #78A1C2", + " * c #7BA6C3", + ".* c #7BA6C4", + "+* c #7CA6C4", + "@* c #7FABC6", + "#* c #82AEC7", + "$* c #83AFC8", + "%* c #83B0C8", + "&* c #88B7CB", + "** c #89B9CC", + "=* c #89BACD", + "-* c #6E8FBD", + ";* c #6F91BD", + ">* c #7093BF", + ",* c #7194BF", + "'* c #7294BF", + ")* c #7395C0", + "!* c #7398C0", + "~* c #779FC1", + "{* c #77A0C1", + "]* c #78A1C1", + "^* c #7AA4C2", + "/* c #7CA7C4", + "(* c #7DA8C4", + "_* c #7DA9C5", + ":* c #7FAAC5", + "<* c #80ACC6", + "[* c #80ADC7", + "}* c #84B1C8", + "|* c #88B8CB", + "1* c #89BACC", + "2* c #BEBDBE", + "3* c #12285A", + "4* c #768CBE", + "5* c #768DBE", + "6* c #6E91BD", + "7* c #7193BE", + "8* c #7295BF", + "9* c #7396BF", + "0* c #7398BF", + "a* c #7499C0", + "b* c #749AC1", + "c* c #779DC1", + "d* c #789FC1", + "e* c #779EC0", + "f* c #769EC0", + "g* c #769FC0", + "h* c #77A0C0", + "i* c #79A2C1", + "j* c #79A3C2", + "k* c #7BA4C2", + "l* c #7CA7C3", + "m* c #7EAAC5", + "n* c #80ADC6", + "o* c #82B0C7", + "p* c #85B2C9", + "q* c #85B3C9", + "r* c #85B4C9", + "s* c #86B5CA", + "t* c #87B6CA", + "u* c #88B9CC", + "v* c #8AB9CC", + "w* c #8ABACC", + "x* c #8BBBCC", + "y* c #758BBE", + "z* c #758CBE", + "A* c #768CBF", + "B* c #6E91BE", + "C* c #7093BE", + "D* c #7397BF", + "E* c #7497BF", + "F* c #749AC0", + "G* c #759AC0", + "H* c #769BC0", + "I* c #769CC0", + "J* c #779EC1", + "K* c #769DC0", + "L* c #759DBF", + "M* c #769DBE", + "N* c #77A1C0", + "O* c #7AA5C2", + "P* c #7DA7C3", + "Q* c #7EA9C4", + "R* c #7EAAC4", + "S* c #7FABC5", + "T* c #81ADC6", + "U* c #81AFC7", + "V* c #82B0C8", + "W* c #83B1C8", + "X* c #84B2C8", + "Y* c #86B6CB", + "Z* c #87B7CA", + "`* c #87B7CB", + " = c #84AFCB", + ".= c #143F5A", + "+= c #12275A", + "@= c #758BBD", + "#= c #768BBE", + "$= c #6E8FBC", + "%= c #6E8EBD", + "&= c #7296BF", + "*= c #7297BF", + "== c #779CC1", + "-= c #749BBE", + ";= c #749CBF", + ">= c #759EBE", + ",= c #769FBF", + "'= c #76A0C0", + ")= c #779FC0", + "!= c #7BA5C3", + "~= c #7CA6C3", + "{= c #7CA8C4", + "]= c #7DA9C3", + "^= c #7FAAC4", + "/= c #81AFC6", + "(= c #82B1C7", + "_= c #85B5C9", + ":= c #86B6CA", + "<= c #82AECA", + "[= c #758ABD", + "}= c #748ABD", + "|= c #758CBD", + "1= c #6D8FBC", + "2= c #759CC1", + "3= c #769DC1", + "4= c #759BBF", + "5= c #739ABE", + "6= c #759DBE", + "7= c #769EBE", + "8= c #769EBF", + "9= c #779FBF", + "0= c #78A1C0", + "a= c #78A2C1", + "b= c #79A2C0", + "c= c #7AA3C1", + "d= c #7CA8C3", + "e= c #7DA9C4", + "f= c #82B0C6", + "g= c #83B2C8", + "h= c #84B3C8", + "i= c #143E5B", + "j= c #11265A", + "k= c #7489BC", + "l= c #748ABC", + "m= c #758ABC", + "n= c #6D8EBC", + "o= c #6E8EBC", + "p= c #6D8FBD", + "q= c #6F8FBD", + "r= c #7092BD", + "s= c #7294BE", + "t= c #7194BE", + "u= c #7295BE", + "v= c #7196BF", + "w= c #759BC0", + "x= c #749ABE", + "y= c #7399BC", + "z= c #729ABD", + "A= c #739ABD", + "B= c #749CBD", + "C= c #769DBF", + "D= c #779EBF", + "E= c #78A2C0", + "F= c #79A3C1", + "G= c #7AA4C1", + "H= c #7BA6C2", + "I= c #7CA9C4", + "J= c #7EABC4", + "K= c #7FACC4", + "L= c #80ADC5", + "M= c #80AEC6", + "N= c #81AEC6", + "O= c #83B1C7", + "P= c #7EA9C7", + "Q= c #7FAAC8", + "R= c #80AAC8", + "S= c #7388BC", + "T= c #7488BC", + "U= c #7389BC", + "V= c #7489BD", + "W= c #6E8EBB", + "X= c #6D8EBD", + "Y= c #7195BE", + "Z= c #7499BF", + "`= c #749ABF", + " - c #749BC0", + ".- c #7198BB", + "+- c #7298BC", + "@- c #7299BC", + "#- c #729ABC", + "$- c #739BBD", + "%- c #749BBD", + "&- c #749DBE", + "*- c #76A0BF", + "=- c #77A2C0", + "-- c #78A3C1", + ";- c #79A4C1", + ">- c #7CA8C2", + ",- c #7DAAC4", + "'- c #7FACC5", + ")- c #7DA7C6", + "!- c #7FA9C7", + "~- c #133E5A", + "{- c #7387BC", + "]- c #6F92BD", + "^- c #7094BE", + "/- c #7296BE", + "(- c #7399BD", + "_- c #0E3559", + ":- c #7096BB", + "<- c #7197BB", + "[- c #7199BB", + "}- c #739BBC", + "|- c #749CBE", + "1- c #749EBE", + "2- c #769FBE", + "3- c #77A0BF", + "4- c #77A1BF", + "5- c #78A3C0", + "6- c #79A4C0", + "7- c #7AA6C2", + "8- c #7BA7C2", + "9- c #7DA8C3", + "0- c #7EACC5", + "a- c #7CA5C5", + "b- c #10255A", + "c- c #7286BB", + "d- c #7287BC", + "e- c #6D8EBB", + "f- c #7093BD", + "g- c #7094BD", + "h- c #7195BF", + "i- c #7399BF", + "j- c #739ABF", + "k- c #7398BE", + "l- c #6F95BA", + "m- c #7096BA", + "n- c #7097BB", + "o- c #739ABC", + "p- c #739CBD", + "q- c #759EBF", + "r- c #78A2BF", + "s- c #7CA7C2", + "t- c #10245A", + "u- c #7185BB", + "v- c #7286BC", + "w- c #7387BB", + "x- c #7487BC", + "y- c #6E90BC", + "z- c #6F93BE", + "A- c #7196BE", + "B- c #7297BE", + "C- c #7298BF", + "D- c #7297BD", + "E- c #6E94BA", + "F- c #6F95B9", + "G- c #6F96BA", + "H- c #7097BA", + "I- c #729ABB", + "J- c #749DBD", + "K- c #77A0BE", + "L- c #78A4C0", + "M- c #7AA5C0", + "N- c #7CA7C5", + "O- c #7185BA", + "P- c #7286BA", + "Q- c #7285BB", + "R- c #7386BB", + "S- c #7288BC", + "T- c #7589BD", + "U- c #6E91BC", + "V- c #6F91BC", + "W- c #6E92BC", + "X- c #6F93BD", + "Y- c #7194BD", + "Z- c #7195BD", + "`- c #7298BE", + " ; c #7297BC", + ".; c #6D93B8", + "+; c #6E94B8", + "@; c #6E95B9", + "#; c #7096B9", + "$; c #739CBC", + "%; c #76A0BE", + "&; c #78A1BF", + "*; c #79A1C1", + "=; c #79A4C2", + "-; c #7BA3C3", + ";; c #7084BA", + ">; c #7184BB", + ",; c #7085BB", + "'; c #7186BB", + "); c #7287BB", + "!; c #7388BB", + "~; c #6C8EBB", + "{; c #6D8FBB", + "]; c #6F92BC", + "^; c #7095BD", + "/; c #7196BD", + "(; c #7195BC", + "_; c #0E3359", + ":; c #6C92B7", + "<; c #6D94B8", + "[; c #6F96B9", + "}; c #6F97BA", + "|; c #7299BB", + "1; c #729BBB", + "2; c #749DBC", + "3; c #759EBD", + "4; c #759FBE", + "5; c #10235A", + "6; c #7083BA", + "7; c #7084BB", + "8; c #7085BA", + "9; c #7093BC", + "0; c #7095BC", + "a; c #6C91B7", + "b; c #6E95B8", + "c; c #7198BA", + "d; c #739BBB", + "e; c #78A2C2", + "f; c #0F225A", + "g; c #6F82B9", + "h; c #7083B9", + "i; c #6F83B9", + "j; c #6F83BA", + "k; c #7184BA", + "l; c #6C8DBB", + "m; c #6C8FBB", + "n; c #6D90BB", + "o; c #6E91BB", + "p; c #6F93BC", + "q; c #7094BC", + "r; c #6F94BB", + "s; c #0D3259", + "t; c #6A8FB6", + "u; c #6B91B7", + "v; c #6E93B8", + "w; c #7098BA", + "x; c #7099BA", + "y; c #7199BA", + "z; c #729BBC", + "A; c #6E81B9", + "B; c #6E82B9", + "C; c #6E90BB", + "D; c #6E93BA", + "E; c #698EB6", + "F; c #6A8EB6", + "G; c #6A90B6", + "H; c #6B90B6", + "I; c #6B92B7", + "J; c #6D93B7", + "K; c #6E96B8", + "L; c #6F97B9", + "M; c #7098BB", + "N; c #78A0C0", + "O; c #6E80B8", + "P; c #6E81B8", + "Q; c #6F81B8", + "R; c #6F81B9", + "S; c #6F82B8", + "T; c #6F92BB", + "U; c #6E92BA", + "V; c #0D3159", + "W; c #688DB4", + "X; c #698EB5", + "Y; c #698FB5", + "Z; c #6A90B7", + "`; c #6C93B7", + " > c #518FC8", + ".> c #6F96B8", + "+> c #123A5A", + "@> c #0F215A", + "#> c #6D80B8", + "$> c #6D81B8", + "%> c #6C8FBA", + "&> c #6E90BA", + "*> c #6E92BB", + "=> c #6E93BC", + "-> c #0C3159", + ";> c #678CB4", + ">> c #678DB4", + ",> c #688EB4", + "'> c #698EB4", + ")> c #6A90B5", + "!> c #6B91B6", + "~> c #6D94B7", + "{> c #6D95B8", + "]> c #6E96B9", + "^> c #0E2159", + "/> c #6C7FB8", + "(> c #6D7FB7", + "_> c #6D7FB8", + ":> c #507FC9", + "<> c #6D8FBA", + "[> c #6D91BB", + "}> c #6D91B9", + "|> c #0C3059", + "1> c #678AB3", + "2> c #678BB3", + "3> c #678DB3", + "4> c #6B91B5", + "5> c #6B92B5", + "6> c #6C92B6", + "7> c #6B92B6", + "8> c #6C93B6", + "9> c #0E2059", + "0> c #6C7EB7", + "a> c #6D7EB7", + "b> c #6E80B7", + "c> c #6E82B8", + "d> c #7084B9", + "e> c #6F84BA", + "f> c #6C90B9", + "g> c #668AB2", + "h> c #698FB4", + "i> c #6A90B4", + "j> c #719ABB", + "k> c #749BBC", + "l> c #6B7EB7", + "m> c #6B7DB6", + "n> c #6C7DB7", + "o> c #6C7FB7", + "p> c #6D80B7", + "q> c #6F84B9", + "r> c #7186BA", + "s> c #6C8EB9", + "t> c #6589B1", + "u> c #678CB2", + "v> c #678DB2", + "w> c #688EB3", + "x> c #6A8FB5", + "y> c #6A91B5", + "z> c #6C92B5", + "A> c #6C94B7", + "B> c #6D95B7", + "C> c #759DBD", + "D> c #0E1F5A", + "E> c #6A7CB6", + "F> c #6A7DB6", + "G> c #6C7EB6", + "H> c #7082B9", + "I> c #7285BA", + "J> c #7085B9", + "K> c #0E335A", + "L> c #658BB1", + "M> c #668BB1", + "N> c #668CB1", + "O> c #678CB1", + "P> c #688DB3", + "Q> c #688FB3", + "R> c #698FB3", + "S> c #6990B4", + "T> c #7097B9", + "U> c #7197BA", + "V> c #739CBE", + "W> c #0E1F59", + "X> c #6A7BB6", + "Y> c #6B7CB6", + "Z> c #6C7DB6", + "`> c #6B7DB7", + " , c #6F82B6", + "., c #648AB0", + "+, c #658AB1", + "@, c #658BB0", + "#, c #668CB2", + "$, c #688FB4", + "%, c #6B90B5", + "&, c #0D1E59", + "*, c #697BB5", + "=, c #6A7CB7", + "-, c #6B7EB6", + ";, c #6C7EB8", + ">, c #6E82B7", + ",, c #6389B0", + "', c #6489B0", + "), c #658AB0", + "!, c #668AB1", + "~, c #688DB2", + "{, c #6990B5", + "], c #697AB5", + "^, c #697BB6", + "/, c #6A7BB5", + "(, c #6D7EB8", + "_, c #6D81B6", + ":, c #6388AE", + "<, c #6388AF", + "[, c #6389AF", + "}, c #6489AF", + "|, c #668BB0", + "1, c #6979B4", + "2, c #6879B5", + "3, c #6879B4", + "4, c #6979B5", + "5, c #6A7AB5", + "6, c #6B7CB7", + "7, c #6D80B5", + "8, c #6287AE", + "9, c #6288AE", + "0, c #0D1D59", + "a, c #6878B4", + "b, c #6778B4", + "c, c #6C7EB5", + "d, c #6186AC", + "e, c #6186AD", + "f, c #6286AE", + "g, c #668DB1", + "h, c #678EB3", + "i, c #0D1C59", + "j, c #6777B3", + "k, c #6777B4", + "l, c #697AB4", + "m, c #6A7CB5", + "n, c #6E81B7", + "o, c #6084AC", + "p, c #6185AC", + "q, c #6086AD", + "r, c #6187AE", + "s, c #6387AE", + "t, c #0C1C59", + "u, c #6676B3", + "v, c #6776B3", + "w, c #6877B4", + "x, c #687AB5", + "y, c #697CB6", + "z, c #6A7DB7", + "A, c #6B7DB5", + "B, c #5F83AC", + "C, c #6083AC", + "D, c #6185AD", + "E, c #6288AD", + "F, c #668BB2", + "G, c #6A91B4", + "H, c #6675B3", + "I, c #6575B3", + "J, c #6677B3", + "K, c #6677B4", + "L, c #6A7DB4", + "M, c #5E82AB", + "N, c #5F83AB", + "O, c #5F84AC", + "P, c #6085AD", + "Q, c #6085AC", + "R, c #10365A", + "S, c #0C1B59", + "T, c #6575B2", + "U, c #6675B2", + "V, c #6676B2", + "W, c #6676B4", + "X, c #6776B4", + "Y, c #687AB4", + "Z, c #5D81AA", + "`, c #5D81AB", + " ' c #5D82AA", + ".' c #5E83AB", + "+' c #6083AB", + "@' c #6286AD", + "#' c #648AB1", + "$' c #6473B3", + "%' c #6474B2", + "&' c #697CB3", + "*' c #5D80A9", + "=' c #5E82AA", + "-' c #5F84AB", + ";' c #6287AD", + ">' c #6389AE", + ",' c #0B1A59", + "'' c #6473B2", + ")' c #6574B2", + "!' c #6574B3", + "~' c #6778B3", + "{' c #6779B4", + "]' c #687BB2", + "^' c #5B7FA9", + "/' c #5B80A9", + "(' c #5C80A9", + "_' c #5D81A9", + ":' c #5E81AA", + "<' c #6184AC", + "[' c #638AB0", + "}' c #678DB1", + "|' c #0B1A58", + "1' c #6372B1", + "2' c #6472B2", + "3' c #6472B1", + "4' c #6473B1", + "5' c #6573B2", + "6' c #6576B3", + "7' c #6878B3", + "8' c #687AB3", + "9' c #5B7EA7", + "0' c #5B7FA8", + "a' c #5B80A8", + "b' c #5C81AA", + "c' c #5F82AB", + "d' c #668DB2", + "e' c #11268E", + "f' c #11268D", + "g' c #0B1958", + "h' c #6373B2", + "i' c #6877B3", + "j' c #6879B2", + "k' c #0C2F59", + "l' c #5A7DA7", + "m' c #5A7EA7", + "n' c #5B7FA7", + "o' c #5B7EA8", + "p' c #5C7FA8", + "q' c #5F83AA", + "r' c #6084AB", + "s' c #6589B0", + "t' c #0C196C", + "u' c #152064", + "v' c #11258D", + "w' c #6371B1", + "x' c #6271B1", + "y' c #6372B2", + "z' c #6778B1", + "A' c #0C2E59", + "B' c #597CA6", + "C' c #5A7CA6", + "D' c #597DA6", + "E' c #5D80AA", + "F' c #5E83AA", + "G' c #0E355B", + "H' c #0C1A6F", + "I' c #131E62", + "J' c #414C90", + "K' c #10258E", + "L' c #6271B0", + "M' c #6272B1", + "N' c #6373B1", + "O' c #6677B1", + "P' c #0B2E59", + "Q' c #577BA5", + "R' c #587BA6", + "S' c #587CA6", + "T' c #5A7EA8", + "U' c #5D82AB", + "V' c #6289AE", + "W' c #1A5FA3", + "X' c #0D1C75", + "Y' c #131E61", + "Z' c #404B8E", + "`' c #475296", + " ) c #10258D", + ".) c #6270B0", + "+) c #6272B0", + "@) c #6576B2", + "#) c #6576B1", + "$) c #577AA4", + "%) c #577BA4", + "&) c #587BA5", + "*) c #5C81A9", + "=) c #648AAF", + "-) c #0E345B", + ";) c #0D1D79", + ">) c #101B5F", + ",) c #3C478B", + "') c #11258E", + ")) c #6371B0", + "!) c #6575B1", + "~) c #0B2E58", + "{) c #5679A3", + "]) c #5779A4", + "^) c #567AA4", + "/) c #577AA5", + "() c #577BA6", + "_) c #597BA6", + ":) c #175797", + "<) c #0E1F82", + "[) c #101B5E", + "}) c #384487", + "|) c #0B1858", + "1) c #6170B0", + "2) c #6575B0", + "3) c #5578A2", + "4) c #5678A3", + "5) c #5679A4", + "6) c #5A7DA6", + "7) c #102394", + "8) c #111C60", + "9) c #141F63", + "0) c #293578", + "a) c #0A1859", + "b) c #616FAF", + "c) c #616FB0", + "d) c #6270AF", + "e) c #6372B0", + "f) c #6474AF", + "g) c #0B2D58", + "h) c #5576A2", + "i) c #5577A2", + "j) c #5577A3", + "k) c #5578A4", + "l) c #5578A3", + "m) c #587CA5", + "n) c #597DA7", + "o) c #5D82A9", + "p) c #6187AD", + "q) c #154F8D", + "r) c #1227A5", + "s) c #0D185C", + "t) c #1B266A", + "u) c #313C80", + "v) c #445093", + "w) c #10248D", + "x) c #6170AF", + "y) c #6474B1", + "z) c #6373AF", + "A) c #5375A1", + "B) c #5476A2", + "C) c #587AA4", + "D) c #5C7FA9", + "E) c #6084AD", + "F) c #102393", + "G) c #0C196A", + "H) c #0E1A5D", + "I) c #242F73", + "J) c #3A4589", + "K) c #626FAF", + "L) c #626FB0", + "M) c #6475B2", + "N) c #6272AE", + "O) c #0A2C59", + "P) c #5275A1", + "Q) c #5376A2", + "R) c #5477A2", + "S) c #144A84", + "T) c #0E1E7F", + "U) c #121D61", + "V) c #2B3679", + "W) c #0A1858", + "X) c #6370B0", + "Y) c #6271AF", + "Z) c #0A2C58", + "`) c #5173A0", + " ! c #5274A1", + ".! c #5274A0", + "+! c #0D1B74", + "@! c #10248E", + "#! c #6171AE", + "$! c #51729F", + "%! c #51739F", + "&! c #5273A0", + "*! c #5476A1", + "=! c #5576A3", + "-! c #114379", + ";! c #102291", + ">! c #0C196B", + ",! c #1F2A6E", + "'! c #364185", + ")! c #465195", + "!! c #10238D", + "~! c #616EAF", + "{! c #616FAD", + "]! c #0A2B59", + "^! c #50729E", + "/! c #50729F", + "(! c #5375A0", + "_! c #134C8D", + ":! c #0E1E81", + "~ c #323D81", + ",~ c #0A2D5D", + "'~ c #1354AE", + ")~ c #10228F", + "!~ c #212C70", + "~~ c #374286", + "{~ c #0F228B", + "]~ c #1251A7", + "^~ c #283377", + "/~ c #3E4A8D", + "(~ c #0F4085", + "_~ c #0B2F62", + ":~ c #1328A8", + "<~ c #0D1C77", + "[~ c #303B7F", + "}~ c #104289", + "|~ c #0B3063", + "1~ c #112497", + "2~ c #1C276A", + "3~ c #0C175B", + "4~ c #0F438A", + "5~ c #0B3266", + "6~ c #0F2088", + "7~ c #0C3367", + "8~ c #365885", + "9~ c #0B3369", + "0~ c #0E1D7C", + "a~ c #0B2F60", + "b~ c #0C366E", + " ", + " ", + " ", + " ", + " ", + " . . ", + " . . . . . . . ", + " . . . . + @ # $ % . . . . ", + " . . . . & * + @ = - % ; > , . . . . ", + " . . . . ' ) ! * + @ ~ { ] % ^ , / ( _ . . . ", + " . . . . : < [ } | * + 1 = $ 2 ; > , / 3 4 5 6 . . . . ", + " . . . . 7 8 9 0 a } | * + b c d 2 % > , / e f g h i j k . . . . ", + " . . . . l m n 9 o p q } | * r s t # u % v w x y z A B i C D E F G . . . ", + " . . . . H I J K L M 9 < q ' | N O P t # Q % R S T / U V W X Y D Z ` ...+.. . . . ", + " . . . . @.#.H I $.%.L M 9 < q } } N r + @ # Q &.R S *.=.f V W X -.;.k ` ...+.+.>.,.. . . . ", + " . . . . '.'.'.#.H ).!.~.n {.].< 0 ' } | ^.b @ # Q ] /.(., / f 4 5 B i D k _. ...:.<.[.,.}.|.1.. . . . ", + " . . . . '.'.'.'.'.2.H 3.J %.m 7 9 < p } ) 4.5.+ @ ~ Q Q 6.> , / 3 4 7.X 8.9.k 0. ...a.b.<.c.d.|.1.e.f.g.. . . ", + " . . . . . '.'.'.'.'.'.'.h.i.j.$.$.m 7 k.< l.a m.| * + @ c Q Q % > , / f f n.B i j k _.o.p.q.+.r.c.s.|.t.u.v.w.x.y.. . . . ", + " . . . . . '.'.'.'.'.'.'.'.z.A.B.I $.m 7 9 ].C.q D.| * + P t # Q 6.> (.T f E.F.6 i j G.0.H.I...+.<.c.d.J.K.L.M.N.x.y.O.P.Q.. . . . ", + " R.. . '.'.'.'.'.'.'.'.'.'.'.S.A.3.I $.m n k.T.U.q V.W.X.+ + t # Q % /.S Y./ z F.W i -.Z.k _.`.a.+.<.r.s. +|.L.M.N.g..+++O.@+#+$+%+. . . ", + " . . . . . '.'.'.'.'.'.'.'.'.&+i.H I $.K n 8 < < q } *+* ^.=+@ { Q ] -+, , ;+_ V W B 8.>+,+_.G ..+.'+c.d.d.t.)+!+M.x.x.~+{+Q.#+$+]+^+/+. . . . ", + " . . =.f . . . . . '.'.'.'.'.'.2.A.H (+$._+:+<+].< q } | N ^.s [+# Q }+6.> *./ f V 5 |+C D 0._. ...q.1+2+3+4+|.1.!+N.5+y.6+O.Q.#+$+7+8+/+/+/+/+. . . . ", + " . . / / y e . . . . . '.'.'.'.&+#.H I 9+0+m a+9 b+l.} m.X.5.+ @ = $ % > c+Y./ f U W X d+k k _. ...e+f+g+c.d.J.)+h+i+j+x.6+k+@+l+m+n+o+/+/+/+/+/+/+/+. . . . ", + " . . S , *.x T / / 3 . . . . . '.'.#.p+q+!.~.m 7 9 < 0 ' } r+* P @ # { s+% w , / e V 5 B -.j k _. .t+e+1+2+3+d.|.K.u.u+N.x..+O.v+w+x+y+z+/+/+/+/+/+/+/+/+/+/+. . . ", + " . . ; > > ^ , A+/ / e y ( . . . . . i.).B+$.C+7 9 : l.a } | * + D+t d Q % > , / f E+5 6 d+j k _.o.p.F+<.r.c.d.J.)+h+!+G+x.H+O.@+@+$+y+%+/+/+/+/+/+/+/+/+/+/+/+/+. . . . ", + " . . 2 % % /.-+> ^ S , Y.I+/ =.e . . . . . I $.C+L 9 J+K+a } & 4.+ P c d Q v L+S / f f g |+i j k ,+H.t+:.+.<.c.s.M+N+O+i+N.x..+~+@+#+P+]+%+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+. . . . ", + " . . # $ Q 2 s+6./.> > , , , , / / ;+y . . . . . 7 a+< U.Q+} | N R+P S+# u % > , / ( f V W i -.Z._.T+I...+.U+c.V+W+t.O+!+N.x.y.++X+@+#+$+%+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+/+. . . . ", + " . . @ t # Y+Y+Q Q &.% v v > c+c+S , , / =.( f . . . . . < q Z+) X.^.D+c Q Q % /., Y./ 4 V W i d+k Z T+`.q.+.<.r.d.}.`+)+!+N.x. @.+k+@+#+x+%+z+/+/+/+/+/+/+/+/+/+/+/+/+/+. . . . f f . ", + " . . + D+@ c # # $ Q Q Q }+% % /./.S S , *.T / ;+;+f . . . . . m.4.5.b @ # Q % R > , e f V W i 8.j k _.G ..+.'+<.c.4+`+)+h+v.w.y..@O.@+#+x+n++@/+/+/+/+/+/+/+/+/+/+/+. . . . f f f f f . ", + " . . . ^.+ + P b @ c t # - - u u &.% /.R c+c+S , A+/ / y f . . . . . 5.+ @ # Q ] /.L+A+=.f V W |+@@9.k _. .t++.b.g+c.W+|.1.!+u+j+#@~+O.@+l+#+$@8+/+/+/+/+/+/+/+/+/+. . . . f f f f f f f f . ", + " . . | N * * + + =+=+D+@ S+# %@%@- Q 2 % 6.v /.> , , , / T y f f . . . . . # Q % % > , / f E+V B -.9.k _.o.p.F+f+r.c.d.|.&@O+u+N.x..+++@+*@#+]+%+/+/+/+/+/+/+/+. . . =@f f f f f f f f f f -@. ", + " . . . W.D.| | 4.* 5.r + P b @ S+# # Y+- Q Q }+% ; -+> w w S , I+/ / 3 f . . . . . ; > , A+f V g ;@i j k _.>@`...+.r.c.d.|.&@,@'@G+x..+O.)@@+x+$+z+/+/+/+/+/+. . . T / ( y f f f f f f f f f f . . ", + " . . . a } V.} W.| r+4.* ^.* + D+@ @ @ # # - Q Q ] 2 ; /.-+> w (., A+Y./ f f . . . . . , / / U V X @@j k Z .t+a.+.<.c.d.|.K.O+!+G+x.y.!@P.Q.~@$+8+/+/+/+. . . ; > w ^ , T / 3 f f f f f f f f f . . ", + " . {@. . . . Z+} ) D.m.| r+* * 5.r + D+@ S+c # # - Q Q s+% % -+> c+(., , / / / / f . . . . . f g B i j k _._.`.q.+.<.c.]@ +1.O+!+N.x..+!@)@^@#+$+%+%+. . . # - Q % % % > L+w Y./ / / f f f f f f f . . ", + " . /@(@_@:@. . . . [ } } W.| N ! * * R++ + @ @ c c # # # Q u 2 % % ; > L+, , *./ / =.;+f . . . . . i j 9.E >@G ..+.'+c.<@4+&@)+!+N.G+.+O.O.l+#+@+. . . O s @ c c $ Q Q 2 % % > c+, I+x ;+f f f f f f . ", + " . [@/@}@/@/@/@. . . . q } } W.V.! ! N * + O + b b [+t c # $ Q u 2 ] % v R > > ^ *.A+I+/ e f f . . . . 9.E _. ...+.'+[.V+}.|.)+!+v.x..+6+{+.+. . . *+X.r+* r + s @ S+c { - Q s+% > > (., *./ f f f f f . ", + " . |@|@|@1@}@2@/@}@/@. . . . Q+[ } W.| N & * * R++ D+=+@ S+~ # # %@Q Q ] % R -+> ^ , w , , / =.;+f . . . . . ...3@<.4@3+}.|.)+!+u+w.x.)+. . . 0 q } [ V.| & 4.+ R++ b @ # # Q Q &.% 6.> > , A+x ( f f . ", + " . 5@5@6@|@7@|@|@8@}@}@2@/@. . . . ' } ) | | X.| * * ^.+ P 1 @ @ ~ # d Y+Q Q % % v > v w , , *.T / =.f f . . . . . 9@2+,.d.|.t.!+!+b.. . . <+9 o b+p 0 q Z+} ) | N * O + s @ t = d - Q % % -+> ^ , *./ . ", + " . 0@5@a@a@b@c@6@7@|@|@[@8@|@8@. . . . . } ) W.W.N & r+* * r + s 1 @ # # # { - Q u % % ; ; > c+, , , I+/ ( f f . . . . . d.M+t.j . . . $.0+m :+7 M 9 9 < U.q Z+} ) *+& 4.* O + @ @ c # { - }+% % R ^ , . ", + " . d@e@f@f@g@5@5@h@b@5@h@|@b@|@|@|@|@. . . . Z+} } | | & X.r+5.i@+ s D+@ [+t # # Q Q 2 &.% % /.R > c+, , T T / y f . . . . . . . . H 3.I !.$.%.~.C+7 {.a+T.: C.< q q } D.| r+* ^.+ =+1 @ c # - Q s+% v . ", + " . j@k@d@d@d@d@g@f@e@d@l@5@5@a@b@6@c@6@|@|@. . . . [ } } m.| | r+* * + + =+D+@ ~ ~ # m@n@n@Q &.% % v > c+(., , Y./ / =.f . . . '.h.#.#.o@H I I J $.K m C+7 M M ].< U.p q ' D.m.| ! * O + @ @ c @ d d 2 . ", + " . p@q@q@r@r@r@j@s@d@d@f@e@5@l@0@5@5@b@b@c@7@7@7@. . . . } V.V.) | & * * * + s t@u@v@v@w@m@n@x@y@u % v % > > (., , *.x / Y.. . z@A@B@'.2.#.#.p+j.I !.$.C@_+:+C+<+8 9 o < U.0 D@} W.m.| r+* O + s @ t # . ", + " . E@E@F@F@G@q@q@H@H@I@d@H@d@d@d@d@5@f@5@5@5@5@5@c@h@. . . . [ Z+) ) m.| 4.J@K@L@M@N@O@v@w@m@P@x@Q@R@S@] % 6.6.> > w , , . . T@U@V@W@X@Y@X@h.2.#.#.H B.I !.$.$._+Z@7 7 9 T.o < l.q q } } | | X.^.r D+=+. ", + " . `@E@E@E@E@F@G@ #p@q@q@q@q@d@k@d@H@d@d@g@f@g@d@5@0@5@5@6@. . . . Z+} .#+#@###$#L@%#M@O@O@&#P@P@n@n@*#=#S@&.}+% 6.-+> > . . -#T@;#U@>#z@,#X@Y@'#@.#.)#!#H I I I $.K m 7 7 7 9 9 T.< q q } } D.r+4.* 5.. ", + " . ~#~#~#{#~#E@E@E@]#G@^#^# #p@q@q@q@j@k@j@k@d@d@d@d@0@0@e@l@0@5@/#(#_#:#<#.#[#}#K@K@L@M@|#O@#2#3#4#4#x@R@5#6#% }+v . . 7#8#9#0#a#U@T@U@U@,#b#X@c#'.#.#.)#H 3.I l $.$.m m 7 7 M 9 9 < 0 q D@} *+| | . ", + " . d#e#f#~#~#~#g#h#{#{#E@i#E@E@q@F@p@]#q@q@q@q@q@j@d@k@d@d@d@j#k#l#m#n#o#:#:#p#+#}#q#$#$#$#M@M@u@v@1#r#s#3#t#u#v#5#w#x#. . y#z#A#B#C#D#a#E#F#G#V@z@H#X@I#'.@.#.#.H H B.I $.$.0+K :+7 M M : : < q q ' } . ", + " . J#K#K#K#K#L#~#~#~#~#~#~#{#{#i#E@E@^#]#G@]#q@q@^#q@I@H@M#M#N#O#k#k#l#P#Q#_#R#:#S#[#[#J@$#$#T#T#u@U#1#1#P@3#t#t#u#V#W#X#Y#Z#`# $y#z#7#7#D#D#-#.$U@U@+$z@@$H#'#'.2.2.#.p+j.I I l $.%.m m 7 7 9 ].< C.q . ", + " . #$J#K#$$$$%$K#K#L#K#L#f#~#f#~#~#`@`@E@E@E@E@E@E@q@&$*$=$=$-$-$;$O#>$>$,$,$Q#:#:#S#S#'$q#J@$#$#T#)$!$U#1#1#s#~$t#t#{$W#]$^$^$/$($`#_$y#:$7#9#D#<$a#E#U@+$[$b#X@X@A@'.2.#.p+H H B.!.!.~._+m }$7 a+9 ].. ", + " . =@|$#$#$#$1$2$K##$%$K#K#L#e#f#e#~#L#~#~#~#{#3$4$5$6$7$7$8$&$*$-$-$9$9$>$0$0$P#P#Q#S#S#'$'$'$a$b$T#c$!$!$#2#3#d$e$f$g$]$^$^$h$/$i$`#j$k$y#7#7#l$<$m$E#;#U@+$z@z@H#'.'.@.#.#.i.H q+I J $.K K :+C+7 . ", + " . n$o$p$=@o$q$#$q$#$#$#$J##$$$%$$$K#K#e#e#r$s$s$t$4$5$5$u$v$7$w$w$w$-$x$9$>$>$0$y$P#z$A$B$'$'$'$C$b$T#D$D$!$E$#F$F$G$H$I$J$K$^$L$Y#h$($($`#`#y#y#M$7#C#D#E#E#N$U@>#z@b#X@'#'.z.#.#.p+H j.!.O$K 0+m . ", + " . P$P$n$=@=@=@=@p$p$o$#$#$#$#$#$Q$K#Q$R$R$S$T$r$s$t$t$U$5$6$V$6$w$w$W$x$-$X$;$>$0$y$y$z$z$Y$Z$`$'$ % %.%T#T#!$u@+%@%#%$%%%&%*%=%J$K$-%;%>%>%($,%'%`#`#y#z#7#)%D#0#E#;#N$U@z@,#@$X@Y@'.S.#.o@H I j.I J . ", + " . !%!%~%P${%!%n$!%=@=@=@=@=@p$#$]%^%/%/%/%(%S$T$_%:%s$<%t$5$5$5$6$u$7$w$w$-$X$X$>$[%>$y$y$Y$Y$A$`$}%C$ %.%.%|%1%+%+%2%3%4%$%%%*%5%I$J$K$^$;%>%6%7%,%8%9%0%k$:$7#8#8#D#a%E#.$U@[$z@z@X@Y@Y@S.@.#.A.H q+. ", + " . b%b%c%b%d%d%!%!%!%e%f%e%n$g%g%g%^%h%h%i%j%/%R$R$T$_%:%s$k%U$U$4$6$u$V$w$7$w$x$X$X$l%l%0$m%n%z$A$o%`$p%q%.%.%|%r%+%s%2%2%3%t%u%v%*%5%J$]$w%L$x%y%6%7%,%'%z%A%`#y#B%7#l$l$D#E#E#T@[$C%z@H#X@A@I#z.#.A.. ", + " . D%E%F%F%b%b%G%b%~%~%H%I%I%I%J%J%J%g%I%I%g%]%(%R$_%R$T$T$K%k%K%U$L%M%N%u$w$O%P%Q%X$X$X$[%R%0$n%n%z$Y$Z$Z$S%q%T%|%U%V%W%s%X%3%t%4%u%v%*%Y%J$J$Z%;%;%y%y%`%8% &9%.&`#+&y#7#7#B#D#0#E#E#U@U@@&#&W@X@B@&+. ", + " . $&E%E%E%%&E%&&b%b%*&=&-&;&I%;&I%>&I%I%I%I%g%h%]%/%,&_%_%'&'&k%k%L%U$M%)&N%u$!&O%x$x$W$X$~&~&{&{&0$]&z$Y$Z$Z$T%.%^&/&U%W%s%(&2%_&3%:&u%v%<&[&}&K$K$;%>%y%y%,%8%9%9%z%|&`#y#:$7#7#C#m$E#E#.$U@U@z@@$X@. ", + " . 1&$&E%2&3&4&5&6&6&*&7&6&=&=&*&=&>&-&8&-&I%I%J%g%h%]%,&(%R$_%9&9&'&k%0&L%)&)&u$v$O%!&W$P%W$X$~&~&[%a&z$Y$b&o%c&.%d&d&e&/&U%W%s%X%2%3%t%f&f&g&<&<&h&i&Z%Z%j&y%y%k&l& &m&.&|&n&0%y#z#A#)%D#<$E#;#T@>#@&. ", + " . o&1&1&1&p&p&q&q&p&6&6&6&6&6&*&*&r&8&7&=&8&8&I%;&s&g%]%]%]%,&,&_%9&'&:%K%L%L%U$t&)&!&!&!&W$P%u&v&~&[%{&{&a&Y$w& %T%T%x&y&y&U%z&A&A&B&C&D&D&E&v%v%v%F&G&i&Z%H&x%I&J&K&8%9%m&.&|&L&M& $y#M$7#8#D#m$a%U@. ", + " . N&o&O&O&P&Q&q&q&p&6&q&q&6&6&5&6&6&6&6&=&7&*&*&=&;&;&g%R&^%^%,&,&S&T&T&U&U&k%L%L%M%N%V&W&O%!&X&u&v&Y&v&{&m%m%Z&C$Z&`&c&x&x&y&y& *.*+*s%2%C&D&t%@*f&v%<&#*G&$*%*H&x%y%`%l&8%9%m&A%&*n&**=*y#y#7#7#9#m$. ", + " . -*-*P&O&P&P&P&P&O&p&;*Q&p&p&p&q&q&5&6&q&6&6&6&6&*&=&=&>*s&s&,*'*,&)*S&T&T&!*k%k%L%M%M%V&t&!&O%P%P%X&Y&v&v&{&~*'$~*{*]*]*c&x&x&^*^& *+*/*(*_*D&t%:*E&<*[*F&#*G&$*%*}*I&I&`%K& &9%m&&*|*n&**1* $y#2*2*. ", + " 3*4*5*-*-*-*-*O&-*P&O&P&6*P&P&Q&p&p&q&p&6&5&q&6&6&6&q&6&*&7*7*>*s&,*8*8*9*S&T&U&0*U&a*b*M%M%V&W&W&c*c*X&X&Y&d*e*'$f*g*h*{*]*i*j*x&x&k* * *l*(*(*_*C&m*@*f&n*[*F&#*G&o*%*}*p*q*r*K&s*t*m&&*|*u*v*w*x*k$. ", + " 3*y*z*4*4*A*-*-*-*-*-*-*P&P&P&O&P&p&p&P&P&;*B*p&p&q&q&q&6&6&7&=&C*7*,*8*8*8*9*T&D*E*!*a*a*F*G*H*V&I*W&W&c*J*~*K*'$L*M*f*g*h*N*]*i*c&x&^*y&O* *l*P*(*Q*R*m*S*E&<*T*F&U*G&V*W*X*X*q*r*l&s*Y*Z*`*|***** =.= ", + " +=y*@=@=#=4*4*4*5*$=%=-*-*-*-*O&O&P&-*P&P&Q&Q&P&q&p&;*q&q&q&7&q&7&C*s&,*,*'*8*9*&=*=D*!*U&a*F*G*M%V&t&W&W&==c*I*S#-=;=L*>=,='=)=h*]*i*j*j*^*O*!=~=/*{=]=Q*^=S*S*<*<*T*F&/=o*(=W*X*X*J&r*_=s*:=`*I$<=-%.= ", + " +=[=}=}=[=y*@=y*z*|=z*4*1=1=-*$=-*%=-*-*-*P&P&-*P&P&P&Q&p&q&q&p&q&7&7&C*7*,*,*8*8*D*&=D*!*!*a*a*a*F*G*V&2=W&3=4=:#5=-=-=6=7=>=8=9=h*0=a=b=c=^*O*O*!=l*d={=e=R*R*R*S*E&n*F&U*U*f=o*W*g=X*h=r*G$G$&%H$I$i= ", + " j=k=l=m=l=}=}=}=}=[=@=#=@=4*#=n=1=$=o=-*1=p=-*-*P&q=-*q=-*P&O&O&;*q&6&r=C*7*7*s=t=u=&=v=9*D*0*0*a*a*F*w=w=w=2=x=:#y=z=A=B=B=6=8=C=D='=h*N*E=F=F=G=^*H=H=l*d={=I=e=R*J=K=L=L=M=N=U*o*O=P=4%Q=R=R=G$*%H${$ ", + " j=S=T=k=U=k=V=k=k=}=m=[=[=@=@=y*y*@=n=W=1=X=X=$=-*-*p=-*-*-*q=-*-*O&;*;*7&7&7&C*t=t=Y=v=&=&=*=D*D*0*a*Z=G*`= -5=_#.-+-@-#-$-%-B=&-6=7=,=*-N*=-E=----;-O*O* *l*>-d=]=,-J='-'-L=n*N=)-B&X%X%_&_&!-Q=R=R=~- ", + " j={-S={-S=S=T=S=T=k=k=V=k=V=l=[=[=[=y*y*y*n=n=o=o=n=$=p=1=%=%=$=-*-*P&O&;*;*]-r=r&C*^-t=t=/-&=/-0*0*0*0*Z=`=F*(-_-:-<-.-[-#-A=}-$-|-&-1-2-*-3-4-0=E=5-6-;-f$7-8-l*d=9-]=,-J=0-U%a-z&W%W%s%2%2%2%_&3%4%F$ ", + " b-c-c-c-d-{-d-{-S=S=S=S=T=T=k=k=V=k=V=}=[=[=y*@=n=o=e-n=1=o=1=$=p=%=-*-*P&O&;*r=]-7&f-g-t=t=Y=h-&=D**=0*0*i-j-k-(#l-m-n-.-[-@-#-o-$-p-B=&-q-2-2-3-4-r-E=5-;-G=f$H=s-8-d=x&x&y&y&q%U%U%z&W%s%s%X%2%_&2%F$ ", + " t-c-u-c-c-c-v-d-{-w-{-w-S=x-S=S=S=U=k=l=k=k=V=}=}=[=e-e-e-W=e-e-o=o=n=$=$=-*y-O&;*]-]-z-C*g-^-Y=Y=A-&=B-B-C-i-D-n#E-F-l-G-H-.-.-@-I-}-}-B=J-1->=2-3-K-4-r-5-L-6-M-f$]*]*i*c&j*x&^*^*y&e&q%z&z&N-A&s%B&+% ", + " t-O-u-u-u-u-P-Q-c-c-c-v-c-R-{-S-{-S=S=T=S=k=S=k=k=T-}=m=}=e-e-e-e-e-n=1=n=$=$=y-U-6*V-W-X-f-f-g-Y-Z-A-/-B-B-`- ;/#.;+;@;l-#;H-H-.-[-I-I-}-$;p-J->=>=2-%;4-&;r-,=3-,={*Z&]**;a=i*c&=;x&-;y&!=/&U%a-+*s%+% ", + " t-;;>;,;;;O-u-u-';Q-u-Q-Q-P-c-d-);d-);w-!;S=S=U=T=T=k=T=k=V=}=l=e-~;e-e-e-n=$={;y-$=y-U-V-];]-f-f-Y-^;Z-/;/;/;(;_;:;.;.;<;@;l-[;};n-.-|;I-1;}-$;2;J-3;4;%;6=6=6=8=8=,=3-3-h*]*a=a=i*c&j*x&x&^&!=y&U%+*+% ", + " 5;6;;;;;;;;;;;7;8;O-u-O-O-';';';';Q-c-c-);{-);w-{-{-S=k=S=U=T=k=V=k=m=e-e-~;e-e-{;1={;U-U-];V-];9;9;g-Y-^;^;/;0;_;a;a;:;.;.;b;@;[;G-H-c;[-|;I-d;$;$;J-o-}-B=B=|-&-6=8=8=,=9=3-h*0=]*`&e;a=c&x&d&^&^&y&|% ", + " f;g;g;h;h;i;j;j;;;;;8;>;k;O-u-u-O-c-u-u-';c-c-c-);c-d-w-w-S=S=S=S=T=U=k=k=V=l;l;~;m;e-1=n;o;V-U-];p;9;g-q;g-0;r;s;t;u;u;:;:;.;v;+;b;[;};w;w;x;y;[-|;|;I-o-z;}-$-%-B=&-6=6=8=8=,=*-h*h*0=]*]*i*j*j*j*x&|% ", + " f;A;B;g;g;g;g;g;h;j;6;6;;;k;;;;;O-;;O-k;u-u-c-Q-';';';c-v-);c-w-);d-S=S=U=S=S=T=S=l;~;m;{;{;C;y-y-V-];9;p;p;g-D;s;E;F;G;H;I;:;:;J;<;<;K;[;L;L;H-H-M;y;[-|;I-I-}-}-$-%-p-B=6=6=C=7=8=,=)=h*N;0=]*]*i*c&|% ", + " f;O;P;Q;R;P;Q;S;g;g;g;g;g;i;j;j;;;;;;;;;7;k;O-O-u-u-u-P-';P-u-c-c-);d-w-S={-S=!;S=S=U=k=~;{;{;n;y-o;U-T;p;p;p;U;V;W;X;Y;Y;G;Z;u;:;`;J; >b;b;.>[;};H-n-n-c;[-[-I-#-#-o-$-$-p-|-&-6=6=q-8=8=9=9=h*h*0=]*+> ", + " @>O;#>O;$>#>O;P;P;P;P;S;g;g;g;g;i;h;j;6;6;6;;;;;;;,;>;k;u-u-u-u-';u-';c-c-c-);{-);{-{-S-S=S=U=%>{;&>&>o;*>W-=>U;->;>>>,>'>Y;)>)>!>`;J;~>~>{>{>K;@;]>[;G-H-M;w;y;|;[-@-I-d;z;$-p-B=B=|-6=6=M*8=9=9=h*h*.% ", + " ^>/>(>_>(>_>_>O;O;O;$>O;A;A;P;P;S;R;g;g;i;i;i;6;6;;;;;;;;;k;k;u-O-u-O-u-Q-u-c-c-';c-c-);w-d-S=S-S-:><>n;[>[>o;}>|>1>2>3>W;'>'>)>4>5>6>7>8>J;~>~>{>{>b;K;[;[;H-H-.-c;y;[-[-#-#-}-}-p-$-B=|-&-6=>=8=,=,=.% ", + " 9>0>0>0>a>a>(>/>_>_>#>b>O;O;P;$>P;P;P;Q;g;S;c>g;g;h;i;d>j;6;e>;;;;;;k;u-;;O-u-O-Q-Q-';c-';c-);););{-S-S=n;n;[>f>|>g>g>2>'>h>h>h>h>i>)>!>7>7>8>`;`;~>~>b;b;K;.>[;[;m-w;w;c;.-[-j>I-#-z;}-k>B=B=B=&->=>=.% ", + " 9>l>m>n>0>0>0>0>a>o>(>(>_>_>p>#>p>O;O;P;P;Q;P;Q;g;g;g;g;i;g;i;h;e>j;q>;;;;;;8;7;8;>;u-O-u-u-O-';r>c-c-););w-);s>|>t>u>v>3>w>,>,>'>h>x>)>i>y>5>z>7>`;`;A>~>B>{>K;b;[;L;};H-<-w;c;[-|;#-I-#-}-}-$-B=B=C> % ", + " D>E>F>m>m>m>G>0>0>n>0>0>0>o>_>o>(>_>_>#>#>#>O;#>P;P;P;A;P;B;g;B;g;i;H>i;i;6;;;h;;;;;8;7;O-O-O-u-u-Q-I>';Q-c-c-J>K>L>M>N>O>u>v>w>P>w>Q>R>S>h>)>)>4>!>7>6>`;`;J;~>b;{>b;.>K;[;T>H-U>c;c;[-|;@-#-#-$-}-V> % ", + " W>X>E>E>m>Y>m>Y>Z>`>n>0>0>0>l>0>0>0>(>0>(>/>(>_>_>#>#>O;P;P;P;P;A;B;A;R;g;g;g;j;i;i;i;;;;;;;;;;;8;k;8;O-u-O-u- ,K>.,+,@,M>M>#,u>v>3>P>,>Q>$,$,h>)>i>%,4>6>7>6>`;J;~>~>+;b;]>]>[;L;H-w;w;y;.-[-|;#-#-o-'$ ", + " &,X>X>*,X>X>X>X>E>E>=,m>`>m>`>l>-,l>n>0>0>0>;,;,a>o>(>_>_>O;P;#>#>P;O;P;c>B;P;c>B;g;i;i;i;6;;;e>6;;;7;;;,;O-O->,_;,,',.,),!,L>N>N>#,v>v>~,w>w>w>h>h>{,i>y>4>4>7>6>:;`;J;~>{>{>b;]>.>[;L;w;w;c;[-.-[-#-'$ ", + " &,],],],],^,*,],/,X>X>E>E>E>Y>m>m>m>`>n>`>l>G>l>0>0>0>(,;,o>(>_>(>#>O;p>#>#>P;P;P;c>A;S;g;g;g;g;i;j;i;;;;;6;;;_,s;:,<,[,},.,),+,|,L>M>N>#,v>v>P>w>w>$,h>h>S>)>)>4>7>7>6>`;A>~>{>b;b;b;]>.>[;L;w;H-c;[-'$ ", + " &,1,2,3,4,2,],],],5,^,^,X>X>E>E>E>F>6,E>Y>`>`>l>`>`>`>n>l>0>o>a>_>(>_>_>_>#>p>O;O;P;$>P;P;P;g;c>g;c>g;g;i;q>i;7,s;8,8,9,:,[,[,',),.,),M>N>N>N>v>v>v>w>w>,>Q>$,Y;i>)>y>!>5>6>:;`;`;~>{>{>b;K;[;[;};H-U>'$ ", + " 0,a,a,b,a,3,3,2,3,4,],],],*,^,],^,X>X>E>E>E>E>E>6,`>m>m>`>Z>G>`>l>0>0>0>a>_>(>p>(>#>#>#>$>P;#>P;P;P;P;P;B;g;i;c,s;d,e,f,8,:,<,:,[,[,',',),@,M>N>M>#,g,v>h,P>w>'>$,h>S>)>y>y>4>6>7>`;`;~>B>b;b;b;]>[;L;S# ", + " i,j,k,k,b,j,b,a,3,3,3,2,l,3,],l,],],*,/,/,X>m,E>X>Y>Y>=,F>F>F>m>-,-,l>n>`>0>0>0>o>o>o>p>(>#>p>#>#>b>O;P;n,P;c>c,V;o,p,q,e,r,r,s,9,9,<,,,},.,.,+,L>M>M>N>O>v>v>3>w>Q>'>h>h>{,)>y>4>4>6>6>`;`;~>~>b;b;b;S# ", + " t,u,u,j,v,k,k,j,k,a,w,a,b,a,a,3,1,4,x,],l,],],/,X>X>^,y,E>Y>=,=,m>z,`>`>m>l>n>n>l>0>G>0>0>o>(>/>_>#>#>#>O;$>$>A,V;B,C,o,p,D,e,e,r,8,E,9,<,[,},',.,.,!,M>M>F,O>O>~,3>P>w>$,Q>h>S>Y;G,y>4>7>6>8>`;J;~>~>:# ", + " t,H,I,u,u,J,u,j,j,K,k,j,j,k,k,b,b,a,a,3,1,4,4,],4,],],/,/,^,X>/,*,E>E>E>E>`>m>`>`>`>`>m>0>-,0>0>0>0>(>/>_>/>p>L,|>M,N,N,C,O,P,Q,D,e,f,e,8,9,9,<,[,',.,),),|,M>N>N>u>u>v>P>w>w>,>$,h>S>)>y>4>4>7>:;8>`;R, ", + " S,T,H,H,I,U,V,V,u,v,W,u,X,j,k,k,b,b,b,b,a,a,a,3,3,3,Y,x,x,],],],],],X>X>E>E>E>E>=,F>`>`>z,`>m>`>0>G>l>0>0>0>0>L,|>Z,`, '.'N,N,+'o,P,P,e,e,@'8,9,8,:,<,[,,,#'),),M>M>F,N>u>v>3>w>,>,>h>h>h>S>i>y>4>4>6>:# ", + " S,$'%'%'T,T,I,U,I,V,U,H,u,u,J,J,u,v,v,j,j,k,b,b,a,a,b,3,3,1,l,3,],l,x,*,],*,*,/,m,X>m,E>F>Y>`>z,m>m>`>`>m>-,G>&'|>*'Z,Z,Z,=' 'M,N,B,-'o,o,d,D,e,;'8,8,9,>'[,,,',),),+,M>M>N>N>v>v>h,w>$,h>h>h>{,)>)>%,:# ", + " ,''''''''')')')'!')'I,T,T,I,H,u,u,u,u,v,W,v,j,j,j,j,k,~'~'{'a,3,3,3,2,x,4,],],],],/,*,y,/,X>m,E>z,E>F>F>Y>`>`>]'|>^'/'('*'Z,_':'M,M,M,N,o,o,<'P,D,D,e,8,s,9,:,<,[,['.,.,),M>@,M>#,O>}'v>3>w>w>h>h>h>S>_# ", + " |'1'2'3'4'''''''5'''%'T,)'T,T,I,H,u,I,6'u,u,u,v,u,u,X,v,j,j,j,7'b,a,a,b,3,3,3,Y,l,],Y,],*,*,*,*,*,X>X>E>E>E>Y>8'|>9'9'0'a'('('b'*'Z,='M,N,c'B,o,o,Q,P,Q,q,e,8,8,9,<,<,[,.,.,),),M>M>F,N>d'u>h,w>w>,>$,o# ", + " e'f'g'1'1'1'2'h'4'''4'''''''%'%'%'I,T,T,I,T,u,6'u,u,u,u,J,v,j,J,j,k,k,j,i'b,a,a,3,3,2,],4,x,],],],],^,/,/,*,X>j'k'l'l'm'n'o'p'/'('('*'_'Z,='M,q'.'-'r'o,Q,D,e,e,8,8,8,9,<,[,',',s'),L>M>M>N>O>v>3>3>w>/# ", + " t'u'v'v'e'w'x'w'1'y'1'3'3'4'4'''''5'%''')')'T,T,T,T,I,u,u,u,u,u,v,J,K,j,v,k,k,b,b,a,a,b,3,3,3,3,1,l,l,],*,*,],z'A'B'C'D'l'l'9'n'0'^'('('E'*'_'='='='F'N,O,o,P,P,P,D,e,f,8,9,:,[,[,,,.,),L>@,M>N>O>u>u>G' ", + " H'I'J'K'K'v'g'L'L'w'w'M'1'1'y'''3'N'2'''%'%'%')'%'T,T,T,T,I,T,I,u,H,u,u,u,u,j,j,k,~'b,b,~'7'3,7'3,3,3,2,1,Y,O'P'Q'R'S'B'B'l'l'o'o'T'0'p'('*'E'_' 'U'='.'N,N,O,o,D,e,D,e,f,8,8,:,V'[,[,.,',),@,|,M>/#W' ", + " X'Y'Z'`'`' ) ) )g'L'.)L'+)1'w'1'1'3'1'2'''''''''%'%'%'!')'T,)'I,T,H,@)u,H,u,u,u,J,j,j,j,k,k,b,b,a,a,b,a,3,#)P'$)%)&)R'R'S'B'D'l'l'9'9'0'0'p'('*)Z,Z, 'U'='.'N,N,r'P,D,P,D,@'e,8,s,9,<,[,=)',),@,-) ", + " ;)>),)`'`'`'`'') )g'.).).)L'))w'w'))w'1'1'4'1'3'4'N'''''%'%')'%'T,T,T,T,I,T,V,H,u,u,u,u,K,v,J,J,k,~'b,~'!)~){)])^)/)/)()R'_)S'B'l'l'm'9'n'0'('('('b'Z,Z,=' 'M,N,N,C,o,o,p,e,e,e,8,8,9,:,<,[,_;:) ", + " <)[)})`'`'`'`'`' ) ) )|)1).).)L'L'L'L'L'w'))1'1'1'3'2'3'h'h'''%'%')')'T,!'T,T,T,T,6'u,u,H,u,u,u,j,J,j,2)P'3)4){)5)5)$)$)Q'Q'S'S'B'D'6)l'T'o'o'0'0'('('*)Z,Z,M,M,.'N,-'o,o,o,P,d,e,;'8,8,:,K> ", + " 7)8)9)0)J'`'`'`'`'`' ) )a)b)c)d).).)d).)L'.)e)1'))x'1'1'1'4'''''''''%'%')'%'!')')'I,I,U,U,V,u,I,u,u,f)g)h)i)j)4)k)l)5)])$)&)&)m)S'B'D'n)6)l'9'n'o'0'p'('E'_'o)Z,M,.'F'N,r'o,P,o,e,D,p)s;q) ", + " r)X's)t)u)v)`'`'`'`'K'w)K'a)b)b)x)1).).).).).)L'w'))))1'1'1'3'1'N'1'4'''y)''%')')'T,T,T,T,6'I,V,z)g)A)A)B)h)i)i)l){){)$)$)/)C)()R'S'B'B'C'l'm'9'o'0'a'D)('_'Z,_'M,='F'N,N,r'o,P,E)s; ", + " F)G)H)I)J)`'`'`'`'`'w) )w)b)b)b)c)K)L)d).).)d).)L'))))w'e)e)1'1'y'2'''4'4'%''')'%'M)T,T,T,N)O)P)A)A)Q)B)B)R)j)j){)5)5)5)$)$)Q'&)S'S'B'D'l'l'9'0'0'p'p'('('*'Z,:'M,F'M,N,-'V;S) ", + " T)U)I'V)J'`'`'`'`'w)w)w)W)c)c)b)c)c)c)L)b)d).).).)X)L'+)w'e)e)1'1'1'h'2'4'''''%')'%'Y)Z)`) !.!P)A)A)A)B)i)j)4)4)4)4)5)$)$)/)Q'&)S'S'B'D'l'm'9'0'0'p'a'('*'_'Z,Z,:'M,V; ", + " r)+!s)t)u)v)`'`'`'`'w)w)@!W)b)b)b)c)b)c)b)L)K).).)d)d)L'L'L'w'w'1'e)1'1'1'3'4'''#!Z)$!$!%!&!.! ! !P)A)*!B)=!i)l)l)4)5)5)$)$)Q'R'S'S'B'6)l'l'l'9'n'0'D)('*)E'_'|>-! ", + " ;!>!s),!'!)!`'`'`'`'!!w)W)b)~!b)b)b)c)b)b)b)c)b)K).).)L'.).)L'))))e)+)1'1'{!]!^!/!$!$!%!%!&!.! !P)(!Q)B)B)h)j)l){)5)^)$)$)Q'&)R'S'S'B'D'6)m'm'9'9'p'|>|>_! ", + " :!)[!}!`'`'`'`'!!!!!!|!~!~!~!c)b)b)c)c)b)b)c)K)x)L).)1).)X)L'L'))1!2!3!3!4!^!/!/!$!%!5!&!.!.! !A)A)B)h)i)i)l)4)5)5)$)$)%)R'&)R'S'B'n)l'k'6!7! ", + " 8!9![)0!a!b!`'`'`'`'!!!!!!|!~!~!b)~!c)c)b)b)b)c)b)c)c)L)d).).).)c!2!d!e!3!f!4!4!g!/!%!`)`)&!.!.!P)A)h!B)B)i)i!4)4){)5)])$)$)%)&)S'k'j!k! ", + " l!H's)m!n!o!`'`'`'`'!!!!|!~!~!~!~!p!b)b)c)c)b)b)b)b)b)b)K)q!2!r!r!s!e!e!3!3!4!4!/!/!`)%!`)`).!P)t!A)Q)B)u!i)j)l)l)4)5)5)P'v! ", + " w!x![)y!z!`'`'`'`'!!!!!!A!~!~!~!~!~!p!~!b)b)c)b)b)c)q!2!B!B!B!B!r!C!e!e!3!f!D!4!/!$!%!`)E!.!.!P)A)F!B)B)G!R)P'P'H! ", + " I!>)J!K!L!`'`'`'`'!!!!|!~!~!~!~!~!~!~!b)~!b)c)M!2!B!B!B!B!B!r!r!N!C!e!3!O!3!4!^!/!/!%!P!`).!.!t!(!Q!g)R! ", + " S!T!s)U!V!o!`'`'`'!!!!!!|!W!~!~!W!~!~!~!~!X!2!B!B!B!B!B!B!B!r!B!r!Y!e!Z!e!`!D!4!^!$!$!%!5!Z)O) ~ ", + " .~+~@~I)#~`'`'`'`'!!!!A!|!W!~!W!W!W!X!2!B!B!B!B!B!B!B!B!B!B!r!r!r!r!e!Z!3!D!D!g!Z)O)$~ ", + " %~8)u'&~L!`'`'`'`'!!!!|!W!~!W!X!2!B!B!B!B!B!B!B!B!B!B!B!B!B!B!r!r!d!3!2!*~=~ ", + " -~T!s);~>~v)`'`'`'!!!!!!A!X!2!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!2!,~'~ ", + " )~t)s)!~~~)!`'`'`'!!{~2!B!B!B!B!B!B!B!B!B!B!B!B!B!B!2!,~]~ ", + " :!U)U)^~/~`'`'`'(~2!B!B!B!B!B!B!B!B!B!B!B!2!_~ ", + " :~<~@~+~[~b!`'}~2!B!B!B!B!B!B!B!B!2!|~ ", + " 1~2~3~,!'!4~2!B!B!B!B!B!2!5~ ", + " 6~x!@~7~2!B!8~2!9~ ", + " 0~a~2!b~ ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " "}; + """ diff --git a/src/Mod/Ship/shipLoadExample/TaskPanel.ui b/src/Mod/Ship/shipLoadExample/TaskPanel.ui index 77d7441895..3c875d905a 100644 --- a/src/Mod/Ship/shipLoadExample/TaskPanel.ui +++ b/src/Mod/Ship/shipLoadExample/TaskPanel.ui @@ -76,7 +76,7 @@ - Serie 60 from Iowa University + Series 60 from Iowa University @@ -86,7 +86,7 @@ - Serie 60 (Katamaran) + Series 60 (Katamaran) diff --git a/src/Mod/Ship/tankCreateTank/TaskPanel.py b/src/Mod/Ship/tankCreateTank/TaskPanel.py new file mode 100644 index 0000000000..ec6394638e --- /dev/null +++ b/src/Mod/Ship/tankCreateTank/TaskPanel.py @@ -0,0 +1,167 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD as App +import FreeCADGui as Gui +# Qt library +from PyQt4 import QtGui,QtCore +# Module +from TankInstance import * +from shipUtils import Paths, Translator + +class TaskPanel: + def __init__(self): + self.ui = Paths.modulePath() + "/tankCreateTank/TaskPanel.ui" + + def accept(self): + # Create new ship instance + obj = App.ActiveDocument.addObject("Part::FeaturePython","Tank") + ShipTank(obj, self.solid, self.form.level.value(), self.form.dens.value()) + if not obj.IsShipTank: + msg = Translator.translate("Tank has not been created.\n") + App.Console.PrintError(msg) + ViewProviderShipTank(obj.ViewObject) + App.ActiveDocument.recompute() + return True + + def reject(self): + return True + + def clicked(self, index): + pass + + def open(self): + pass + + def needsFullSpace(self): + return True + + def isAllowedAlterSelection(self): + return False + + def isAllowedAlterView(self): + return True + + def isAllowedAlterDocument(self): + return False + + def helpRequested(self): + pass + + def setupUi(self): + mw = self.getMainWindow() + form = mw.findChild(QtGui.QWidget, "TaskPanel") + form.level = form.findChild(QtGui.QDoubleSpinBox, "Level") + form.dens = form.findChild(QtGui.QDoubleSpinBox, "Density") + self.form = form + # Initial values + if self.initValues(): + return True + self.retranslateUi() + # Connect Signals and Slots + QtCore.QObject.connect(form.level, QtCore.SIGNAL("valueChanged(double)"), self.onLevel) + QtCore.QObject.connect(form.dens , QtCore.SIGNAL("valueChanged(double)"), self.onDens) + + def getMainWindow(self): + "returns the main window" + # using QtGui.qApp.activeWindow() isn't very reliable because if another + # widget than the mainwindow is active (e.g. a dialog) the wrong widget is + # returned + toplevel = QtGui.qApp.topLevelWidgets() + for i in toplevel: + if i.metaObject().className() == "Gui::MainWindow": + return i + raise Exception("No main window found") + + def initValues(self): + """ Get selected geometry. + @return False if sucessfully find valid geometry. + """ + self.solid = None + solids = [] + selObjs = Gui.Selection.getSelection() + if not selObjs: + msg = Translator.translate("Tank objects can only be created on top of structure geometry (no object selected).\n") + App.Console.PrintError(msg) + msg = Translator.translate("Please create a tank geometry before using this tool.\n") + App.Console.PrintError(msg) + return True + for i in range(0, len(selObjs)): + solid = selObjs[i] + if solid.isDerivedFrom('Part::Feature'): + # Get shape + shape = solid.Shape + if not shape: + continue + solid = shape + if not solid.isDerivedFrom('Part::TopoShape'): + return None + # Get shells + shells = solid.Shells + if not shells: + continue + # Build solids + for s in shells: + solids.append(Part.Solid(s)) + if not solids: + msg = Translator.translate("Ship objects can only be created on top of structure geometry (no solids can't be computed).\n") + App.Console.PrintError(msg) + msg = Translator.translate("Please create or download a tank geometry before using this tool\n") + App.Console.PrintError(msg) + return True + self.solid = Part.CompSolid(solids) + msg = Translator.translate("Ready to work\n") + App.Console.PrintMessage(msg) + return False + + def retranslateUi(self): + """ Set user interface locale strings. + """ + self.form.setWindowTitle(Translator.translate("Create a new tank")) + name = Translator.translate("Filling level") + " (\%)" + self.form.findChild(QtGui.QLabel, "LevelLabel").setText(name) + name = '\n' + name = name + Translator.translate("Density") + name = name + '(kg/m3)' + self.form.findChild(QtGui.QLabel, "DensityLabel").setText(name) + + def onLevel(self, value): + """ Method called when tank filling level has been modified. + @param value Changed value. + """ + pass + + def onDens(self, value): + """ Method called when fluid density has been modified. + @param value Changed value. + """ + pass + +def createTask(): + panel = TaskPanel() + Gui.Control.showDialog(panel) + if panel.setupUi(): + Gui.Control.closeDialog(panel) + return None + return panel diff --git a/src/Mod/Ship/tankCreateTank/TaskPanel.ui b/src/Mod/Ship/tankCreateTank/TaskPanel.ui new file mode 100644 index 0000000000..635af00900 --- /dev/null +++ b/src/Mod/Ship/tankCreateTank/TaskPanel.ui @@ -0,0 +1,141 @@ + + + TaskPanel + + + + 0 + 0 + 260 + 180 + + + + Create new ship tank + + + + + + + + + 240 + 160 + + + + Fluid + + + false + + + + + 0 + 20 + 241 + 141 + + + + + 6 + + + QLayout::SetDefaultConstraint + + + + + QLayout::SetDefaultConstraint + + + 10 + + + 0 + + + 10 + + + 0 + + + + + Filling level (%) + + + + + + + 1 + + + 100.000000000000000 + + + 1.000000000000000 + + + + + + + + + 10 + + + 0 + + + 10 + + + 0 + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Density (kg/m<span style=" vertical-align:super;">3</span>)</p></body></html> + + + + + + + 1 + + + 1000000.000000000000000 + + + 10.000000000000000 + + + 998.000000000000000 + + + + + + + + + + + + + + + + diff --git a/src/Mod/Ship/tankCreateTank/__init__.py b/src/Mod/Ship/tankCreateTank/__init__.py new file mode 100644 index 0000000000..cbfb57d75d --- /dev/null +++ b/src/Mod/Ship/tankCreateTank/__init__.py @@ -0,0 +1,36 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD +import FreeCADGui + +# Qt libraries +from PyQt4 import QtGui,QtCore + +# Main object +import TaskPanel + +def load(): + """ Loads the tool """ + TaskPanel.createTask() From 8aefe7fa63fd2398d75af94ed4da4a2a6ac17423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Mon, 14 May 2012 14:06:07 +0200 Subject: [PATCH 14/69] Added tool to setup ship weights (structural based) --- src/Mod/Ship/CMakeLists.txt | 18 +- src/Mod/Ship/Icons/Weight.png | Bin 0 -> 7327 bytes src/Mod/Ship/Icons/Weight.xcf | Bin 0 -> 32353 bytes src/Mod/Ship/Icons/Weight.xpm | 721 +++++++++++++++++++++++++ src/Mod/Ship/InitGui.py | 4 +- src/Mod/Ship/Instance.py | 41 +- src/Mod/Ship/Makefile.am | 6 + src/Mod/Ship/ShipGui.py | 13 + src/Mod/Ship/shipHydrostatics/Tools.py | 2 +- src/Mod/Ship/tankWeights/TaskPanel.py | 212 ++++++++ src/Mod/Ship/tankWeights/TaskPanel.ui | 97 ++++ src/Mod/Ship/tankWeights/__init__.py | 36 ++ 12 files changed, 1145 insertions(+), 5 deletions(-) create mode 100644 src/Mod/Ship/Icons/Weight.png create mode 100644 src/Mod/Ship/Icons/Weight.xcf create mode 100644 src/Mod/Ship/Icons/Weight.xpm create mode 100644 src/Mod/Ship/tankWeights/TaskPanel.py create mode 100644 src/Mod/Ship/tankWeights/TaskPanel.ui create mode 100644 src/Mod/Ship/tankWeights/__init__.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index a362a0c6a6..a3c14ce960 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -33,6 +33,9 @@ SET(ShipIcons_SRCS Icons/ReparametrizeIco.xpm Icons/Ship.xcf Icons/Ship.xpm + Icons/Weight.png + Icons/Weight.xcf + Icons/Weight.xpm Icons/Tank.png Icons/Tank.xcf Icons/Tank.xpm @@ -96,6 +99,13 @@ SET(ShipUtils_SRCS ) SOURCE_GROUP("shiputils" FILES ${ShipUtils_SRCS}) +SET(ShipWeights_SRCS + tankWeights/__init__.py + tankWeights/TaskPanel.py + tankWeights/TaskPanel.ui +) +SOURCE_GROUP("shipweights" FILES ${ShipWeights_SRCS}) + SET(ShipCreateTank_SRCS tankCreateTank/__init__.py tankCreateTank/TaskPanel.py @@ -103,7 +113,7 @@ SET(ShipCreateTank_SRCS ) SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) -SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipCreateTank_SRCS}) +SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS}) ADD_CUSTOM_TARGET(Ship ALL SOURCES ${all_files} @@ -159,6 +169,12 @@ INSTALL( DESTINATION Mod/Ship/shipUtils ) +INSTALL( + FILES + ${ShipWeights_SRCS} + DESTINATION + Mod/Ship/tankWeights +) INSTALL( FILES ${ShipCreateTank_SRCS} diff --git a/src/Mod/Ship/Icons/Weight.png b/src/Mod/Ship/Icons/Weight.png new file mode 100644 index 0000000000000000000000000000000000000000..b613fa569e1ba8860e75b14b7ae9f17fcd46dd34 GIT binary patch literal 7327 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 z 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 JLcuER{{W=4{qg_+ literal 0 HcmV?d00001 diff --git a/src/Mod/Ship/Icons/Weight.xcf b/src/Mod/Ship/Icons/Weight.xcf new file mode 100644 index 0000000000000000000000000000000000000000..e10ea8e991301b56e4a9a30104fe9db7056c8776 GIT binary patch literal 32353 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 c #767576", +", c #90908F", +"' c #A1A2A2", +") c #ACACAC", +"! c #A1A1A1", +"~ c #8F8E8F", +"{ c #777776", +"] c #636363", +"^ c #636364", +"/ c #636263", +"( c #636362", +"_ c #767676", +": c #9D9D9D", +"< c #B0B0B1", +"[ c #B1B1B0", +"} c #B0B0B0", +"| c #AFAFAF", +"1 c #9C9C9C", +"2 c #767777", +"3 c #656565", +"4 c #646463", +"5 c #868787", +"6 c #AFAFB0", +"7 c #AEAEAE", +"8 c #ADAEAE", +"9 c #ADADAE", +"0 c #868687", +"a c #666667", +"b c #676766", +"c c #656665", +"d c #7B7B7C", +"e c #AFAEAE", +"f c #ACADAC", +"g c #ACACAB", +"h c #7C7C7C", +"i c #686867", +"j c #686868", +"k c #666767", +"l c #A0A0A0", +"m c #ADADAD", +"n c #ADADAC", +"o c #ABAAAA", +"p c #ABABAA", +"q c #9E9E9E", +"r c #6A6969", +"s c #696969", +"t c #676768", +"u c #676868", +"v c #7F7F7F", +"w c #ABABAC", +"x c #A9A9A9", +"y c #A8A8A9", +"z c #80807F", +"A c #6B6B6B", +"B c #6B6C6B", +"C c #696A69", +"D c #939394", +"E c #AAAAAA", +"F c #A7A8A8", +"G c #A8A7A8", +"H c #929292", +"I c #6D6C6D", +"J c #6D6D6D", +"K c #6A6A6A", +"L c #A6A6A6", +"M c #9D9E9E", +"N c #6D6E6E", +"O c #6E6E6D", +"P c #6C6C6C", +"Q c #A0A09F", +"R c #A8A7A7", +"S c #A4A4A5", +"T c #706F6F", +"U c #706F70", +"V c #6D6E6D", +"W c #A6A5A6", +"X c #A3A3A3", +"Y c #919191", +"Z c #717070", +"` c #707171", +" . c #6F6F6F", +".. c #6F6F6E", +"+. c #818281", +"@. c #A4A4A4", +"#. c #A2A2A2", +"$. c #A2A2A1", +"%. c #727272", +"&. c #707071", +"*. c #717071", +"=. c #9A9A9A", +"-. c #A2A2A3", +";. c #A0A1A0", +">. c #989897", +",. c #737473", +"'. c #737474", +"). c #727172", +"!. c #808080", +"~. c #9F9F9F", +"{. c #9E9F9F", +"]. c #808181", +"^. c #747574", +"/. c #757575", +"(. c #737373", +"_. c #737374", +":. c #888888", +"<. c #A09FA0", +"[. c #9F9FA0", +"}. c #A09F9F", +"|. c #878888", +"1. c #767675", +"2. c #949494", +"3. c #9D9D9E", +"4. c #9D9C9C", +"5. c #9C9C9D", +"6. c #787778", +"7. c #777778", +"8. c #777777", +"9. c #8C8C8C", +"0. c #999A99", +"a. c #818181", +"b. c #797878", +"c. c #797879", +"d. c #605F60", +"e. c #605F5F", +"f. c #606160", +"g. c #616061", +"h. c #626162", +"i. c #616262", +"j. c #636262", +"k. c #626363", +"l. c #636463", +"m. c #646464", +"n. c #646564", +"o. c #646465", +"p. c #656564", +"q. c #656666", +"r. c #666666", +"s. c #666565", +"t. c #676666", +"u. c #676767", +"v. c #686768", +"w. c #686767", +"x. c #696869", +"y. c #5F5F60", +"z. c #616060", +"A. c #626161", +"B. c #626263", +"C. c #636464", +"D. c #656464", +"E. c #656566", +"F. c #696868", +"G. c #6A6A69", +"H. c #69696A", +"I. c #B3B3B3", +"J. c #B3B2B2", +"K. c #B2B2B2", +"L. c #B2B2B1", +"M. c #B2B1B1", +"N. c #B1B1B1", +"O. c #B0B1B1", +"P. c #AFB0AF", +"Q. c #AEAFAF", +"R. c #ACADAD", +"S. c #ABABAB", +"T. c #AAABAA", +"U. c #AAA9AA", +"V. c #6B6B6A", +"W. c #6B6C6C", +"X. c #6C6B6C", +"Y. c #B2B3B3", +"Z. c #B2B3B2", +"`. c #B2B1B2", +" + c #B1B2B1", +".+ c #B1B1B2", +"++ c #B1B0B0", +"@+ c #B0AFB0", +"#+ c #AFAEAF", +"$+ c #AEADAE", +"%+ c #ADACAC", +"&+ c #ACACAD", +"*+ c #ABACAC", +"=+ c #ACABAB", +"-+ c #AAAAAB", +";+ c #A9A9AA", +">+ c #A8A8A8", +",+ c #6D6C6C", +"'+ c #6C6D6D", +")+ c #B1B2B2", +"!+ c #B0B1B0", +"~+ c #AFB0B0", +"{+ c #AEAFAE", +"]+ c #AFAFAE", +"^+ c #AEADAD", +"/+ c #ACABAC", +"(+ c #A9AAAA", +"_+ c #A9A8A9", +":+ c #A7A7A7", +"<+ c #A6A7A6", +"[+ c #6E6E6E", +"}+ c #B1B0B1", +"|+ c #ADAEAD", +"1+ c #ADACAD", +"2+ c #A9A8A8", +"3+ c #A7A8A7", +"4+ c #A7A6A7", +"5+ c #A5A5A6", +"6+ c #A6A6A5", +"7+ c #A6A5A5", +"8+ c #A5A5A5", +"9+ c #6F6F70", +"0+ c #5F605F", +"a+ c #B3B2B3", +"b+ c #AAABAB", +"c+ c #A9AAA9", +"d+ c #A4A5A5", +"e+ c #A3A4A4", +"f+ c #A4A3A3", +"g+ c #A3A3A4", +"h+ c #B0AFAF", +"i+ c #AEAEAF", +"j+ c #A8A9A8", +"k+ c #A7A7A8", +"l+ c #A7A7A6", +"m+ c #A7A6A6", +"n+ c #A5A4A4", +"o+ c #A4A3A4", +"p+ c #A3A4A3", +"q+ c #A3A3A2", +"r+ c #B2B2B3", +"s+ c #AAAAA9", +"t+ c #A6A6A7", +"u+ c #A5A6A6", +"v+ c #A5A6A5", +"w+ c #A4A5A4", +"x+ c #A4A4A3", +"y+ c #A1A2A1", +"z+ c #A1A1A0", +"A+ c #737273", +"B+ c #747374", +"C+ c #AEAEAD", +"D+ c #A6A7A7", +"E+ c #A2A3A2", +"F+ c #A2A1A2", +"G+ c #A0A1A1", +"H+ c #A1A0A1", +"I+ c #A0A0A1", +"J+ c #747474", +"K+ c #B0B0AF", +"L+ c #ABACAB", +"M+ c #ABAAAB", +"N+ c #A5A4A5", +"O+ c #A3A2A3", +"P+ c #A2A1A1", +"Q+ c #9FA09F", +"R+ c #9E9F9E", +"S+ c #757676", +"T+ c #AAA9A9", +"U+ c #A5A5A4", +"V+ c #A2A3A3", +"W+ c #A1A1A2", +"X+ c #9F9E9E", +"Y+ c #9E9D9D", +"Z+ c #9C9D9D", +"`+ c #9D9D9C", +" @ c #777877", +".@ c #A8A8A7", +"+@ c #9FA0A0", +"@@ c #9F9E9F", +"#@ c #9D9E9D", +"$@ c #9C9D9C", +"%@ c #9B9C9C", +"&@ c #787879", +"*@ c #676867", +"=@ c #A9A9A8", +"-@ c #9E9E9D", +";@ c #9C9C9B", +">@ c #9B9B9B", +",@ c #9B9B9A", +"'@ c #9A9B9B", +")@ c #797A79", +"!@ c #797A7A", +"~@ c #7A797A", +"{@ c #686969", +"]@ c #9D9C9D", +"^@ c #9A9B9A", +"/@ c #999A9A", +"(@ c #999999", +"_@ c #999899", +":@ c #7A7B7B", +"<@ c #7B7B7B", +"[@ c #A3A2A2", +"}@ c #A1A0A0", +"|@ c #9E9E9F", +"1@ c #9C9B9C", +"2@ c #9C9B9B", +"3@ c #9A9A99", +"4@ c #9A999A", +"5@ c #989998", +"6@ c #989898", +"7@ c #979898", +"8@ c #989797", +"9@ c #979797", +"0@ c #7C7C7D", +"a@ c #6B6A6A", +"b@ c #6B6A6B", +"c@ c #000000", +"d@ c #9F9F9E", +"e@ c #9B9A9A", +"f@ c #9A9999", +"g@ c #989899", +"h@ c #969796", +"i@ c #969696", +"j@ c #7D7E7D", +"k@ c #7E7E7E", +"l@ c #9B9B9C", +"m@ c #999898", +"n@ c #979897", +"o@ c #969697", +"p@ c #969595", +"q@ c #959596", +"r@ c #959595", +"s@ c #959594", +"t@ c #959495", +"u@ c #7E7F7F", +"v@ c #6C6D6C", +"w@ c #9E9D9E", +"x@ c #979798", +"y@ c #969596", +"z@ c #969695", +"A@ c #949594", +"B@ c #939494", +"C@ c #949493", +"D@ c #939493", +"E@ c #807F80", +"F@ c #9B9C9B", +"G@ c #969797", +"H@ c #959696", +"I@ c #959494", +"J@ c #949394", +"K@ c #939393", +"L@ c #939293", +"M@ c #929392", +"N@ c #939292", +"O@ c #818282", +"P@ c #6E6F6F", +"Q@ c #989999", +"R@ c #979696", +"S@ c #939392", +"T@ c #929393", +"U@ c #919291", +"V@ c #919190", +"W@ c #828283", +"X@ c #828382", +"Y@ c #707070", +"Z@ c #9B9A9B", +"`@ c #9A9A9B", +" # c #949595", +".# c #929291", +"+# c #929191", +"@# c #919091", +"## c #909090", +"$# c #908F90", +"%# c #8F8F90", +"&# c #848383", +"*# c #838384", +"=# c #848484", +"-# c #717171", +";# c #999998", +"># c #979697", +",# c #909190", +"'# c #919090", +")# c #8F8F8F", +"!# c #8E8E8E", +"~# c #8E8F8E", +"{# c #858484", +"]# c #858585", +"^# c #727372", +"/# c #99999A", +"(# c #959695", +"_# c #949495", +":# c #949393", +"<# c #919292", +"[# c #908F8F", +"}# c #8F8F8E", +"|# c #8F8E8E", +"1# c #8E8F8F", +"2# c #8E8D8E", +"3# c #8E8D8D", +"4# c #8D8E8D", +"5# c #8D8C8D", +"6# c #8D8D8D", +"7# c #868686", +"8# c #747473", +"9# c #929192", +"0# c #909091", +"a# c #8D8E8E", +"b# c #8D8D8E", +"c# c #8C8D8D", +"d# c #8B8C8B", +"e# c #878787", +"f# c #878887", +"g# c #747475", +"h# c #747575", +"i# c #979796", +"j# c #8E8E8F", +"k# c #8E8E8D", +"l# c #8D8D8C", +"m# c #8B8C8C", +"n# c #8B8B8B", +"o# c #8A8B8B", +"p# c #8B8A8A", +"q# c #8A8A8A", +"r# c #888988", +"s# c #898989", +"t# c #8F9090", +"u# c #8D8C8C", +"v# c #8C8B8C", +"w# c #8B8B8A", +"x# c #8C8C8B", +"y# c #8A8A8B", +"z# c #89898A", +"A# c #898A89", +"B# c #8A8989", +"C# c #888788", +"D# c #8C8B8B", +"E# c #777878", +"F# c #8B8A8B", +"G# c #8A8B8A", +"H# c #8A898A", +"I# c #8A8A89", +"J# c #888989", +"K# c #898888", +"L# c #888887", +"M# c #868786", +"N# c #797978", +"O# c #797979", +"P# c #929293", +"Q# c #878788", +"R# c #878687", +"S# c #868586", +"T# c #858485", +"U# c #7A7A7A", +"V# c #898A8A", +"W# c #898889", +"X# c #888889", +"Y# c #888787", +"Z# c #878786", +"`# c #868685", +" $ c #858685", +".$ c #838483", +"+$ c #8F908F", +"@$ c #7B7C7C", +"#$ c #8C8C8D", +"$$ c #898988", +"%$ c #878686", +"&$ c #868585", +"*$ c #848585", +"=$ c #858584", +"-$ c #838383", +";$ c #828282", +">$ c #838282", +",$ c #7D7C7C", +"'$ c #858586", +")$ c #838484", +"!$ c #838382", +"~$ c #818182", +"{$ c #828181", +"]$ c #808180", +"^$ c #919192", +"/$ c #7E7D7D", +"($ c #8C8D8C", +"_$ c #838283", +":$ c #828383", +"<$ c #828281", +"[$ c #818180", +"}$ c #7F807F", +"|$ c #7E7E7F", +"1$ c #7E7F7E", +"2$ c #7F7E7F", +"3$ c #858686", +"4$ c #848384", +"5$ c #818081", +"6$ c #818080", +"7$ c #807F7F", +"8$ c #7F7F7E", +"9$ c #7F7F80", +"0$ c #909191", +"a$ c #8B8B8C", +"b$ c #848584", +"c$ c #848483", +"d$ c #7E7E7D", +"e$ c #7E7D7E", +"f$ c #7D7D7D", +"g$ c #828182", +"h$ c #7F8080", +"i$ c #7D7C7D", +"j$ c #7C7D7D", +"k$ c #7C7D7C", +"l$ c #7C7C7B", +"m$ c #7B7C7B", +"n$ c #7D7E7E", +"o$ c #7D7D7E", +"p$ c #7D7D7C", +"q$ c #7C7B7C", +"r$ c #7B7B7A", +"s$ c #7A7B7A", +"t$ c #7A7A7B", +"u$ c #7A7A79", +"v$ c #787979", +"w$ c #848485", +"x$ c #7F7E7E", +"y$ c #7C7B7B", +"z$ c #7A7979", +"A$ c #787878", +"B$ c #787777", +"C$ c #787877", +"D$ c #808081", +"E$ c #787978", +"F$ c #777677", +"G$ c #767776", +"H$ c #7B7A7A", +"I$ c #767677", +"J$ c #757576", +"K$ c #757475", +"L$ c #747373", +"M$ c #727373", +"N$ c #737372", +"O$ c #737272", +"P$ c #7B7A7B", +"Q$ c #757675", +"R$ c #757574", +"S$ c #717272", +"T$ c #717172", +"U$ c #777676", +"V$ c #70706F", +"W$ c #79797A", +"X$ c #757474", +"Y$ c #727271", +"Z$ c #727171", +"`$ c #6F6E6F", +" % c #767575", +".% c #6F6E6E", +"+% c #6E6F6E", +"@% c #6E6D6D", +"#% c #6D6D6E", +"$% c #6D6D6C", +"%% c #717271", +"&% c #6C6C6D", +"*% c #6B6B6C", +"=% c #717170", +"-% c #6F706F", +";% c #6E6D6E", +">% c #6C6C6B", +",% c #6A6B6B", +"'% c #6A6B6A", +")% c #707170", +"!% c #6F7070", +"~% c #6A6A6B", +"{% c #6E6E6F", +"]% c #696A6A", +"^% c #696968", +"/% c #686968", +"(% c #666766", +"_% c #6C6B6B", +":% c #676667", +"<% c #6A696A", +"[% c #656465", +"}% c #666566", +"|% c #666665", +"1% c #646565", +"2% c #646364", +"3% c #646363", +"4% c #989798", +"5% c #686869", +"6% c #626362", +"7% c #606061", +"8% c #B3B3B2", +"9% c #A8A9A9", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" . . + @ + # + ", +" + + $ + % & & & * * = ", +" - - ; > , ' ) ! ~ { ] ] ^ ", +" / ( _ : < [ < } } | | 1 2 3 3 ", +" 4 4 5 } 6 | | 7 7 8 9 0 a b ", +" 3 c d e 7 7 f ) g h i j ", +" k k l m n o p q r s ", +" t u v ) w x y z A B ", +" C s D o E F G H I J ", +" K A l x L M N O ", +" P P Q R S 1 T U ", +" V V H L W X X Y Z ` ", +" ...+.@.S #.$.+.%.%. ", +" &.*.=.-.X ;.l >.,.'. ", +" ).%.!.! ! ! ~.~.{.].^./. ", +" (._.:.<.[.}.~. q q q M |.1._ ", +" /./.v 2.q M 3.: : 4.5.H !.6.7. ", +" 2 { 8.!.9.2.0.2.9.a.b.b.c. ", +" . . . . . . . . . . . . . . . . . . . . . . . . . . d.. e.@ f.f.g.& & h.& = i.j.j.j.k.] ] l.4 m.n.o.p.3 3 q.r.s.r.r.t.t.u.u.j v.w.j j x. ", +" . . . . . . . . . . . . . . . . . . . . . . . @ d.y.+ z.z.f.$ & A.A.= * * j.B.] ] C.m.4 m.m.D.p.p.3 E.E.r.r.t.r.u.u.u.u t i j j x.F.s s G.H.K K ", +" . . . . I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.J.K.K.K.L.M.N.O.} } } } } P.| | Q.7 7 8 m m 9 m f R.) ) ) S.S.S.S.T.E E U.E x V.A W.X. ", +" . . . I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.Y.Z.K.`. +M..+N.++N.++} @+| | | | e #+7 $+$+m m %+&+) ) ) *+=+g S.o E -+;+E ;+x x x >+>+>+F G ,+'+J ", +" . . I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.J.I.K.K.K.K.)+M.[ ++!+} ~+} ~+| | {+]+{+$+7 $+^+m n n %+) /+w S.S.S.o E E E U.(+x x x _+>+>+>+:+R :+:+<+:+L O [+ ", +" . . . I.I.I.I.I.I.I.I.I.I.I.I.I.I.J.Z.K.K..+`.N.N.}+++} ~+} 6 | | | e 7 7 7 m |+m &+1+f ) *+*+w S.T.-+o E E E (+x x 2+2+>+>+3+F R :+4+L L L 5+6+7+8+@. .9+T ", +" 0+@ I.I.I.I.I.I.I.I.I.Y.a+Z.K.K.L.N.N.[ !+} } } ~+} 6 | {+7 7 7 $+m ^+m ) ) 1+) g /+S.S.S.b+E E E (+c+x x y >+>+>+F F :+:+:+L L L W W 5+8+S d+@.@.e+f+g+Z &. ", +" & $ I.I.I.I.I.I.J.K.K. +L.N.N.!+} } h+| | 6 | | {+i+7 7 8 m R.R.) ) ) ) *+w S.b+b+E E E (+x x _+j+y >+G G 3+k+:+l+m+m+L 7+5+5+d+8+n+@.o+o+p+X q+X #.#.#.).%. ", +" & * * r+K.L.)+.+N.N.++N.!+} } h+@+| | | 7 7 7 9 m m 1+&+&+f ) ) w S.S.o E T.s+s+x (+x 2+j+y >+F k+:+:+4+t+t+L u+u+v+v+8+8+w+@.x+o+@.X X q+-.-.#.' y+! ! z+A+(.B+ ", +" ] / +.+N.N.} !+} @+@+P.| | ]+i+7 |+C+m m m &+f f g *+=+S.S.S.o E E U.s+(+x x j+2+>+F k+k+:+D+m+L L 6+8+7+8+8+8+n+@.@.o+x+f+X -.E+#.' F+' ! ! ;.G+H+I+}.}.<.J+/. ", +" 4 m.m.!+K+@+~+h+| #+7 {+7 C+^+8 m m &+&+) g L+S.S.S.p M+E U.s+x x x x 2+2+>+R F k+:+<+l+L L L W 8+v+N+w+@.S x+x+X X q+X O+E+F+P+F+! ! H+I+;.Q l Q+~.~.{.~.R+q S+_ _ ", +" 3 3 h+| | {+e 7 ^+^+^+m m ) ) ) ) =+w S.S.b+-+T.U.c+T+;+x x 2+2+>+>+R :+:+D+L L L L 6+7+8+N+U+w+@.e+x+o+p+V+X #.E+' y+W+! H+l l l l [.[.~.{.~.q X+q 3.Y+: : Z+`+8. @ ", +" r.r.7 m 7 ^+m n R.) ) L+*+=+L+S.-+b+E E (+T+x x x >+j+>+.@G :+l+:+D+L L u+7+5+8+N+8+@.@.f+X f+f+q+X #.#.y+' $.! ! ;.I+l Q +@Q+}.@@~.q q q #@3.: : : $@$@1 1 %@%@&@c. ", +" u.w.*@m &+&+) /+w S.S.S.S.o o E x c+T+x y =@>+>+R G :+:+:+m+:+6+7+6+8+8+8+n+S @.e+@.x+X q+#.#.E+#.y+! ! ! z+I+l l +@l }.~.~.R+R+q -@#@: : `+5.1 1 ;@>@>@>@>@,@'@=.)@!@~@ ", +" j {@g S.S.S.S.E p E c+T+U.x x 2+_+>+>+F R R t+:+m+L L 5+v+8+8+8+N+w+@.@.f+p+f+X X #.#.#.! ! ! ! I+z+l l [.~.~.~.{.X+q q M M : : ]@5.1 1 %@;@>@>@>@>@^@=./@(@(@0.(@_@:@<@ ", +" H.s r M+T.E E T+;+x =@x y >+>+G F :+:+D+L L L L 7+8+8+n+U+@.@.p+p+X X O+V+#.[@#.$.P+! ! }@G+l <.Q [.[.~.@@|@q q M #@: : : 4.1 1 1@2@>@>@>@=.=.=.3@4@3@(@(@_@5@6@7@8@9@h h 0@ ", +" a@b@T+;+x =@y >+>+>+3+G :+:+l+c@c@c@c@c@c@d+8+N+@.@.@.X o+X X q+#.#.P+P+W+! ! G+H+l +@+@<.~.~.d@R+q q 3.M 3.: ]@Z+5.1@2@1 >@>@=.=.e@=.=.f@(@(@(@g@6@6@6@6@9@8@h@9@i@i@i@j@k@ ", +" P W.j+>+G R 3+:+:+t+:+L L 7+7+c@c@c@c@c@c@@.@.X X O+X #.' #.F+P+! G+H+l l l ~.Q ~.~.X+q R+-@M M #@Z+]@4.1 1 ;@2@l@>@e@^@^@=.4@=.0.(@(@5@m@6@>.8@n@9@9@i@o@i@p@q@p@r@s@t@v u@ ", +" J J v@:+:+:+l+L 5+6+5+8+d+d+@.@.c@c@c@c@c@c@#.#.#.#.' P+! ! ! I+l Q ~.~.~.d@q X+q q w@3.: : 5.1 4.1 %@2@>@>@>@e@=.=.3@4@0._@_@5@5@6@6@x@9@8@9@9@i@i@i@y@z@r@r@t@A@2.B@C@D@!.E@!. ", +" O [+L L v+8+8+8+8+S @.e+x+x+X X c@c@c@c@c@c@! H+! }@}@l l l [.~.{.d@q q M w@Y+: : ]@1 $@1 1 %@F@>@'@'@=.=.4@=.(@(@g@(@6@6@7@9@n@9@9@o@G@i@z@H@y@r@r@I@r@2.2.2.J@K@K@L@M@N@H a.O@ ", +" P@T N+w+n+@.@.g+X X V+O+V+[@#.y+c@c@c@c@c@c@l Q Q+~.~.~.{.{.q q q : : `+: `+1 ;@2@F@l@>@,@^@=.=./@/@(@Q@Q@m@5@6@x@x@x@9@9@i@R@i@z@r@q@r@r@t@A@A@D 2.K@D@K@S@T@H H U@H Y Y V@W@X@ ", +" Y@Y@Y@p+g+X [@X #.-.#.F+W+! ! ! ! c@c@c@c@c@c@|@q q q M 3.: : ]@]@5.1 1 1 >@>@^@Z@=.`@=.f@f@(@Q@m@6@Q@6@x@9@8@9@9@o@R@i@i@y@q@r@ #I@I@2.J@B@K@K@S@N@M@H .#+#.#Y Y V@@#####$#%#&#*#=# ", +" -#-#-.O+#.' ! P+W+z+}@I+}@l l Q [.c@c@c@c@c@c@Y+: ]@Z+: 1 1 %@>@>@>@>@'@=.=./@3@(@(@;#6@6@6@6@7@9@n@>#9@i@R@i@i@q@r@r@ #s@2.2.J@D D@K@S@K@H N@H +#Y Y Y ,#'#####, $#)#)#)#!#~#!#{#]# ", +" %.^#! ! ! }@z+}@l +@+@~.~.X+{.R+q c@c@c@c@c@c@1 1 ;@;@F@>@=.e@'@=./#3@f@(@Q@_@5@m@>.>.n@9@G@R@>#o@i@(#z@H@r@t@2._#2.:#B@D K@N@T@H H .#<#Y Y Y Y ######$#[#)#}#|#1#|#!#2#3#4#5#6#7#7# ", +" (.(.8#l l l ~.[.d@~.q X+q Y+#@3.: Z+c@c@c@c@c@c@>@>@^@`@=.=./@c@c@c@c@c@c@6@>.8@9@9@h@9@i@i@c@c@c@c@c@c@2.2.2.c@c@c@c@c@c@H U@9#U@Y Y Y 0#####%#)#)#)#)#!#}#a#b#b#6#c#5#c#9.9.9.d#e#f#e# ", +" g#h#~.|@@@|@q -@q #@: : : 4.$@F@%@%@c@c@c@c@c@c@4@4@(@(@Q@Q@c@c@c@c@c@c@i#i#o@i@z@i@H@r@c@c@c@c@c@c@c@c@c@N@M@c@c@c@c@c@c@Y '#'#####%#%#)#)#j#!#!#!#k#!#6#6#5#9.l#9.9.m#d#n#o#o#p#q#r#s# ", +" S+/.S+q q 3.: ]@$@`+4.1 %@l@>@Z@,@e@=.c@c@c@c@c@c@(@m@6@>.8@n@c@c@c@c@c@i@y@q@r@r@s@s@2.c@c@c@c@c@c@c@c@c@c@c@U@c@c@c@c@c@c@t#, )#1#}#|#!#!#2#4#6#6#c#u#9.9.v#n#n#n#w#q#q#q#q#q#s#s#s#q#q#p# ", +" 2 8.`+`+1 1 %@1 F@2@>@>@^@=./@3@4@/@(@c@c@c@c@c@c@9@9@9@>#h@c@c@c@c@c@r@r@t@I@2.B@D@K@K@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@|#!#!#6#b#6#5#l#9.9.9.x#9.n#n#n#o#y#q#z#A#B#s#s#r#:.:.f#C#f#d#D# ", +" 7.E#%@l@>@>@`@'@e@3@=.4@(@(@5@5@6@6@6@c@c@c@c@c@c@i@i@r@r@c@c@c@c@c@c@K@K@D@K@S@S@L@H c@c@c@c@c@c@c@##$#$#$#c@c@c@c@c@c@c@c@6#6#9.5#9.9.d#D#n#n#F#q#G#q#H#I#A#s#J#K#K#:.C#C#L#e#e#M#7#7#9.9. ", +" N#c.O#e@=.=.=.(@0.(@(@_@5@>.6@7@9@9@9@G@c@c@c@c@c@c@s@r@r@c@c@c@c@c@c@S@S@P#H U@Y Y Y Y c@c@c@c@c@c@)#~ 1#~#!#2#c@c@c@c@c@c@c@x#n#9.n#n#w#G#q#I#H#B#A#s#r#s#:.:.:.Q#L#e#R#R#7#7#S#7#]#]#T#a#!#~# ", +" ~@U#f@f@(@Q@m@g@7@>.x@n@9@i@9@i@H@y@r@y@c@c@c@c@c@c@2.D c@c@c@c@c@c@H U@Y Y V@,#,###, c@c@c@c@c@c@c@a#a#k#6#5#6#c@c@c@c@c@c@c@o#y#q#q#I#s#V#W#s#X#:.:.Y#:.C#e#Z#7#7#`#7# $]#]#T#]#{#=#=#=#.$)#+$ ", +" <@@$6@8@9@x@9@9@o@o@i@i@y@H@r@r@ #I@A@2.c@c@c@c@c@c@M@c@c@c@c@c@c@Y 0#####$#)#)#)#1#~#c@c@c@c@c@c@u#6##$9.9.D#D#d#c@c@c@c@c@c@A#s#$$J#X#r#:.:.f#e#e#%$R#7#7#&$]#`#*$=$=$=#=#=#*#-$-$-$;$W@>$,#Y ", +" h h ,$9@i@i@i@i@y@H@r@r@r@r@_#2.J@:#K@K@N@c@c@c@c@c@c@c@c@c@c@c@c@##$#%#)#1#|#~#!#a#a#6#c@c@c@c@c@c@D#9.n#o#p#q#q#B#c@c@c@c@c@c@:.|.:.L#e#5 Z#R#7#7#'$ $]#]#{#=#=#=#=#)$&#-$-$W@!$X@;$~${$a.]$H H ^$ ", +" j@/$H@y@p@r@r@t@2.2.2.2.J@:#K@K@P#M@H H +#c@c@c@c@c@c@c@c@c@c@c@c@c@!#2#k#b#3#5#c##$($9.c@c@c@c@c@c@o#q#V#z#B#s#s#W#c@c@c@c@c@c@e#e#0 7#`#`#S#]#]#=$T#=#=#)$*#-$-$_$:$;$;$<$O@{$a.]$[$!.!.!.v }$K@:# ", +" |$1$2$s@2.2.C@:#D@K@K@K@H M@H H U@Y Y Y 0###c@c@c@c@c@c@c@c@c@c@c@c@c@c#5##$9.9.m#D#v#n#o#c@c@c@c@c@c@s#J#$$:.:.:.L#Y#c@c@c@c@c@c@`#3$]#]#]#*$]#=#*#4$)$-$!$X@!$;$;$~$a.a.].5$5$6$!.}$7$v 8$v u@2$2.2.r@ ", +" 9$}$:#B@K@K@L@H N@<#H <#<#@#0$0$######, +$)#c@c@c@c@c@c@c@c@c@c@c@c@c@c@a$D#y#p#q#q#q#q#s#c@c@c@c@c@c@C#e#:.e#e#5 %$7#c@c@c@c@c@c@b$=#c$=#-$-$-$W@;$;$;$;$+.a.a.a.[$[$!.!.!.}$v u@2$k@k@k@d$e$f$,$f$r@z@ ", +" a.].L@T@H U@+#U@Y Y Y ######, +$)#)#j#!#!#k#c@c@c@c@c@c@c@c@a$c@c@c@c@c@p#q#I#B#s#s#K#s#K#c@c@c@c@c@c@%$%$7#7#'$'$]#{#c@c@c@c@c@c@-$-$:$!$X@g$~$O@+.a.a.[$!.!.}$E@h$v 2$2$|$k@k@f$/$f$i$j$k$h l$@$m$9@9@ ", +" g$~$<$+#Y @#'#V@t#[#[#%#)#1#|#j#!#k#b#6#4#l#l#c@c@c@c@c@c@n#w#y#c@c@c@c@c@c@W#J#:.:.:.|.e#e#c@c@c@c@c@c@3$]#*$=$=$=#4$*#c@c@c@c@c@c@<$a.+.a.a.[$].!.z E@z v v v |$1$k@n$k@f$o$f$j$p$0@h q$<@<@r$r$s$s$6@6@5@ ", +" >$-$,###+$[#[#)#}#}#!#!#b#b#6#6#6#6##$u#9.x#d#c@c@c@c@c@c@I#B#s#s#c@c@c@c@c@c@Q#5 Z#M#7#7#7#c@c@c@c@c@c@=#c$c$-$-$-$>$X@c@c@c@c@c@c@a.[$!.!.7$h$v 8$8$8$1$k@n$j@/$f$f$j$0@h q$<@l$<@<@t$U#U#~@u$O#O#O#v$f@/@ ", +" c$)$=#)#)#!#~#!#3#a#4#6#5#9.#$9.v#m#n#n#n#w#G#q#c@c@c@c@c@c@$$K#:.C#c@c@c@c@c@c@7#7# $ $]#T#w$c@c@c@c@c@c@c@!$X@;$;$a.<$c@c@c@c@c@c@c@v v v k@x$k@k@k@o$o$f$k$f$i$h l$q$y$<@<@<@t$U#U#u$O#z$c.&@b.A$E#B$C$,@^@,@ ", +" ]#]#!#6#k#3#6#6#l#($9.9.m#n#n#n#F#y#q#I#V#z#s#s#c@c@c@c@c@c@e#e#M#M#7#c@c@c@c@c@c@b$=#=#)$.$-$-$c@c@c@c@c@c@g${$a.!.a.D$c@c@c@c@c@c@c@x$k@d$/$f$f$j$p$h h h c@c@c@c@c@c@U#U#z$~@O#b.E$c.A$A$C$7.8.8.F$G$_ _ 1 1 ", +" 7#7##$9.c#9.x#n#n#n#n#n#p#q#I#H#s#s#s#s#J#:.:.L#c@c@c@c@c@c@3$ $&$]#]#c@c@c@c@c@c@-$-$>$>$;$;$O@c@c@c@c@c@c@c@!.E@9$v c@c@c@c@c@c@c@c@f$f$0@0@h <@l$y$<@t$H$c@c@c@c@c@c@c.O#c.&@A$A$8.B$8.{ I$_ > S+/.J$/.h#: #@ ", +" e#e#Y#v#n#n#o#p#q#q#q#A#z#s#J#K#X#:.:.Q#e#e#e#Z#0 c@c@c@c@c@c@b$*$b$=#=#)$c@c@c@c@c@c@O@;$a.a.]$].6$c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@d <@<@r$:@<@H$U#u$u$U#c@c@c@c@c@c@7.7.8.I$2 I$_ S+S+S+/.g#/./.^.J+'.8#q @@{. ", +" :.:.F#p#q#q#q#A#s#J#s#X#:.:.|.L#Y#5 %$7#7#7# $`#]#c@c@c@c@c@c@c$-$-$-$>$;$;$c@c@c@c@c@c@!.!.!.}$v v c@c@c@c@c@c@c@c@c@c@c@h c@c@c@c@c@c@r$U#U#~@u$O#O#N#E$A$A$c@c@c@c@c@c@2 _ _ 1.J$J$/.K$h#J+J+L$'.(.M$N$%.O$%.l +@ ", +" z#V#z#$$W#X#r#:.:.Q#e#Q#R#Z#%$7#S#'$]#]#]#]#=#{#=#c@c@c@c@c@c@W@;$;$+.a.a.5$c@c@c@c@c@c@v u@1$1$k@k@d$c@c@c@c@c@c@c@c@c@<@P$c@c@c@c@c@c@z$O#v$b.A$A$A$B$B$8.8.c@c@c@c@c@c@Q$K$J+R$J+J+B+L$'.(.(.%.O$%.S$T$-#-#` H+! ", +" p#F#w#:.:.:.Y#e#Z#Z#7#7#7#`#&$]#]#T#b$=#=#c$-$*#-$!$c@c@c@c@c@c@a.].]$]$!.E@}$9$c@c@c@c@c@c@e$f$f$f$f$i$h h c@c@c@c@c@s$U#U#u$c@c@c@c@c@c@A$ @6. @8.{ 2 U$_ _ _ c@c@c@c@c@c@B+J+_.(.A+A+^#%.%.%.%.S$` *.-#Y@Y@U V$#.q+E+ ", +" v#9.e#5 e#7#7#S#3$]#]#]#*$=$b$4$&#-$-$-$:$_$;$;$<$<${$a.].]$!.!.!.z v 8$v 8$x$k@k@d$j@f$f$p$f$h l$m$d <@<@:@U#t$u$W$O#O#O#v$v$c@c@c@c@c@c@{ { G$_ _ _ /./.h#X$^.^.8#(.,.(.(.A+%.%.Y$Z$S$-#-#*.Z Y@Y@U U . .`$`$[+[+e+o+ ", +" c#5#6#7#`#]#]#=$=$w$=#c$=#-$4$-$_$X@;$;${$a.{$a.a.a.6$!.h$7$}$2$2$k@k@k@n$/$f$f$f$k$k$h h q$m$<@P$r$U#U#U#U#u$O#O#c.E$A$E#7. @7.c@c@c@c@c@c@J$ %/./.X$J+J+_.B+(.(.A+M$^#%.%.S$S$-#-#*.&.Z Y@U . ....%+%[+[+@%#%#%J $%U+8+8+ ", +" 2#!#*$b$w$=#.$-$-$-$W@-$;$;$;${${$a.a.!.!.!.h$h$9$v v 2$2$1$e$k@k@j@p$h ,$h h l$h <@<@<@U#s$U#U#W$W$O#c@c@c@c@c@c@8.E#8.2 8._ c@c@c@c@c@c@R$g#J+_.8#,.(.A+M$%.A+%%S$-#-#-#` Y@Y@Y@V$ .9+ .[+`$.%[+[+N J J I &%,+P P P *%<+<+ ", +" ~ )#-$-$-$:$_$>$;$g$g$+.a.a.6$5$!.!.E@7$v v |$|$k@k@k@f$/$f$k$h k$h @$l$<@<@:@<@r$U#~@u$z$)@O#b.A$E$C$c@c@c@c@c@c@c@S+1.S+Q$c@c@c@c@c@c@c@L$(.(.N$%.%.%.Z$).-#-#=%*.Y@Y@U -% . . .[++%O V ;%J J J v@P W.>%A A A ,%'%V.K :+3+ ", +" )###,#;$;$g$a.+.a.a.[$a.!.h$9$E@v 8$v x$x$n$e$/$f$f$i$0@k$h q$q$d <@H$t$U#U#~@U#!@O#v$&@A$b.7.6.7.8.8.F$8.c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@O$%.S$Y$-#-#=%` )%Y@!%9+T T ..%P@[+N ;%O J J '+$%I P P B A A a@b@~%K r s s s F.2+=@_+ ", +" Y Y a.a.a.5$!.]$E@z z v u@u@k@x$k@e$f$f$j$f$h 0@h h d <@<@<@H$U#U#U#u$O#O#N#A$E$A$A$C$A$8.8.U$_ _ > > _ /./.c@c@c@c@c@c@c@c@c@c@c@c@c@c@c@-#Z )%&.Y@Y@T !% . .{%+%[+[+N V @%I $%,+&%P W.P A A A ,%K K G.]%s s s x.j j j u w.E E ", +" S@M@!.E@}$v v v 8$1$k@k@k@e$f$f$j$h k$h y$q$<@<@U#r$U#U#u$U#O#v$E$N#b.A$B$ @8.8.{ I$U$2 > %S+Q$h#/.g#J+,.J+,.c@c@c@c@c@c@c@c@c@c@c@c@` Y@!%!%T . .`$[+[+N O J J J $%I P P P P A A A ,%'%K s H.H.s ^%/%^%j t u t u.u.(%b a S.L+ ", +" D K@C@v u@k@k@k@j@f$/$f$f$h h h d d <@<@P$:@H$u$U#!@z$O#&@O#c.A$E#6.8.8.F$F$G$_ _ %> /.X$/.^.J+J+_.J+(.(.(.A+^#%.Y$c@c@c@c@c@c@c@c@T . .P@P@{%[+O N N J J $%P ,+B _%*%_%,%b@,%K K K H.s s s j j j i *@u.u.k :%(%a r.3 c s.3 ) R.1+ ", +" 2.s@k@e$j@j@p$f$p$h h q$<@<@<@s$t$t$U#U#W$O#O#c.A$A$A$A$ @7.8.{ { I$_ Q$J$> /./.g#J+J+J+J+8#(.(.M$A+%.%.S$T$-#-#*.)%Y@Y@U Y@-% .+%{%+%[+[+;%@%J $%&%P P X.X._%A A A V.,%K <%C C s /%s ^%u j u.u.u.u.a :%r.r.E.3 3 p.p.[%m.m.m.] 9 ^+ ", +" z@i@H@,$j$h @$m$d <@<@P$U#U#)@U#u$O#E$b.b.A$A$6.C$7.8.G$I$_ _ _ S+Q$/.K$R$^.J+'.(._.(.(.(.O$O$%.%.-#-#-#-#-#)%V$Y@T -% . . .{%[+N N J J J v@,+P _%>%X.B A '%A K G.G.<%r s /%{@j j j v.*@u.t.t.r.r.}%E.|%3 1%o.p.m.2%m.] 3%] / j.j.#+| | ", +" 9@9@h l$<@<@t$H$U#u$W$O#O#O#O#A$b.A$E# @8.8.8.I$_ _ /.S+1./.R$/.J+J+L$8#(.(.(.^#A+%.S$Z$%%-#*.-#)%=%Y@T V$-% ...`$+%[+O J J J '+&%J P P X._%A A V.A K K ]%C r ^%{@^%j j v.*@w.u.u.:%t.r.r.c q.3 1%1%3 m.4 ] 4 ] ] / * * * A.& & f.& < ++ ", +" 4%6@H$U#!@z$W$O#O#b.c.A$A$C$7.7.8.I$U$G$_ /.1.1././.R$^.J+_._.'.(.(.^#A+%.%.Z$T$-#-#=%)%Y@Y@T T . .`$`$+%[+[+O @%J ,+&%P P P W._%A A A K K K K G.s ^%F.s j u u j u u.k b r.|%s.s.3 3 [%p.[%m.m.] ^ ] ] k.( * = * - & $ % z.+ + # + .+.+ ", +" _@(@(@O#E$E$A$A$6.8.C$8.8.8._ _ _ S+S+/././.X$X$J+J+(.L$(.(.N$M$%.%.%%%%-#-#Y@)%Z 9+Y@ . . .`$+%[+O N O #%J J v@$%P P A *%A V.,%~%a@K K s s ^%^%5%/%i u.*@u.u.b r.r.r.|%3 s.3 3 1%m.m.m.] m.] 6%] * * i.- - - & + 7%+ + + y.. . . . . a+I.I. ", +" =.=.A$A$B$8. @I$F$G$G$_ S+ %/./.^.J+^.8#J+B+(.(.O$A+%.%.Y$%.T$-#-#=%Y@Y@!%-%U . ...[++%[+O @%J J '+P v@P W.B _%A A ,%,%K K <%s s {@{@^%j v.i t u.u.t.a r.r.r.E.E.3 o.D.m.m.C.l.3%] ( / j.* = A.& & g.g.z.+ d.y.. . . . . . . . . . . . I.I. ", +" >@>@>@8.8._ G$> > _ S+R$g#^.J+J+8#_.(.(.N$^#O$%.T$-#-#-#-#=%Y@Y@Y@!%9+ .[+P@..[+[+O J J J ,+$%P P B W.*%A ~%K K K <%s <%s F.5%/%j u v.u.w.u.k t.r.r.3 }%3 [%D.1%m.m.4 C.2%] ] ( 6%* A.- & & & + + + d.0+y.. . . . . . . . . . . . . . . . I.I.I. ", +" 1 1 _ J$/.J$/.R$J+R$J+8#_.(.(.M$O$O$%.Y$S$T$=%-#Z &.Y@Y@ .T ...`$[+[+N [+N J I '+v@P P W._%A A ,%A K ]%K C s s s s /%j v.w.j u.u.k :%r.|%r.q.3 3 3 n.m.m.2%C.] ] / B.* * * A.h.& z.+ + + 0++ 0+0+. . . . . . . . . . . . . . . . . . . . . I.I. ", +" : 3.R$K$J+J+(.L$(.A+N$N$%.%.Y$-#T$-#&.&.` Y@-%V$ . ...+%{%[+N [+J J ,+&%P P P P A A A A K K K K G.s s x.F.5%j j w.u.u.a u.r.r.r.3 q.c 1%1%o.m.m.2%C.] ] ] k.6%* = & ; & $ f.f.d.@ d.. . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I. ", +" |@d@8#(.(.A+^#%.Y$-#-#-#-#*.&.Y@Y@Y@9+9+ .+%+%[+[+[+[+#%J J v@P P W.*%_%A A a@'%K K H.C <%x.F.5%j j i i *@u.:%b r.r.r.}%3 3 3 [%o.o.2%m.^ ^ ] / 6%j.= h.= h.& & f.+ + + y.y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I. ", +" Q+[.l %.Y$Y$-#-#-#&.Z !%V$9+ . . . ..%[+V [+J J J v@J P P X._%A A a@V.~%]%K r s s s {@j j j i u.u.u.k (%r.}%|%c 3 1%p.3 m.^ C.] ] ] * * j.h.- ; & % 7%f.+ + + e.e.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I.I. ", +" ! ! -#)%Y@V$V$9+ . ...{%[+O [+@%J J '+I $%P P P A B A ~%b@V.<%r C s s s /%j j *@w.i u.u.k r.r.r.3 3 3 3 D.m.m.] 3%] ] 6%k.* * * = h.& & g.f.+ + y.# d.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I. ", +" #.#.E+U ..%[+{%[+V @%[+J J v@v@P B W.*%A V.a@'%K r <%C H.s /%j j j i *@u.u.k b t.r.q.|%|%3 3 n.1%4 m.3%] ] j.B.j.* * * & & & g.+ f.+ . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I.I. ", +" f+x+@.@.N+d+N+8+u+5+u+L L l+l+<+:+:+F >+>+>+2+2+x x c+(+E E T.E b+S.g S.g ) ) f n m m m 8 $+^+7 i+e 7 | | ~+K+K+++}+[ }+N.N.K.`.K.Z.K.8%I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I. ", +" 8+8+5+L L L <+D+:+4+F .@:+>+>+>+9%x x T+U.E U.E b+b+S.S.g w ) ) f R.n m m m ^+7 8 7 7 ]+Q.| | P.h+} < N.}+N.N.)+ +)+K.Z.Y.8%I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I.I. ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/src/Mod/Ship/InitGui.py b/src/Mod/Ship/InitGui.py index 05dbfecb5f..6cf4c9689e 100644 --- a/src/Mod/Ship/InitGui.py +++ b/src/Mod/Ship/InitGui.py @@ -34,13 +34,13 @@ class ShipWorkbench ( Workbench ): # ToolBar list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendToolbar("Ship design",list) - list = ["Ship_CreateTank"] + list = ["Ship_Weights", "Ship_CreateTank"] self.appendToolbar("Loading",list) # Menu list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendMenu("Ship design",list) - list = ["Ship_CreateTank"] + list = ["Ship_Weights", "Ship_CreateTank"] self.appendToolbar("Loading",list) Gui.addWorkbench(ShipWorkbench()) diff --git a/src/Mod/Ship/Instance.py b/src/Mod/Ship/Instance.py index 16d77f8689..adba4d22b1 100644 --- a/src/Mod/Ship/Instance.py +++ b/src/Mod/Ship/Instance.py @@ -663,7 +663,7 @@ class ViewProviderShip: def sections(obj): """ Returns the discretization points of sections, with the advantage that is a list of nSections lists, with the points. - @param Ship object + @param obj Ship object @return Sections points """ histogram = obj.nPoints[:] @@ -674,3 +674,42 @@ def sections(obj): for j in range(histogram[i],histogram[i+1]): sections[i].append(points[j]) return sections + +def weights(obj): + """ Returns Ship weights list. If weights has not been sets, + this tool creates it. + @param obj Ship object + @return Weights list. None if errors + """ + # Test if is a ship instance + props = obj.PropertiesList + try: + props.index("IsShip") + except ValueError: + return None + if not obj.IsShip: + return None + # Test if properties already exist + try: + props.index("WeightNames") + except ValueError: + obj.addProperty("App::PropertyStringList","WeightNames","Ship", str(Translator.translate("Ship Weights names"))).WeightNames=[Translator.translate("Lightweight").__str__()] + try: + props.index("WeightMass") + except ValueError: + # Compute mass aproximation + from shipHydrostatics import Tools + disp = Tools.Displacement(obj,obj.Draft,0.0) + obj.addProperty("App::PropertyFloatList","WeightMass","Ship", str(Translator.translate("Ship Weights masses"))).WeightMass=[1000.0 * disp[1]] + try: + props.index("WeightPos") + except ValueError: + # Compute mass aproximation + from shipHydrostatics import Tools + disp = Tools.Displacement(obj,obj.Draft,0.0) + obj.addProperty("App::PropertyVectorList","WeightPos","Ship", str(Translator.translate("Ship Weights centers of gravity"))).WeightPos=[Vector(disp[2],0.0,obj.Draft)] + # Setup list + weights = [] + for i in range(0,len(obj.WeightNames)): + weights.append([obj.WeightNames[i], obj.WeightMass[i], obj.WeightPos[i]]) + return weights diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index 0251da41d6..35c1957b7a 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -34,6 +34,9 @@ nobase_data_DATA = \ Icons/ReparametrizeIco.xpm \ Icons/Ship.xcf \ Icons/Ship.xpm \ + Icons/Weight.png \ + Icons/Weight.xcf \ + Icons/Weight.xpm \ Icons/Tank.png \ Icons/Tank.xcf \ Icons/Tank.xpm \ @@ -66,6 +69,9 @@ nobase_data_DATA = \ shipUtils/Math.py \ shipUtils/Paths.py \ shipUtils/Translator.py \ + tankWeights/__init__.py \ + tankWeights/TaskPanel.py \ + tankWeights/TaskPanel.ui \ tankCreateTank/__init__.py \ tankCreateTank/TaskPanel.py \ tankCreateTank/TaskPanel.ui diff --git a/src/Mod/Ship/ShipGui.py b/src/Mod/Ship/ShipGui.py index f62bebfb4d..71a9e75e49 100644 --- a/src/Mod/Ship/ShipGui.py +++ b/src/Mod/Ship/ShipGui.py @@ -84,6 +84,18 @@ class Hydrostatics: ToolTip = str(Translator.translate('Plot ship hydrostatics')) return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} +class SetWeights: + def Activated(self): + import tankWeights + tankWeights.load() + + def GetResources(self): + from shipUtils import Paths, Translator + IconPath = Paths.iconsPath() + "/Weight.png" + MenuText = str(Translator.translate('Set ship weights')) + ToolTip = str(Translator.translate('Set ship weights, tanks must be added later')) + return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} + class CreateTank: def Activated(self): import tankCreateTank @@ -101,4 +113,5 @@ FreeCADGui.addCommand('Ship_CreateShip', CreateShip()) FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw()) FreeCADGui.addCommand('Ship_AreasCurve', AreasCurve()) FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics()) +FreeCADGui.addCommand('Ship_Weights', SetWeights()) FreeCADGui.addCommand('Ship_CreateTank', CreateTank()) diff --git a/src/Mod/Ship/shipHydrostatics/Tools.py b/src/Mod/Ship/shipHydrostatics/Tools.py index a2b1f2c65f..3db0039573 100644 --- a/src/Mod/Ship/shipHydrostatics/Tools.py +++ b/src/Mod/Ship/shipHydrostatics/Tools.py @@ -116,7 +116,7 @@ def Displacement(ship, draft, trim): @param ship Selected ship instance @param draft Draft. @param trim Trim in degrees. - @return [areas,disp,xcb]: \n + @return [areas,disp,xcb,Cb]: \n areas : Area of each section \n disp: Ship displacement \n xcb: X bouyance center coordinate diff --git a/src/Mod/Ship/tankWeights/TaskPanel.py b/src/Mod/Ship/tankWeights/TaskPanel.py new file mode 100644 index 0000000000..14cda21e57 --- /dev/null +++ b/src/Mod/Ship/tankWeights/TaskPanel.py @@ -0,0 +1,212 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD as App +import FreeCADGui as Gui +# Qt library +from PyQt4 import QtGui,QtCore +# Module +from Instance import * +from shipUtils import Paths, Translator + +class TaskPanel: + def __init__(self): + self.ui = Paths.modulePath() + "/tankWeights/TaskPanel.ui" + self.ship = None + + def accept(self): + if not self.ship: + return False + # Setup lists + name = [] + mass = [] + pos = [] + for i in range(0,self.form.weights.rowCount() - 1): + item = self.form.weights.item(i,0) + name.append(item.text().__str__()) + item = self.form.weights.item(i,1) + mass.append(item.text().toFloat()[0]) + vec = [] + item = self.form.weights.item(i,2) + vec.append(item.text().toFloat()[0]) + item = self.form.weights.item(i,3) + vec.append(item.text().toFloat()[0]) + item = self.form.weights.item(i,4) + vec.append(item.text().toFloat()[0]) + pos.append(App.Base.Vector(vec[0],vec[1],vec[2])) + # Send to ship + self.ship.WeightNames = name[:] + self.ship.WeightMass = mass[:] + self.ship.WeightPos = pos[:] + return True + + def reject(self): + if not self.ship: + return False + return True + + def clicked(self, index): + pass + + def open(self): + pass + + def needsFullSpace(self): + return True + + def isAllowedAlterSelection(self): + return False + + def isAllowedAlterView(self): + return True + + def isAllowedAlterDocument(self): + return False + + def helpRequested(self): + pass + + def setupUi(self): + mw = self.getMainWindow() + form = mw.findChild(QtGui.QWidget, "TaskPanel") + form.weights = form.findChild(QtGui.QTableWidget, "Weights") + self.form = form + # Initial values + if self.initValues(): + return True + self.retranslateUi() + # Connect Signals and Slots + QtCore.QObject.connect(form.weights,QtCore.SIGNAL("cellChanged(int,int)"),self.onTableItem); + + def getMainWindow(self): + "returns the main window" + # using QtGui.qApp.activeWindow() isn't very reliable because if another + # widget than the mainwindow is active (e.g. a dialog) the wrong widget is + # returned + toplevel = QtGui.qApp.topLevelWidgets() + for i in toplevel: + if i.metaObject().className() == "Gui::MainWindow": + return i + raise Exception("No main window found") + + def initValues(self): + """ Get selected geometry. + @return False if sucessfully values initialized. + """ + # Get selected objects + selObjs = FreeCADGui.Selection.getSelection() + if not selObjs: + msg = Translator.translate("Ship instance must be selected (no object selected)\n") + App.Console.PrintError(msg) + return True + for i in range(0,len(selObjs)): + obj = selObjs[i] + # Test if is a ship instance + props = obj.PropertiesList + try: + props.index("IsShip") + except ValueError: + continue + if obj.IsShip: + # Test if another ship already selected + if self.ship: + msg = Translator.translate("More than one ship selected (extra ship will be neglected)\n") + App.Console.PrintWarning(msg) + break + self.ship = obj + # Test if any valid ship was selected + if not self.ship: + msg = Translator.translate("Ship instance must be selected (no valid ship found at selected objects)\n") + App.Console.PrintError(msg) + return True + # Get weights + w = weights(self.ship) + # Set the items + self.form.weights.setRowCount(len(w)+1) + for i in range(0,len(w)): + item = QtGui.QTableWidgetItem(w[i][0]) + self.form.weights.setItem(i,0,item) + string = '%g' % (w[i][1]) + item = QtGui.QTableWidgetItem(string) + self.form.weights.setItem(i,1,item) + string = '%g' % (w[i][2].x) + item = QtGui.QTableWidgetItem(string) + self.form.weights.setItem(i,2,item) + string = '%g' % (w[i][2].y) + item = QtGui.QTableWidgetItem(string) + self.form.weights.setItem(i,3,item) + string = '%g' % (w[i][2].z) + item = QtGui.QTableWidgetItem(string) + self.form.weights.setItem(i,4,item) + msg = Translator.translate("Ready to work\n") + App.Console.PrintMessage(msg) + return False + + def retranslateUi(self): + """ Set user interface locale strings. + """ + self.form.setWindowTitle(Translator.translate("Set weights")) + labels = [] + labels.append(Translator.translate("Name")) + labels.append(Translator.translate("Mass") + " [kg]") + labels.append(QtCore.QString("g.x [m]")) + labels.append(QtCore.QString("g.y [m]")) + labels.append(QtCore.QString("g.z [m]")) + self.form.weights.setHorizontalHeaderLabels(labels) + + def onTableItem(self, row, column): + """ Function called when an item of table is changed. + @param row Changed item row + @param column Changed item column + """ + item = self.form.weights.item(row,column) + # Row deletion + if column == 0: + if not item.text(): + self.form.weights.removeRow(row) + # Ensure that exist one empty item at the end + nRow = self.form.weights.rowCount() + last = self.form.weights.item(nRow-1,0) + if last: + if(last.text() != ''): + self.form.weights.setRowCount(nRow+1) + # Fields must be numbers + for i in range(0,self.form.weights.rowCount()-1): # Avoid last row + for j in range(1,self.form.weights.columnCount()): # Avoid name column + item = self.form.weights.item(i,j) + if not item: + item = QtGui.QTableWidgetItem('0.0') + self.form.weights.setItem(i,j,item) + continue + (number,flag) = item.text().toFloat() + if not flag: + item.setText('0.0') + +def createTask(): + panel = TaskPanel() + Gui.Control.showDialog(panel) + if panel.setupUi(): + Gui.Control.closeDialog(panel) + return None + return panel diff --git a/src/Mod/Ship/tankWeights/TaskPanel.ui b/src/Mod/Ship/tankWeights/TaskPanel.ui new file mode 100644 index 0000000000..23dc4ff1bb --- /dev/null +++ b/src/Mod/Ship/tankWeights/TaskPanel.ui @@ -0,0 +1,97 @@ + + + TaskPanel + + + + 0 + 0 + 260 + 256 + + + + Set wieghts + + + + + + true + + + 1 + + + 5 + + + false + + + 20 + + + false + + + false + + + + + Name + + + + + Mass [kg] + + + + + g.x [m] + + + + + g.y [m] + + + + + g.z [m] + + + + + Lightweight + + + + + 0.0 + + + + + 0.0 + + + + + 0.0 + + + + + 0.0 + + + + + + + + + diff --git a/src/Mod/Ship/tankWeights/__init__.py b/src/Mod/Ship/tankWeights/__init__.py new file mode 100644 index 0000000000..cbfb57d75d --- /dev/null +++ b/src/Mod/Ship/tankWeights/__init__.py @@ -0,0 +1,36 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD +import FreeCADGui + +# Qt libraries +from PyQt4 import QtGui,QtCore + +# Main object +import TaskPanel + +def load(): + """ Loads the tool """ + TaskPanel.createTask() From 17666cb08ad2eda3dc6a8a8b4205e2ebfd2b5091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Mon, 14 May 2012 14:42:14 +0200 Subject: [PATCH 15/69] Added annotations to ship weights definition tool --- src/Mod/Ship/CMakeLists.txt | 1 + src/Mod/Ship/Makefile.am | 1 + src/Mod/Ship/tankWeights/Preview.py | 106 ++++++++++++++++++++++++++ src/Mod/Ship/tankWeights/TaskPanel.py | 34 +++++++++ 4 files changed, 142 insertions(+) create mode 100644 src/Mod/Ship/tankWeights/Preview.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index a3c14ce960..c44e313aba 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -101,6 +101,7 @@ SOURCE_GROUP("shiputils" FILES ${ShipUtils_SRCS}) SET(ShipWeights_SRCS tankWeights/__init__.py + tankWeights/Preview.py tankWeights/TaskPanel.py tankWeights/TaskPanel.ui ) diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index 35c1957b7a..01b296c011 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -70,6 +70,7 @@ nobase_data_DATA = \ shipUtils/Paths.py \ shipUtils/Translator.py \ tankWeights/__init__.py \ + tankWeights/Preview.py \ tankWeights/TaskPanel.py \ tankWeights/TaskPanel.ui \ tankCreateTank/__init__.py \ diff --git a/src/Mod/Ship/tankWeights/Preview.py b/src/Mod/Ship/tankWeights/Preview.py new file mode 100644 index 0000000000..571155bb9d --- /dev/null +++ b/src/Mod/Ship/tankWeights/Preview.py @@ -0,0 +1,106 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD,FreeCADGui +from FreeCAD import Base +from FreeCAD import Part +# FreeCADShip modules +from shipUtils import Paths, Translator + +class Preview(object): + def __init__(self): + """ Constructor. + """ + self.objects = [] + + def reinit(self): + """ Reinitializate drawer. + """ + self.clean() + + def update(self, names, pos): + """ Update the 3D view printing annotations. + @param names Weight names. + @param pos Weight positions (FreeCAD::Base::Vector). + """ + # Destroy all previous entities + self.clean() + for i in range(0, len(names)): + # Draw gravity line + line = Part.makeLine((pos[i].x,pos[i].y,pos[i].z),(pos[i].x,pos[i].y,pos[i].z - 9.81)) + Part.show(line) + objs = FreeCAD.ActiveDocument.Objects + self.objects.append(objs[-1]) + objs[-1].Label = names[i] + 'Line' + # Draw circles + circle = Part.makeCircle(0.5, pos[i], Base.Vector(1.0,0.0,0.0)) + Part.show(circle) + objs = FreeCAD.ActiveDocument.Objects + self.objects.append(objs[-1]) + objs[-1].Label = names[i] + 'CircleX' + circle = Part.makeCircle(0.5, pos[i], Base.Vector(0.0,1.0,0.0)) + Part.show(circle) + objs = FreeCAD.ActiveDocument.Objects + self.objects.append(objs[-1]) + objs[-1].Label = names[i] + 'CircleY' + circle = Part.makeCircle(0.5, pos[i], Base.Vector(0.0,0.0,1.0)) + Part.show(circle) + objs = FreeCAD.ActiveDocument.Objects + self.objects.append(objs[-1]) + objs[-1].Label = names[i] + 'CircleZ' + # Draw annotation + self.objects.append(DrawText(names[i] + 'Text', names[i], Base.Vector(pos[i].x+1.0,pos[i].y,pos[i].z))) + + def clean(self): + """ Erase all annotations from screen. + """ + for i in range(0,len(self.objects)): + if not FreeCAD.ActiveDocument.getObject(self.objects[i].Name): + continue + FreeCAD.ActiveDocument.removeObject(self.objects[i].Name) + self.objects = [] + +def DrawText(name, string, position, displayMode="Screen", angle=0.0, justification="Left", colour=(0.00,0.00,0.00), size=12): + """ Draws a text in a desired position. + @param name Name of the object + @param string Text to draw (recommended format u'') + @param position Point to draw the text + @param angle Counter clockwise rotation of text + @param justification Alignement of the text ("Left", "Right" or "Center") + @param colour Colour of the text + @param size Font size + @return FreeCAD annotation object + """ + # Create the object + text = FreeCAD.ActiveDocument.addObject("App::Annotation",name) + # Set the text + text.LabelText = [string, u''] + # Set the options + text.Position = position + FreeCADGui.ActiveDocument.getObject(text.Name).Rotation = angle + FreeCADGui.ActiveDocument.getObject(text.Name).Justification = justification + FreeCADGui.ActiveDocument.getObject(text.Name).FontSize = size + FreeCADGui.ActiveDocument.getObject(text.Name).TextColor = colour + FreeCADGui.ActiveDocument.getObject(text.Name).DisplayMode = displayMode + return FreeCAD.ActiveDocument.getObject(text.Name) diff --git a/src/Mod/Ship/tankWeights/TaskPanel.py b/src/Mod/Ship/tankWeights/TaskPanel.py index 14cda21e57..a01f7c73c2 100644 --- a/src/Mod/Ship/tankWeights/TaskPanel.py +++ b/src/Mod/Ship/tankWeights/TaskPanel.py @@ -27,6 +27,7 @@ import FreeCADGui as Gui # Qt library from PyQt4 import QtGui,QtCore # Module +import Preview from Instance import * from shipUtils import Paths, Translator @@ -34,8 +35,10 @@ class TaskPanel: def __init__(self): self.ui = Paths.modulePath() + "/tankWeights/TaskPanel.ui" self.ship = None + self.preview = Preview.Preview() def accept(self): + self.preview.clean() if not self.ship: return False # Setup lists @@ -62,6 +65,7 @@ class TaskPanel: return True def reject(self): + self.preview.clean() if not self.ship: return False return True @@ -98,6 +102,21 @@ class TaskPanel: self.retranslateUi() # Connect Signals and Slots QtCore.QObject.connect(form.weights,QtCore.SIGNAL("cellChanged(int,int)"),self.onTableItem); + # Update screen + name = [] + pos = [] + for i in range(0,self.form.weights.rowCount() - 1): + item = self.form.weights.item(i,0) + name.append(item.text().__str__()) + vec = [] + item = self.form.weights.item(i,2) + vec.append(item.text().toFloat()[0]) + item = self.form.weights.item(i,3) + vec.append(item.text().toFloat()[0]) + item = self.form.weights.item(i,4) + vec.append(item.text().toFloat()[0]) + pos.append(App.Base.Vector(vec[0],vec[1],vec[2])) + self.preview.update(name, pos) def getMainWindow(self): "returns the main window" @@ -202,6 +221,21 @@ class TaskPanel: (number,flag) = item.text().toFloat() if not flag: item.setText('0.0') + # Update screen annotations + name = [] + pos = [] + for i in range(0,self.form.weights.rowCount() - 1): + item = self.form.weights.item(i,0) + name.append(item.text().__str__()) + vec = [] + item = self.form.weights.item(i,2) + vec.append(item.text().toFloat()[0]) + item = self.form.weights.item(i,3) + vec.append(item.text().toFloat()[0]) + item = self.form.weights.item(i,4) + vec.append(item.text().toFloat()[0]) + pos.append(App.Base.Vector(vec[0],vec[1],vec[2])) + self.preview.update(name, pos) def createTask(): panel = TaskPanel() From 48932c44d0c6f6335109ee76f87629d0977b1eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Tue, 22 May 2012 11:10:17 +0200 Subject: [PATCH 16/69] Fixed UI symbol --- src/Mod/Ship/tankCreateTank/TaskPanel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Ship/tankCreateTank/TaskPanel.py b/src/Mod/Ship/tankCreateTank/TaskPanel.py index ec6394638e..4eddbd03e7 100644 --- a/src/Mod/Ship/tankCreateTank/TaskPanel.py +++ b/src/Mod/Ship/tankCreateTank/TaskPanel.py @@ -139,7 +139,7 @@ class TaskPanel: """ Set user interface locale strings. """ self.form.setWindowTitle(Translator.translate("Create a new tank")) - name = Translator.translate("Filling level") + " (\%)" + name = Translator.translate("Filling level") + " (%)" self.form.findChild(QtGui.QLabel, "LevelLabel").setText(name) name = '\n' name = name + Translator.translate("Density") From 961613a49ac1eadf8d2fc998762450df874f6751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Tue, 22 May 2012 11:10:55 +0200 Subject: [PATCH 17/69] Started GZ curves computation tool development --- src/Mod/Ship/CMakeLists.txt | 15 +- src/Mod/Ship/InitGui.py | 4 +- src/Mod/Ship/Makefile.am | 5 +- src/Mod/Ship/ShipGui.py | 13 ++ src/Mod/Ship/TankInstance.py | 62 +++++++- src/Mod/Ship/tankGZ/TaskPanel.py | 233 +++++++++++++++++++++++++++++++ src/Mod/Ship/tankGZ/TaskPanel.ui | 140 +++++++++++++++++++ src/Mod/Ship/tankGZ/__init__.py | 36 +++++ 8 files changed, 503 insertions(+), 5 deletions(-) create mode 100644 src/Mod/Ship/tankGZ/TaskPanel.py create mode 100644 src/Mod/Ship/tankGZ/TaskPanel.ui create mode 100644 src/Mod/Ship/tankGZ/__init__.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index c44e313aba..3106dbc47b 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -114,7 +114,14 @@ SET(ShipCreateTank_SRCS ) SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) -SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS}) +SET(ShipGZ_SRCS + tankGZ/__init__.py + tankGZ/TaskPanel.py + tankGZ/TaskPanel.ui +) +SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) + +SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS} ${ShipGZ_SRCS}) ADD_CUSTOM_TARGET(Ship ALL SOURCES ${all_files} @@ -182,6 +189,12 @@ INSTALL( DESTINATION Mod/Ship/tankCreateTank ) +INSTALL( + FILES + ${ShipGZ_SRCS} + DESTINATION + Mod/Ship/tankGZ +) INSTALL( FILES ${ShipMain_SRCS} diff --git a/src/Mod/Ship/InitGui.py b/src/Mod/Ship/InitGui.py index 6cf4c9689e..aa8a409aff 100644 --- a/src/Mod/Ship/InitGui.py +++ b/src/Mod/Ship/InitGui.py @@ -34,13 +34,13 @@ class ShipWorkbench ( Workbench ): # ToolBar list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendToolbar("Ship design",list) - list = ["Ship_Weights", "Ship_CreateTank"] + list = ["Ship_Weights", "Ship_CreateTank", "Ship_GZ"] self.appendToolbar("Loading",list) # Menu list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendMenu("Ship design",list) - list = ["Ship_Weights", "Ship_CreateTank"] + list = ["Ship_Weights", "Ship_CreateTank", "Ship_GZ"] self.appendToolbar("Loading",list) Gui.addWorkbench(ShipWorkbench()) diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index 01b296c011..54b7ecf6a5 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -75,7 +75,10 @@ nobase_data_DATA = \ tankWeights/TaskPanel.ui \ tankCreateTank/__init__.py \ tankCreateTank/TaskPanel.py \ - tankCreateTank/TaskPanel.ui + tankCreateTank/TaskPanel.ui \ + tankGZ/__init__.py \ + tankGZ/TaskPanel.py \ + tankGZ/TaskPanel.ui CLEANFILES = $(BUILT_SOURCES) diff --git a/src/Mod/Ship/ShipGui.py b/src/Mod/Ship/ShipGui.py index 71a9e75e49..c97f1e9087 100644 --- a/src/Mod/Ship/ShipGui.py +++ b/src/Mod/Ship/ShipGui.py @@ -108,6 +108,18 @@ class CreateTank: ToolTip = str(Translator.translate('Create a new ship tank')) return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} +class GZ: + def Activated(self): + import tankGZ + tankGZ.load() + + def GetResources(self): + from shipUtils import Paths, Translator + IconPath = Paths.iconsPath() + "/HydrostaticsIco.png" + MenuText = str(Translator.translate('GZ curve')) + ToolTip = str(Translator.translate('Transversal stability GZ curve computation')) + return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} + FreeCADGui.addCommand('Ship_LoadExample', LoadExample()) FreeCADGui.addCommand('Ship_CreateShip', CreateShip()) FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw()) @@ -115,3 +127,4 @@ FreeCADGui.addCommand('Ship_AreasCurve', AreasCurve()) FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics()) FreeCADGui.addCommand('Ship_Weights', SetWeights()) FreeCADGui.addCommand('Ship_CreateTank', CreateTank()) +FreeCADGui.addCommand('Ship_GZ', GZ()) diff --git a/src/Mod/Ship/TankInstance.py b/src/Mod/Ship/TankInstance.py index 584aa0f974..6e44795f86 100644 --- a/src/Mod/Ship/TankInstance.py +++ b/src/Mod/Ship/TankInstance.py @@ -45,7 +45,7 @@ class ShipTank: # Add uniqueness property to identify Tank instances obj.addProperty("App::PropertyBool","IsShipTank","ShipTank", str(Translator.translate("True if is a valid ship tank instance"))).IsShipTank=True # Add general options - obj.addProperty("App::PropertyFloat","Level","ShipTank", str(Translator.translate("Filling level"))).Level=level + obj.addProperty("App::PropertyFloat","Level","ShipTank", str(Translator.translate("Fluid filling level percentage"))).Level=level obj.addProperty("App::PropertyFloat","Density","ShipTank", str(Translator.translate("Inside fluid density"))).Density=density # Add shapes shape = self.computeShape(solid) @@ -1884,3 +1884,63 @@ class ViewProviderShipTank: " ", " "}; """ + +def tankWeight(obj, angles=Vector(0.0,0.0,0.0), cor=Vector(0.0,0.0,0.0)): + """ Compute tank fluid weight and their center of gravity. + @param obj Tank object. + @param angles Tank angles, Roll, Pitch and Yaw. + @param cor Center or rotation. + @return Weight and center of gravity. None if errors detected + """ + # Test if is a tank instance + props = obj.PropertiesList + try: + props.index("IsShipTank") + except ValueError: + return None + if not obj.IsShipTank: + return None + # Get object solids + Solids = obj.Shape.Solids + W = [0.0, 0.0, 0.0, 0.0] + for s in Solids: + # Get fluid volume + bbox = s.BoundBox + z0 = bbox.ZMin + z1 = bbox.ZMax + dz = obj.Level/100.0 * (z1-z0) + z = z0 + dz + dx = bbox.XMax-bbox.XMin + dy = bbox.YMax-bbox.YMin + box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) + fluid = s.common(box) + vol = fluid.Volume + W[0] = W[0] + vol*obj.Density + # Compute fluid solid in rotated position (non linear rotation + # are ussually computed as Roll -> Pitch -> Yaw). + s.rotate(cor, Vector(1.0,0.0,0.0), angles.x) + s.rotate(cor, Vector(0.0,1.0,0.0), angles.y) + s.rotate(cor, Vector(0.0,0.0,1.0), angles.z) + bbox = s.BoundBox + z0 = bbox.ZMin + z1 = bbox.ZMax + dx = bbox.XMax-bbox.XMin + dy = bbox.YMax-bbox.YMin + Error = 0.01*vol + z = 0.0 + v = 0.0 + while(abs(vol - v) > Error): + z = z + (vol - v) / (dx*dy) + dz = z - z0 + box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) + fluid = s.common(box) + v = fluid.Volume + if(abs(vol - v) / (dx*dy) <= 0.000001): + break + # Add fluid moments + for f in fluid.Solids: + cog = f.CenterOfMass + W[1] = W[1] + f.Volume*obj.Density*cog.x + W[2] = W[2] + f.Volume*obj.Density*cog.y + W[3] = W[3] + f.Volume*obj.Density*cog.z + return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py new file mode 100644 index 0000000000..8a47e67ae0 --- /dev/null +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -0,0 +1,233 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD as App +import FreeCADGui as Gui +# Qt library +from PyQt4 import QtGui,QtCore +# Module +from Instance import * +from TankInstance import * +from shipUtils import Paths, Translator + +class TaskPanel: + def __init__(self): + self.ui = Paths.modulePath() + "/tankGZ/TaskPanel.ui" + self.ship = None + self.tanks = {} + + def accept(self): + if not self.ship: + return False + return True + + def reject(self): + if not self.ship: + return False + return True + + def clicked(self, index): + pass + + def open(self): + pass + + def needsFullSpace(self): + return True + + def isAllowedAlterSelection(self): + return False + + def isAllowedAlterView(self): + return True + + def isAllowedAlterDocument(self): + return False + + def helpRequested(self): + pass + + def setupUi(self): + mw = self.getMainWindow() + form = mw.findChild(QtGui.QWidget, "TaskPanel") + form.tanks = form.findChild(QtGui.QListWidget, "Tanks") + form.disp = form.findChild(QtGui.QLabel, "DisplacementLabel") + form.draft = form.findChild(QtGui.QLabel, "DraftLabel") + self.form = form + # Initial values + if self.initValues(): + return True + self.retranslateUi() + self.onTanksSelection() + # Connect Signals and Slots + QtCore.QObject.connect(form.tanks,QtCore.SIGNAL("itemSelectionChanged()"),self.onTanksSelection) + return False + + def getMainWindow(self): + "returns the main window" + # using QtGui.qApp.activeWindow() isn't very reliable because if another + # widget than the mainwindow is active (e.g. a dialog) the wrong widget is + # returned + toplevel = QtGui.qApp.topLevelWidgets() + for i in toplevel: + if i.metaObject().className() == "Gui::MainWindow": + return i + raise Exception("No main window found") + + def initValues(self): + """ Get selected geometry. + @return False if sucessfully values initialized. + """ + # Get selected objects + selObjs = FreeCADGui.Selection.getSelection() + if not selObjs: + msg = Translator.translate("Ship instance must be selected (no object selected)\n") + App.Console.PrintError(msg) + return True + for i in range(0,len(selObjs)): + obj = selObjs[i] + # Test if is a ship instance + props = obj.PropertiesList + try: + props.index("IsShip") + except ValueError: + continue + if obj.IsShip: + # Test if another ship already selected + if self.ship: + msg = Translator.translate("More than one ship selected (extra ship will be neglected)\n") + App.Console.PrintWarning(msg) + break + self.ship = obj + # Test if any valid ship was selected + if not self.ship: + msg = Translator.translate("Ship instance must be selected (no valid ship found at selected objects)\n") + App.Console.PrintError(msg) + return True + props = self.ship.PropertiesList + try: + props.index("WeightNames") + except: + msg = Translator.translate("Ship weights has not been set. You need to set weights before use this tool.\n") + App.Console.PrintError(msg) + return True + # Setup available tanks list + objs = App.ActiveDocument.Objects + iconPath = Paths.iconsPath() + "/Tank.xpm" + icon = QtGui.QIcon(QtGui.QPixmap(iconPath)) + for obj in objs: + # Try to get valid tank property + props = obj.PropertiesList + try: + props.index("IsShipTank") + except ValueError: + continue + if not obj.IsShipTank: + continue + # Add tank to list + name = obj.Name + label = obj.Label + tag = label + ' (' + name + ')' + self.tanks[tag] = name + # self.tanks.append([name, tag]) + item = QtGui.QListWidgetItem(tag) + item.setIcon(icon) + self.form.tanks.addItem(item) + msg = Translator.translate("Ready to work\n") + App.Console.PrintMessage(msg) + return False + + def retranslateUi(self): + """ Set user interface locale strings. + """ + self.form.setWindowTitle(Translator.translate("GZ curve computation")) + self.form.findChild(QtGui.QGroupBox, "LoadConditionGroup").setTitle(Translator.translate("Loading condition.")) + + def onTanksSelection(self): + """ Called when tanks are selected or deselected. + """ + # Set displacement label + disp = self.computeDisplacement() + self.form.disp.setText(Translator.translate("Displacement") + ' %g [kg]' % (disp[0])) + + def getTanks(self): + """ Get the selected tanks objects list. + @return Selected tanks list. + """ + items = self.form.tanks.selectedItems() + tanks = [] + for item in items: + tag = str(item.text()) + name = self.tanks[tag] + t = App.ActiveDocument.getObject('Tank') + if not t: + continue + tanks.append(t) + return tanks + + def computeDisplacement(self): + """ Computes ship displacement. + @return Ship displacement and center of gravity. None if errors detected. + """ + if not self.ship: + return None + # Test if is a ship instance + obj = self.ship + props = obj.PropertiesList + try: + props.index("IsShip") + except ValueError: + return None + if not obj.IsShip: + return None + # Test if properties already exist + try: + props.index("WeightNames") + except: + return None + # Get ship structure weights + W = [0.0, 0.0, 0.0, 0.0] + sWeights = weights(obj) + for w in sWeights: + W[0] = W[0] + w[1] + W[1] = W[1] + w[1]*w[2][0] + W[2] = W[2] + w[1]*w[2][1] + W[3] = W[3] + w[1]*w[2][2] + # Get selected tanks weights + tanks = self.getTanks() + for t in tanks: + w = tankWeight(t) + W[0] = W[0] + w[0] + W[1] = W[1] + w[0]*w[1] + W[2] = W[2] + w[0]*w[2] + W[3] = W[3] + w[0]*w[3] + return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] + +def createTask(): + panel = TaskPanel() + Gui.Control.showDialog(panel) + if panel.setupUi(): + Gui.Control.closeDialog(panel) + return None + return panel diff --git a/src/Mod/Ship/tankGZ/TaskPanel.ui b/src/Mod/Ship/tankGZ/TaskPanel.ui new file mode 100644 index 0000000000..a1f16c39b7 --- /dev/null +++ b/src/Mod/Ship/tankGZ/TaskPanel.ui @@ -0,0 +1,140 @@ + + + TaskPanel + + + + 0 + 0 + 256 + 368 + + + + + 256 + 368 + + + + GZ curve computation + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Loading condition + + + + + 0 + 20 + 231 + 151 + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::MultiSelection + + + + + + + Displacement = 0 [kg] + + + + + + + Draft = 0 [m] + + + + + + + + + + + + 0 + 0 + + + + Roll angles + + + + + 0 + 20 + 231 + 141 + + + + + QLayout::SetMinimumSize + + + + + Start [deg] + + + + + + + End [deg] + + + + + + + Number of points + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Ship/tankGZ/__init__.py b/src/Mod/Ship/tankGZ/__init__.py new file mode 100644 index 0000000000..cbfb57d75d --- /dev/null +++ b/src/Mod/Ship/tankGZ/__init__.py @@ -0,0 +1,36 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD +import FreeCADGui + +# Qt libraries +from PyQt4 import QtGui,QtCore + +# Main object +import TaskPanel + +def load(): + """ Loads the tool """ + TaskPanel.createTask() From 5a106e7c057ad6e1a154377094c552f91773432f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Wed, 23 May 2012 19:04:35 +0200 Subject: [PATCH 18/69] Added draft computation to GZ tool. --- src/Mod/Ship/tankGZ/TaskPanel.py | 47 ++++++++++++++++++++------------ src/Mod/Ship/tankGZ/TaskPanel.ui | 2 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index 8a47e67ae0..9864bfd10c 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.py +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -30,12 +30,14 @@ from PyQt4 import QtGui,QtCore from Instance import * from TankInstance import * from shipUtils import Paths, Translator +from shipHydrostatics import Tools as Hydrostatics class TaskPanel: def __init__(self): self.ui = Paths.modulePath() + "/tankGZ/TaskPanel.ui" self.ship = None self.tanks = {} + self.trim = 0.0 def accept(self): if not self.ship: @@ -163,14 +165,18 @@ class TaskPanel: """ self.form.setWindowTitle(Translator.translate("GZ curve computation")) self.form.findChild(QtGui.QGroupBox, "LoadConditionGroup").setTitle(Translator.translate("Loading condition.")) + self.form.findChild(QtGui.QGroupBox, "AnglesGroup").setTitle(Translator.translate("Roll angles.")) def onTanksSelection(self): """ Called when tanks are selected or deselected. """ # Set displacement label disp = self.computeDisplacement() - self.form.disp.setText(Translator.translate("Displacement") + ' %g [kg]' % (disp[0])) - + self.form.disp.setText(Translator.translate("Displacement") + ' = %g [kg]' % (disp[0])) + # Set draft label + draft = self.computeDraft(disp[0]) + self.form.draft.setText(Translator.translate("Draft") + ' = %g [m]' % (draft)) + def getTanks(self): """ Get the selected tanks objects list. @return Selected tanks list. @@ -192,23 +198,9 @@ class TaskPanel: """ if not self.ship: return None - # Test if is a ship instance - obj = self.ship - props = obj.PropertiesList - try: - props.index("IsShip") - except ValueError: - return None - if not obj.IsShip: - return None - # Test if properties already exist - try: - props.index("WeightNames") - except: - return None # Get ship structure weights W = [0.0, 0.0, 0.0, 0.0] - sWeights = weights(obj) + sWeights = weights(self.ship) for w in sWeights: W[0] = W[0] + w[1] W[1] = W[1] + w[1]*w[2][0] @@ -224,6 +216,27 @@ class TaskPanel: W[3] = W[3] + w[0]*w[3] return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] + def computeDraft(self, disp): + """ Computes ship draft. + @param disp Ship displacement. + @return Ship draft. None if errors detected. + @note 0 trim will be assumed. + """ + if not self.ship: + return None + # Initial condition + trim = 0.0 + dens = 1025 + bbox = self.ship.Shape.BoundBox + draft = bbox.ZMin + dx = bbox.XMax - bbox.XMin + dy = bbox.YMax - bbox.YMin + w = 0.0 + while(abs(disp - w)/disp > 0.01): + draft = draft + (disp - w) / (dens*dx*dy) + w = 1000.0*Hydrostatics.Displacement(self.ship, draft, trim)[1] + return draft + def createTask(): panel = TaskPanel() Gui.Control.showDialog(panel) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.ui b/src/Mod/Ship/tankGZ/TaskPanel.ui index a1f16c39b7..41823bf714 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.ui +++ b/src/Mod/Ship/tankGZ/TaskPanel.ui @@ -76,7 +76,7 @@ - + 0 From f2117880a0f9f86cf6fcf79a0a37e4160c81435d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Sat, 26 May 2012 15:33:29 +0200 Subject: [PATCH 19/69] Added trim option to GZ curves tool --- src/Mod/Ship/shipHydrostatics/Tools.py | 6 +- src/Mod/Ship/tankGZ/TaskPanel.py | 106 ++++++++++++++++++++----- src/Mod/Ship/tankGZ/TaskPanel.ui | 84 +++++++++++++++++--- 3 files changed, 163 insertions(+), 33 deletions(-) diff --git a/src/Mod/Ship/shipHydrostatics/Tools.py b/src/Mod/Ship/shipHydrostatics/Tools.py index 3db0039573..2c0e5aa36b 100644 --- a/src/Mod/Ship/shipHydrostatics/Tools.py +++ b/src/Mod/Ship/shipHydrostatics/Tools.py @@ -439,9 +439,9 @@ def KBT(ship, draft, trim, roll=0.0): @param draft Draft. @param trim Trim in degrees. @param roll Roll angle in degrees. - @return [KBTx, KBTy]: \n - KBTy : TRansversal KB y coordinate \n - KBTz : TRansversal KB z coordinate + @return [KBTy, KBTz]: \n + KBTy : Transversal KB y coordinate \n + KBTz : Transversal KB z coordinate """ angle = math.radians(trim) rAngle = math.radians(roll) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index 9864bfd10c..48a23242bb 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.py +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -21,6 +21,7 @@ #* * #*************************************************************************** +import math # FreeCAD modules import FreeCAD as App import FreeCADGui as Gui @@ -37,7 +38,6 @@ class TaskPanel: self.ui = Paths.modulePath() + "/tankGZ/TaskPanel.ui" self.ship = None self.tanks = {} - self.trim = 0.0 def accept(self): if not self.ship: @@ -71,11 +71,13 @@ class TaskPanel: pass def setupUi(self): - mw = self.getMainWindow() - form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.tanks = form.findChild(QtGui.QListWidget, "Tanks") - form.disp = form.findChild(QtGui.QLabel, "DisplacementLabel") - form.draft = form.findChild(QtGui.QLabel, "DraftLabel") + mw = self.getMainWindow() + form = mw.findChild(QtGui.QWidget, "TaskPanel") + form.tanks = form.findChild(QtGui.QListWidget, "Tanks") + form.disp = form.findChild(QtGui.QLabel, "DisplacementLabel") + form.draft = form.findChild(QtGui.QLabel, "DraftLabel") + form.trim = form.findChild(QtGui.QDoubleSpinBox, "Trim") + form.autoTrim = form.findChild(QtGui.QPushButton, "TrimAutoCompute") self.form = form # Initial values if self.initValues(): @@ -84,6 +86,8 @@ class TaskPanel: self.onTanksSelection() # Connect Signals and Slots QtCore.QObject.connect(form.tanks,QtCore.SIGNAL("itemSelectionChanged()"),self.onTanksSelection) + QtCore.QObject.connect(form.trim,QtCore.SIGNAL("valueChanged(double)"),self.onTrim) + QtCore.QObject.connect(form.autoTrim,QtCore.SIGNAL("pressed()"),self.onAutoTrim) return False def getMainWindow(self): @@ -166,6 +170,10 @@ class TaskPanel: self.form.setWindowTitle(Translator.translate("GZ curve computation")) self.form.findChild(QtGui.QGroupBox, "LoadConditionGroup").setTitle(Translator.translate("Loading condition.")) self.form.findChild(QtGui.QGroupBox, "AnglesGroup").setTitle(Translator.translate("Roll angles.")) + self.form.findChild(QtGui.QLabel, "TrimLabel").setText(Translator.translate("Trim") + " [deg]") + self.form.findChild(QtGui.QLabel, "StartAngleLabel").setText(Translator.translate("Start") + " [deg]") + self.form.findChild(QtGui.QLabel, "EndAngleLabel").setText(Translator.translate("Start") + " [deg]") + self.form.findChild(QtGui.QLabel, "NAngleLabel").setText(Translator.translate("Number of points")) def onTanksSelection(self): """ Called when tanks are selected or deselected. @@ -174,8 +182,59 @@ class TaskPanel: disp = self.computeDisplacement() self.form.disp.setText(Translator.translate("Displacement") + ' = %g [kg]' % (disp[0])) # Set draft label - draft = self.computeDraft(disp[0]) - self.form.draft.setText(Translator.translate("Draft") + ' = %g [m]' % (draft)) + draft = self.computeDraft(disp[0], self.form.trim.value()) + self.form.draft.setText(Translator.translate("Draft") + ' = %g [m]' % (draft[0])) + + def onTrim(self, trim): + """ Called when trim angle value is changed. + @param trim Selected trim angle. + """ + self.onTanksSelection() + + def onAutoTrim(self): + """ Called when trim angle must be auto computed. + """ + # Start at null trim angle + trim = 0.0 + # Get center of gravity + disp = self.computeDisplacement(trim) + G = [disp[1], disp[2], disp[3]] + disp = disp[0] + # Get bouyancy center + draft = self.computeDraft(disp) + xcb = draft[1] + draft = draft[0] + KBT = Hydrostatics.KBT(self.ship, draft, trim) + B = [xcb, KBT[0], KBT[1]] + # Get stability initial condition + BG = [G[0]-B[0], G[1]-B[1], G[2]-B[2]] + x = BG[0]*math.cos(math.radians(trim)) - BG[2]*math.sin(math.radians(trim)) + y = BG[1] + z = BG[0]*math.sin(math.radians(trim)) + BG[2]*math.cos(math.radians(trim)) + var = math.degrees(math.atan2(x,z)) + # Iterate looking stability point + dVar = math.copysign(0.0033, var) + while True: + if (dVar*math.copysign(dVar, var) < 0.0): + break + trim = trim - math.copysign(dVar, var) + # Get center of gravity + disp = self.computeDisplacement(trim) + G = [disp[1], disp[2], disp[3]] + disp = disp[0] + # Get bouyancy center + draft = self.computeDraft(disp, trim) + xcb = draft[1] + draft = draft[0] + KBT = Hydrostatics.KBT(self.ship, draft, trim) + B = [xcb, KBT[0], KBT[1]] + # Get stability initial condition + BG = [G[0]-B[0], G[1]-B[1], G[2]-B[2]] + x = BG[0]*math.cos(math.radians(trim)) - BG[2]*math.sin(math.radians(trim)) + y = BG[1] + z = BG[0]*math.sin(math.radians(trim)) + BG[2]*math.cos(math.radians(trim)) + var = math.degrees(math.atan2(x,z)) + self.form.trim.setValue(trim) def getTanks(self): """ Get the selected tanks objects list. @@ -186,14 +245,15 @@ class TaskPanel: for item in items: tag = str(item.text()) name = self.tanks[tag] - t = App.ActiveDocument.getObject('Tank') + t = App.ActiveDocument.getObject(name) if not t: continue tanks.append(t) return tanks - def computeDisplacement(self): + def computeDisplacement(self, trim=0.0): """ Computes ship displacement. + @param trim Trim angle [degrees]. @return Ship displacement and center of gravity. None if errors detected. """ if not self.ship: @@ -209,33 +269,39 @@ class TaskPanel: # Get selected tanks weights tanks = self.getTanks() for t in tanks: - w = tankWeight(t) + w = tankWeight(t, App.Base.Vector(0.0,-trim,0.0)) + # Unrotate center of gravity + x = w[1]*math.cos(math.radians(-trim)) - w[3]*math.sin(math.radians(-trim)) + y = w[2] + z = w[1]*math.sin(math.radians(-trim)) + w[3]*math.cos(math.radians(-trim)) W[0] = W[0] + w[0] - W[1] = W[1] + w[0]*w[1] - W[2] = W[2] + w[0]*w[2] - W[3] = W[3] + w[0]*w[3] + W[1] = W[1] + w[0]*x + W[2] = W[2] + w[0]*y + W[3] = W[3] + w[0]*z return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] - def computeDraft(self, disp): + def computeDraft(self, disp, trim=0.0): """ Computes ship draft. @param disp Ship displacement. - @return Ship draft. None if errors detected. - @note 0 trim will be assumed. + @param trim Trim angle [degrees]. + @return Ship draft, and longitudinal bouyance center position. None if errors detected. """ if not self.ship: return None # Initial condition - trim = 0.0 dens = 1025 bbox = self.ship.Shape.BoundBox draft = bbox.ZMin dx = bbox.XMax - bbox.XMin dy = bbox.YMax - bbox.YMin w = 0.0 + xcb = 0.0 while(abs(disp - w)/disp > 0.01): draft = draft + (disp - w) / (dens*dx*dy) - w = 1000.0*Hydrostatics.Displacement(self.ship, draft, trim)[1] - return draft + ww = Hydrostatics.Displacement(self.ship, draft, trim) + w = 1000.0*ww[1] + xcb = ww[2] + return [draft,xcb] def createTask(): panel = TaskPanel() diff --git a/src/Mod/Ship/tankGZ/TaskPanel.ui b/src/Mod/Ship/tankGZ/TaskPanel.ui index 41823bf714..3742ecb820 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.ui +++ b/src/Mod/Ship/tankGZ/TaskPanel.ui @@ -7,13 +7,13 @@ 0 0 256 - 368 + 408 256 - 368 + 408 @@ -25,7 +25,7 @@ 0 - 0 + 6 @@ -43,7 +43,7 @@ 0 20 231 - 151 + 231 @@ -71,6 +71,55 @@ + + + + + + + 3 + 0 + + + + Trim [deg] + + + + + + + + 3 + 0 + + + + -45.000000000000000 + + + 45.000000000000000 + + + 0.100000000000000 + + + + + + + + 1 + 0 + + + + Auto + + + + + @@ -78,9 +127,9 @@ - + 0 - 0 + 3 @@ -92,7 +141,7 @@ 0 20 231 - 141 + 101 @@ -121,13 +170,28 @@ - + + + 0.000000000000000 + + + 90.000000000000000 + + - + + + 90.000000000000000 + + - + + + 10000 + + From 0044f0dc63b03d8acd420e6a189a6746b861ab9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Sun, 27 May 2012 14:31:10 +0200 Subject: [PATCH 20/69] Added roll angles options controller at GZ curves tool --- src/Mod/Ship/tankGZ/TaskPanel.py | 20 +++++++++++++++++++- src/Mod/Ship/tankGZ/TaskPanel.ui | 9 +++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index 48a23242bb..a577ee53c1 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.py +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -78,6 +78,9 @@ class TaskPanel: form.draft = form.findChild(QtGui.QLabel, "DraftLabel") form.trim = form.findChild(QtGui.QDoubleSpinBox, "Trim") form.autoTrim = form.findChild(QtGui.QPushButton, "TrimAutoCompute") + form.roll0 = form.findChild(QtGui.QDoubleSpinBox, "StartAngle") + form.roll1 = form.findChild(QtGui.QDoubleSpinBox, "EndAngle") + form.nRoll = form.findChild(QtGui.QSpinBox, "NAngle") self.form = form # Initial values if self.initValues(): @@ -88,6 +91,9 @@ class TaskPanel: QtCore.QObject.connect(form.tanks,QtCore.SIGNAL("itemSelectionChanged()"),self.onTanksSelection) QtCore.QObject.connect(form.trim,QtCore.SIGNAL("valueChanged(double)"),self.onTrim) QtCore.QObject.connect(form.autoTrim,QtCore.SIGNAL("pressed()"),self.onAutoTrim) + QtCore.QObject.connect(form.roll0,QtCore.SIGNAL("valueChanged(double)"),self.onRoll) + QtCore.QObject.connect(form.roll1,QtCore.SIGNAL("valueChanged(double)"),self.onRoll) + QtCore.QObject.connect(form.nRoll,QtCore.SIGNAL("valueChanged(int)"),self.onRoll) return False def getMainWindow(self): @@ -236,6 +242,15 @@ class TaskPanel: var = math.degrees(math.atan2(x,z)) self.form.trim.setValue(trim) + def onRoll(self, value): + """ Called when roll angles options are modified. + @param value Dummy changed value. + """ + roll0 = self.form.roll0.value() + self.form.roll1.setMinimum(roll0) + roll1 = self.form.roll1.value() + self.form.roll0.setMaximum(roll1) + def getTanks(self): """ Get the selected tanks objects list. @return Selected tanks list. @@ -254,7 +269,10 @@ class TaskPanel: def computeDisplacement(self, trim=0.0): """ Computes ship displacement. @param trim Trim angle [degrees]. - @return Ship displacement and center of gravity. None if errors detected. + @return Ship displacement and center of gravity. None if errors + detected. + @note Returned center of gravity is refered to ship attached + axis coordinates. """ if not self.ship: return None diff --git a/src/Mod/Ship/tankGZ/TaskPanel.ui b/src/Mod/Ship/tankGZ/TaskPanel.ui index 3742ecb820..c1cad3e014 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.ui +++ b/src/Mod/Ship/tankGZ/TaskPanel.ui @@ -184,13 +184,22 @@ 90.000000000000000 + + 90.000000000000000 + + + 2 + 10000 + + 90 + From 0955fe85a748782ee4c87ce9d67a24387562eb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Sun, 27 May 2012 15:41:57 +0200 Subject: [PATCH 21/69] Added GZ computation capabilities. --- src/Mod/Ship/tankGZ/TaskPanel.py | 47 ++++++++++++++++++++++++++++---- src/Mod/Ship/tankGZ/TaskPanel.ui | 6 ++-- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index a577ee53c1..72a7ef057c 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.py +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -42,6 +42,20 @@ class TaskPanel: def accept(self): if not self.ship: return False + # Get general data + disp = self.computeDisplacement() + draft = self.computeDraft(disp[0], self.form.trim.value()) + trim = self.form.trim.value() + # Get roll angles + roll0 = self.form.roll0.value() + roll1 = self.form.roll1.value() + nRoll = self.form.nRoll.value() + dRoll = (roll1 - roll0) / (nRoll - 1) + roll = [] + GZ = [] + for i in range(0, nRoll): + roll.append(i*dRoll) + GZ.append(self.computeGZ(draft[0], trim, roll[-1])) return True def reject(self): @@ -266,7 +280,7 @@ class TaskPanel: tanks.append(t) return tanks - def computeDisplacement(self, trim=0.0): + def computeDisplacement(self, trim=0.0, roll=0.0): """ Computes ship displacement. @param trim Trim angle [degrees]. @return Ship displacement and center of gravity. None if errors @@ -287,15 +301,18 @@ class TaskPanel: # Get selected tanks weights tanks = self.getTanks() for t in tanks: - w = tankWeight(t, App.Base.Vector(0.0,-trim,0.0)) + w = tankWeight(t, App.Base.Vector(roll,-trim,0.0)) # Unrotate center of gravity x = w[1]*math.cos(math.radians(-trim)) - w[3]*math.sin(math.radians(-trim)) y = w[2] z = w[1]*math.sin(math.radians(-trim)) + w[3]*math.cos(math.radians(-trim)) + w[1] = x + w[2] = y*math.cos(math.radians(-roll)) - z*math.sin(math.radians(-roll)) + w[3] = y*math.sin(math.radians(-roll)) + z*math.cos(math.radians(-roll)) W[0] = W[0] + w[0] - W[1] = W[1] + w[0]*x - W[2] = W[2] + w[0]*y - W[3] = W[3] + w[0]*z + W[1] = W[1] + w[0]*w[1] + W[2] = W[2] + w[0]*w[2] + W[3] = W[3] + w[0]*w[3] return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] def computeDraft(self, disp, trim=0.0): @@ -321,6 +338,26 @@ class TaskPanel: xcb = ww[2] return [draft,xcb] + def computeGZ(self, draft, trim, roll): + """ Compute GZ value. + @param draft Ship draft. + @param trim Ship trim angle [degrees]. + @param roll Ship roll angle [degrees]. + @return GZ value [m]. + """ + # Get center of gravity (x coordinate not relevant) + disp = self.computeDisplacement(trim, roll) + G = [disp[2], disp[3]] + disp = disp[0] + # Get bouyancy center (x coordinate not relevant) + KBT = Hydrostatics.KBT(self.ship, draft, trim, roll) + B = [KBT[0], KBT[1]] + # GZ computation + BG = [G[0] - B[0], G[1] - B[1]] + y = BG[0]*math.cos(math.radians(-roll)) - BG[1]*math.sin(math.radians(-roll)) + z = BG[0]*math.sin(math.radians(-roll)) + BG[1]*math.cos(math.radians(-roll)) + return -y + def createTask(): panel = TaskPanel() Gui.Control.showDialog(panel) diff --git a/src/Mod/Ship/tankGZ/TaskPanel.ui b/src/Mod/Ship/tankGZ/TaskPanel.ui index c1cad3e014..ce59e5d2b5 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.ui +++ b/src/Mod/Ship/tankGZ/TaskPanel.ui @@ -182,10 +182,10 @@ - 90.000000000000000 + 89.000000000000000 - 90.000000000000000 + 45.000000000000000 @@ -198,7 +198,7 @@ 10000 - 90 + 46 From f9318d9a5852f8820f244bacc38e66be0b6b5ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Sun, 27 May 2012 16:39:30 +0200 Subject: [PATCH 22/69] GZ curve plotting --- src/Mod/Ship/CMakeLists.txt | 1 + src/Mod/Ship/Makefile.am | 1 + src/Mod/Ship/tankGZ/Plot.py | 186 +++++++++++++++++++++++++++++++ src/Mod/Ship/tankGZ/TaskPanel.py | 2 + 4 files changed, 190 insertions(+) create mode 100644 src/Mod/Ship/tankGZ/Plot.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index 3106dbc47b..7944e4c38e 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -116,6 +116,7 @@ SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) SET(ShipGZ_SRCS tankGZ/__init__.py + tankGZ/Plot.py tankGZ/TaskPanel.py tankGZ/TaskPanel.ui ) diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index 54b7ecf6a5..a6d38f5002 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -77,6 +77,7 @@ nobase_data_DATA = \ tankCreateTank/TaskPanel.py \ tankCreateTank/TaskPanel.ui \ tankGZ/__init__.py \ + tankGZ/Plot.py \ tankGZ/TaskPanel.py \ tankGZ/TaskPanel.ui diff --git a/src/Mod/Ship/tankGZ/Plot.py b/src/Mod/Ship/tankGZ/Plot.py new file mode 100644 index 0000000000..1ce11d344a --- /dev/null +++ b/src/Mod/Ship/tankGZ/Plot.py @@ -0,0 +1,186 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import os +# FreeCAD modules +import FreeCAD,FreeCADGui +from FreeCAD import Part, Base +from FreeCAD import Image, ImageGui +# FreeCADShip modules +from shipUtils import Paths, Translator + +header = """ ################################################################# + + ##### #### ### #### ##### # # ### #### + # # # # # # # # # # # # + # ## #### #### # # # # # # # # # # # + #### # # # # # # # ##### # # ## ## ##### # #### + # # #### #### # # # # # # # # # # + # # # # # # # # # # # # # # + # # #### #### ### # # #### ##### # # ### # + + ################################################################# +""" + +class Plot(object): + def __init__(self, x, y, disp, draft, trim): + """ Constructor. performs plot and show it (Using pyxplot). + @param x Roll angles [deg]. + @param y GZ value [m]. + @param disp Ship displacement [tons]. + @param draft Ship draft [m]. + @param trim Ship trim angle [deg]. + """ + if self.createDirectory(): + return + if self.saveData(x,y): + return + if self.saveLayout(x,y, disp, draft, trim): + return + if self.execute(): + return + ImageGui.open(self.path + 'gz.png') + + def createDirectory(self): + """ Create needed folder to write data and scripts. + @return True if error happens. + """ + self.path = FreeCAD.ConfigGet("UserAppData") + "ShipOutput/" + if not os.path.exists(self.path): + os.makedirs(self.path) + if not os.path.exists(self.path): + msg = Translator.translate("Can't create '" + self.path + "' folder.\n") + FreeCAD.Console.PrintError(msg) + return False + + def saveData(self,x,y): + """ Write data file. + @param x Roll angles. + @param y GZ value. + @return True if error happens. + """ + # Open the file + filename = self.path + 'gz.dat' + try: + Output = open(filename, "w") + except IOError: + msg = Translator.translate("Can't write '" + filename + "' file.\n") + FreeCAD.Console.PrintError(msg) + return True + # Print header + Output.write(header) + Output.write(" #\n") + Output.write(" # File automatically exported by FreeCAD-Ship\n") + Output.write(" # This file contains transversal GZ stability parameter, filled with following columns:\n") + Output.write(" # 1: Roll angles [deg]\n") + Output.write(" # 2: GZ [m]\n") + Output.write(" #\n") + Output.write(" #################################################################\n") + # Print data + if len(x) < 2: + msg = Translator.translate("Not enough data to plot.\n") + FreeCAD.Console.PrintError(msg) + return True + for i in range(0, len(x)): + string = "%f %f\n" % (x[i], y[i]) + Output.write(string) + # Close file + Output.close() + self.dataFile = filename + msg = Translator.translate("Data saved at '" + self.dataFile + "'.\n") + FreeCAD.Console.PrintMessage(msg) + return False + + def saveLayout(self, x, y, disp, draft, trim): + """ Prints the data output. + @param x Roll angles. + @param y GZ value. + @param disp Ship displacement. + @param draft Ship draft. + @param trim Ship trim angle. + @return True if error happens. + """ + filename = self.path + 'gz.pyxplot' + # Open the file + try: + Output = open(filename, "w") + except IOError: + msg = Translator.translate("Can't write '" + filename + "' file.\n") + FreeCAD.Console.PrintError(msg) + return True + # Write header + Output.write(header) + Output.write(" #\n") + Output.write(" # File automatically exported by FreeCAD-Ship\n") + Output.write(" # This file contains a script to plot transversal GZ stability parameter.\n") + Output.write(" # To use it execute:\n") + Output.write(" #\n") + Output.write(" # pyxplot %s\n" % (filename)) + Output.write(" #\n") + Output.write(" #################################################################\n") + # Write general options for hydrostatics + Output.write("set numeric display latex\n") + Output.write("set output '%s'\n" % (self.path + 'gz.eps')) + Output.write("set nokey\n") + Output.write("set grid\n") + Output.write("# X axis\n") + Output.write("set xlabel '$roll$ / degrees'\n") + Output.write("set xtic\n") + Output.write("# Y axis\n") + Output.write("set ylabel '$GZ$ / m'\n") + Output.write("set ytic\n") + Output.write("# Line styles\n") + Output.write("set style 1 line linetype 1 linewidth 2 colour rgb (0):(0):(0)\n") + # Additional data + Output.write("# Additional data\n") + Output.write("set label (1) '$\\Delta = %g \\mathrm{tons}$' %f,%f\n" % (disp, x[0] + 0.65*(x[-1] - x[0]), min(y) + 0.95*(max(y)-min(y)))) + Output.write("set label (2) '$T = %g \\mathrm{m}$' %f,%f\n" % (draft, x[0] + 0.65*(x[-1] - x[0]), min(y) + 0.85*(max(y)-min(y)))) + Output.write("set label (3) '$Trim = %g^\\circ$' %f,%f\n" % (trim, x[0] + 0.65*(x[-1] - x[0]), min(y) + 0.75*(max(y)-min(y)))) + # Write plot call + Output.write("# Plot\n") + Output.write("plot '%s' using 1:2 title 'GZ' axes x1y1 with lines style 1\n" % (self.dataFile)) + # Close file + self.layoutFile = filename + Output.close() + return False + + def execute(self): + """ Calls pyxplot in order to plot an save an image. + @return True if error happens. + """ + filename = self.path + 'gz' + comm = "pyxplot %s" % (self.layoutFile) + if os.system(comm): + msg = Translator.translate("Can't execute pyxplot. Maybe is not installed?\n") + FreeCAD.Console.PrintError(msg) + msg = Translator.translate("Plot will not generated\n") + FreeCAD.Console.PrintError(msg) + return True + comm = "gs -r300 -dEPSCrop -dTextAlphaBits=4 -sDEVICE=png16m -sOutputFile=%s.png -dBATCH -dNOPAUSE %s.eps" % (filename,filename) + if os.system(comm): + msg = Translator.translate("Can't execute ghostscript. Maybe is not installed?\n") + FreeCAD.Console.PrintError(msg) + msg = Translator.translate("Generated image will not converted to png\n") + FreeCAD.Console.PrintError(msg) + return True + return False diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py index 72a7ef057c..5fefccafb9 100644 --- a/src/Mod/Ship/tankGZ/TaskPanel.py +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -28,6 +28,7 @@ import FreeCADGui as Gui # Qt library from PyQt4 import QtGui,QtCore # Module +from Plot import * from Instance import * from TankInstance import * from shipUtils import Paths, Translator @@ -56,6 +57,7 @@ class TaskPanel: for i in range(0, nRoll): roll.append(i*dRoll) GZ.append(self.computeGZ(draft[0], trim, roll[-1])) + Plot(roll, GZ, disp[0]/1000.0, draft[0], trim) return True def reject(self): From 399d9468f494b3b4f5cc62c2e35aa604815af4d9 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 31 May 2012 11:50:25 +0200 Subject: [PATCH 23/69] Implement CDATA reader, fix bugs in Writer::insertBinFile --- src/App/PropertyFile.cpp | 52 +++++++++++++++++++++++++++------------- src/Base/Reader.cpp | 43 +++++++++++++++++++++++++++++---- src/Base/Reader.h | 15 ++++++++++-- src/Base/Writer.cpp | 24 +++++++------------ 4 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/App/PropertyFile.cpp b/src/App/PropertyFile.cpp index 13599c88a6..52278ea991 100644 --- a/src/App/PropertyFile.cpp +++ b/src/App/PropertyFile.cpp @@ -257,13 +257,18 @@ void PropertyFileIncluded::setPyObject(PyObject *value) void PropertyFileIncluded::Save (Base::Writer &writer) const { if (writer.isForceXML()) { - writer.Stream() << writer.ind() << "" << endl; - - // write the file in the XML stream - if (!_cValue.empty()) + if (!_cValue.empty()) { + Base::FileInfo file(_cValue.c_str()); + writer.Stream() << writer.ind() << "" << std::endl; + // write the file in the XML stream + writer.incInd(); writer.insertBinFile(_cValue.c_str()); - - writer.Stream() << writer.ind() <<"" << endl ; + writer.decInd(); + writer.Stream() << writer.ind() <<"" << endl; + } + else + writer.Stream() << writer.ind() << "" << std::endl; } else { // instead initiate an extra file @@ -280,17 +285,30 @@ void PropertyFileIncluded::Save (Base::Writer &writer) const void PropertyFileIncluded::Restore(Base::XMLReader &reader) { reader.readElement("FileIncluded"); - string file (reader.getAttribute("file") ); - - if (!file.empty()) { - // initate a file read - reader.addFile(file.c_str(),this); - - // is in the document transient path - aboutToSetValue(); - _cValue = getDocTransientPath() + "/" + file; - _BaseFileName = file; - hasSetValue(); + if (reader.hasAttribute("file")) { + string file (reader.getAttribute("file") ); + if (!file.empty()) { + // initate a file read + reader.addFile(file.c_str(),this); + // is in the document transient path + aboutToSetValue(); + _cValue = getDocTransientPath() + "/" + file; + _BaseFileName = file; + hasSetValue(); + } + } + // section is XML stream + else if (reader.hasAttribute("data")) { + string file (reader.getAttribute("data") ); + if (!file.empty()) { + // is in the document transient path + aboutToSetValue(); + _cValue = getDocTransientPath() + "/" + file; + reader.readBinFile(_cValue.c_str()); + reader.readEndElement("FileIncluded"); + _BaseFileName = file; + hasSetValue(); + } } } diff --git a/src/Base/Reader.cpp b/src/Base/Reader.cpp index db355f723f..6791b99f62 100644 --- a/src/Base/Reader.cpp +++ b/src/Base/Reader.cpp @@ -34,6 +34,7 @@ /// Here the FreeCAD includes sorted by Base,App,Gui...... #include "Reader.h" +#include "Base64.h" #include "Exception.h" #include "Persistence.h" #include "InputSource.h" @@ -79,6 +80,7 @@ Base::XMLReader::XMLReader(const char* FileName, std::istream& str) //parser->setFeature(XMLUni::fgXercesDynamic, true); parser->setContentHandler(this); + parser->setLexicalHandler(this); parser->setErrorHandler(this); try { @@ -127,7 +129,7 @@ long Base::XMLReader::getAttributeAsInteger(const char* AttrName) const if (pos != AttrMap.end()) return atol(pos->second.c_str()); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return 0; @@ -140,7 +142,7 @@ unsigned long Base::XMLReader::getAttributeAsUnsigned(const char* AttrName) cons if (pos != AttrMap.end()) return strtoul(pos->second.c_str(),0,10); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return 0; @@ -153,7 +155,7 @@ double Base::XMLReader::getAttributeAsFloat (const char* AttrName) const if (pos != AttrMap.end()) return atof(pos->second.c_str()); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return 0.0; @@ -166,7 +168,7 @@ const char* Base::XMLReader::getAttribute (const char* AttrName) const if (pos != AttrMap.end()) return pos->second.c_str(); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return ""; @@ -255,6 +257,22 @@ void Base::XMLReader::readCharacters(void) { } +void Base::XMLReader::readBinFile(const char* filename) +{ + Base::FileInfo fi(filename); + Base::ofstream to(fi, std::ios::out | std::ios::binary); + if (!to) + throw Base::Exception("XMLReader::readBinFile() Could not open file!"); + + bool ok; + do { + ok = read(); if (!ok) break; + } while (ReadType != EndCDATA); + + to << Base::base64_decode(Characters); + to.close(); +} + void Base::XMLReader::readFiles(zipios::ZipInputStream &zipstream) const { // It's possible that not all objects inside the document could be created, e.g. if a module @@ -370,15 +388,32 @@ void Base::XMLReader::endElement (const XMLCh* const /*uri*/, const XMLCh *cons ReadType = EndElement; } +void Base::XMLReader::startCDATA () +{ + ReadType = StartCDATA; +} +void Base::XMLReader::endCDATA () +{ + ReadType = EndCDATA; +} + +#if (XERCES_VERSION_MAJOR == 2) void Base::XMLReader::characters(const XMLCh* const chars, const unsigned int length) +#else +void Base::XMLReader::characters(const XMLCh* const chars, const XMLSize_t length) +#endif { Characters = StrX(chars).c_str(); ReadType = Chars; CharacterCount += length; } +#if (XERCES_VERSION_MAJOR == 2) void Base::XMLReader::ignorableWhitespace( const XMLCh* const /*chars*/, const unsigned int /*length*/) +#else +void Base::XMLReader::ignorableWhitespace( const XMLCh* const /*chars*/, const XMLSize_t /*length*/) +#endif { //fSpaceCount += length; } diff --git a/src/Base/Reader.h b/src/Base/Reader.h index 3ced7241b5..7e55575d42 100644 --- a/src/Base/Reader.h +++ b/src/Base/Reader.h @@ -131,6 +131,8 @@ public: void readEndElement(const char* ElementName=0); /// read until characters are found void readCharacters(void); + /// read binary file + void readBinFile(const char*); //@} /** @name Attribute handling */ @@ -171,8 +173,15 @@ protected: // ----------------------------------------------------------------------- virtual void startElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs); virtual void endElement (const XMLCh* const uri, const XMLCh *const localname, const XMLCh *const qname); - virtual void characters (const XMLCh* const chars, const unsigned int length); + virtual void startCDATA (); + virtual void endCDATA (); +#if (XERCES_VERSION_MAJOR == 2) + virtual void characters (const XMLCh* const chars, const unsigned int length); virtual void ignorableWhitespace(const XMLCh* const chars, const unsigned int length); +#else + virtual void characters (const XMLCh* const chars, const XMLSize_t length); + virtual void ignorableWhitespace(const XMLCh* const chars, const XMLSize_t length); +#endif virtual void resetDocument(); @@ -198,7 +207,9 @@ protected: Chars, StartElement, StartEndElement, - EndElement + EndElement, + StartCDATA, + EndCDATA } ReadType; diff --git a/src/Base/Writer.cpp b/src/Base/Writer.cpp index 01eecef6b5..621893a8a3 100644 --- a/src/Base/Writer.cpp +++ b/src/Base/Writer.cpp @@ -65,32 +65,26 @@ void Writer::insertAsciiFile(const char* FileName) if (!from) throw Base::Exception("Writer::insertAsciiFile() Could not open file!"); - Stream() << "" << endl; + Stream() << "]]>" << endl; } void Writer::insertBinFile(const char* FileName) { Base::FileInfo fi(FileName); - Base::ifstream from(fi, std::ios::in | std::ios::binary); + Base::ifstream from(fi, std::ios::in | std::ios::binary | std::ios::ate); if (!from) throw Base::Exception("Writer::insertAsciiFile() Could not open file!"); - Stream() << " bytes(fileSize); + from.read((char*)&bytes[0], fileSize); + Stream() << Base::base64_encode(&bytes[0], fileSize); Stream() << "]]>" << endl; } From c3c6080791210e453b6110b2ed7d043af871d419 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 28 May 2012 16:02:26 -0300 Subject: [PATCH 24/69] Added FreeCADGui.doCommand() python command --- src/Gui/Application.h | 2 ++ src/Gui/ApplicationPy.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Gui/Application.h b/src/Gui/Application.h index 74b3dbb64b..44a73d0c67 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -231,6 +231,8 @@ public: PYFUNCDEF_S(sActiveDocument); PYFUNCDEF_S(sGetDocument); + PYFUNCDEF_S(sDoCommand); + static PyMethodDef Methods[]; private: diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 9ca9ee3afe..9a2a51af4e 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -128,6 +128,9 @@ PyMethodDef Application::Methods[] = { {"getDocument", (PyCFunction) Application::sGetDocument, 1, "getDocument(string) -> object\n\n" "Get a document by its name"}, + {"doCommand", (PyCFunction) Application::sDoCommand, 1, + "doCommand(string) -> None\n\n" + "Prints the given string in the python console and runs it"}, {NULL, NULL} /* Sentinel */ }; @@ -765,3 +768,12 @@ PyObject* Application::sRunCommand(PyObject * /*self*/, PyObject *args,PyObject return 0; } } + +PyObject* Application::sDoCommand(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) +{ + char *pstr=0; + if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C + return NULL; // NULL triggers exception + Command::doCommand(Command::Doc,pstr); + return Py_None; +} From a8c9e54cf1c45596168803ee29d0cbeca6b0fb78 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 28 May 2012 16:02:56 -0300 Subject: [PATCH 25/69] Testing python Gui.doCommand with the Draft Line tool --- src/Mod/Draft/DraftGui.py | 6 +++++- src/Mod/Draft/DraftTools.py | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 13517f4785..7e651a6986 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -78,7 +78,11 @@ class todo: try: name = str(name) FreeCAD.ActiveDocument.openTransaction(name) - func() + if isinstance(func,list): + for l in func: + FreeCADGui.doCommand(l) + else: + func() FreeCAD.ActiveDocument.commitTransaction() except: wrn = "[Draft.todo.commit] Unexpected error:", sys.exc_info()[0], "in ", f, "(", arg, ")" diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 885e781c64..81d1bc05d1 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -415,13 +415,26 @@ class Line(Creator): def finish(self,closed=False,cont=False): "terminates the operation and closes the poly if asked" if self.obj: + # remove temporary object, if any old = self.obj.Name todo.delay(self.doc.removeObject,old) self.obj = None if (len(self.node) > 1): + # building command string + if self.support: + sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' + else: + sup = 'None' + points='[' + for n in self.node: + if len(points) > 1: + points += ',' + points += 'FreeCAD.Vector('+str(n.x) + ',' + str(n.y) + ',' + str(n.z) + ')' + points += ']' self.commit(translate("draft","Create DWire"), - partial(Draft.makeWire,self.node,closed, - face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'points='+points, + 'Draft.makeWire(points,closed='+str(closed)+',face='+str(bool(self.ui.fillmode))+',support='+sup+')']) if self.ui: self.linetrack.finalize() self.constraintrack.finalize() From fb0540c87e2ab271998d90db4ea974feb2d8af01 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 29 May 2012 22:17:12 -0300 Subject: [PATCH 26/69] Used Gui.doCommand() in all Draft commands --- src/Mod/Draft/DraftTools.py | 186 +++++++++++++++++++++++---------- src/Mod/Draft/DraftVecUtils.py | 4 + 2 files changed, 136 insertions(+), 54 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 81d1bc05d1..93fb74fcc5 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -380,8 +380,38 @@ class Creator: todo.delayCommit(self.commitList) self.commitList = [] + def getStrings(self): + "returns a couple of useful strings fro building python commands" + + # current plane rotation + p = plane.getRotation() + qr = p.Rotation.Q + qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')' + + # support object + if self.support: + sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' + else: + sup = 'None' + + # contents of self.node + points='[' + for n in self.node: + if len(points) > 1: + points += ',' + points += DraftVecUtils.toString(n) + points += ']' + + # fill mode + if self.ui: + fil = str(bool(self.ui.fillmode)) + else: + fil = "True" + + return qr,sup,points,fil + def commit(self,name,func): - "stores partial actions to be committed to the FreeCAD document" + "stores actions to be committed to the FreeCAD document" self.commitList.append((name,func)) class Line(Creator): @@ -421,20 +451,11 @@ class Line(Creator): self.obj = None if (len(self.node) > 1): # building command string - if self.support: - sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' - else: - sup = 'None' - points='[' - for n in self.node: - if len(points) > 1: - points += ',' - points += 'FreeCAD.Vector('+str(n.x) + ',' + str(n.y) + ',' + str(n.z) + ')' - points += ']' + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create DWire"), ['import Draft', - 'points='+points, - 'Draft.makeWire(points,closed='+str(closed)+',face='+str(bool(self.ui.fillmode))+',support='+sup+')']) + 'points='+pts, + 'Draft.makeWire(points,closed='+str(closed)+',face='+fil+',support='+sup+')']) if self.ui: self.linetrack.finalize() self.constraintrack.finalize() @@ -628,9 +649,12 @@ class BSpline(Line): old = self.obj.Name self.doc.removeObject(old) try: + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create BSpline"), - partial(Draft.makeBSpline,self.node,closed, - face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'points='+pts, + 'Draft.makeBSpline(points,closed='+str(closed)+',face='+fil+',support='+sup+')']) except: print "Draft: error delaying commit" if self.ui: @@ -741,12 +765,15 @@ class Rectangle(Creator): if abs(DraftVecUtils.angle(p4.sub(p1),plane.u,plane.axis)) > 1: length = -length height = p2.sub(p1).Length if abs(DraftVecUtils.angle(p2.sub(p1),plane.v,plane.axis)) > 1: height = -height - p = plane.getRotation() - p.move(p1) try: + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create Rectangle"), - partial(Draft.makeRectangle,length,height, - p,self.ui.fillmode,support=self.support)) + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(p1), + 'Draft.makeRectangle(length='+str(length)+',height='+str(height)+',placement=pl,face='+fil+',support='+sup+')']) except: print "Draft: error delaying commit" self.finish(cont=True) @@ -1013,23 +1040,30 @@ class Arc(Creator): def drawArc(self): "actually draws the FreeCAD object" - p = plane.getRotation() - p.move(self.center) + rot,sup,pts,fil = self.getStrings() if self.closedCircle: try: - self.commit(translate("draft","Create Circle"), - partial(Draft.makeCircle,self.rad,p, - self.ui.fillmode,support=self.support)) + # building command string + self.commit(translate("draft","Create Circle"), + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',support='+sup+')']) except: - print "Draft: error delaying commit" + print "Draft: error delaying commit" else: sta = math.degrees(self.firstangle) end = math.degrees(self.firstangle+self.angle) if end < sta: sta,end = end,sta try: - self.commit(translate("draft","Create Arc"), - partial(Draft.makeCircle,self.rad,p,self.ui.fillmode, - sta,end,support=self.support)) + # building command string + self.commit(translate("draft","Create Arc"), + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',startangle='+str(sta)+',endangle='+str(end)+',support='+sup+')']) except: print "Draft: error delaying commit" self.finish(cont=True) @@ -1257,11 +1291,14 @@ class Polygon(Creator): def drawPolygon(self): "actually draws the FreeCAD object" - p = plane.getRotation() - p.move(self.center) + rot,sup,pts,fil = self.getStrings() + # building command string self.commit(translate("draft","Create Polygon"), - partial(Draft.makePolygon,self.ui.numFaces.value(),self.rad, - True,p,face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makePolygon('+str(self.ui.numFaces.value())+',radius='+str(self.rad)+',inscribed=True,placement=pl,face='+fil+',support='+sup+')']) self.finish(cont=True) def numericInput(self,numx,numy,numz): @@ -1326,8 +1363,16 @@ class Text(Creator): def createObject(self): "creates an object in the current doc" - self.commit(translate("draft","Create Text"), - partial(Draft.makeText,self.text,self.node[0])) + tx = '' + for l in self.text: + if tx: + tx += ',' + tx += '"'+l+'"' +# self.commit(translate("draft","Create Text"), +# ['import Draft', +# 'Draft.makeText(['+tx+'],'+DraftVecUtils.toString(self.node[0])+')']) + self.commit(translate("draft","Create Text"),partial(Draft.makeText,self.text,self.node[0])) + self.finish(cont=True) def action(self,arg): @@ -1429,9 +1474,9 @@ class Dimension(Creator): pt = o.ViewObject.RootNode.getChildren()[1].getChildren()[0].getChildren()[0].getChildren()[3] p3 = Vector(pt.point.getValues()[2].getValue()) self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,p1,p2,p3)) - self.commit(translate("draft","Delete Measurement"), - partial(FreeCAD.ActiveDocument.removeObject,o.Name)) + ['import Draft', + 'Draft.makeDimension('+DraftVecUtils.toString(p1)+','+DraftVecUtils.toString(p2)+','+DraftVecUtils.toString(p3)+')', + 'FreeCAD.ActiveDocument.removeObject("'+o.Name+'")']) def createObject(self): "creates an object in the current doc" @@ -1449,8 +1494,8 @@ class Dimension(Creator): self.arcmode,self.node[2])) else: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.node[0],self.node[1], - self.node[2])) + ['import Draft', + 'Draft.makeDimension('+DraftVecUtils.toString(self.node[0])+','+DraftVecUtils.toString(self.node[1])+','+DraftVecUtils.toString(self.node[2])+')']) if self.ui.continueMode: self.cont = self.node[2] if not self.dir: @@ -1712,7 +1757,7 @@ class Modifier: self.commitList = [] def commit(self,name,func): - "stores partial actions to be committed to the FreeCAD document" + "stores actions to be committed to the FreeCAD document" # print "committing" self.commitList.append((name,func)) @@ -1768,10 +1813,20 @@ class Move(Modifier): def move(self,delta,copy=False): "moving the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: - self.commit(translate("draft","Copy"),partial(Draft.move,self.sel,delta,copy)) + self.commit(translate("draft","Copy"), + ['import Draft', + 'Draft.move('+sel+','+DraftVecUtils.toString(delta)+',copy='+str(copy)+')']) else: - self.commit(translate("draft","Move"),partial(Draft.move,self.sel,delta,copy)) + self.commit(translate("draft","Move"), + ['import Draft', + 'Draft.move('+sel+','+DraftVecUtils.toString(delta)+',copy='+str(copy)+')']) self.doc.recompute() def action(self,arg): @@ -1926,14 +1981,20 @@ class Rotate(Modifier): def rot (self,angle,copy=False): "rotating the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: self.commit(translate("draft","Copy"), - partial(Draft.rotate,self.sel, - math.degrees(angle),self.center,plane.axis,copy)) + ['import Draft', + 'Draft.rotate('+sel+','+str(math.degrees(angle))+','+DraftVecUtils.toString(self.center)+',axis='+DraftVecUtils.toString(plane.axis)+',copy='+str(copy)+')']) else: self.commit(translate("draft","Rotate"), - partial(Draft.rotate,self.sel, - math.degrees(angle),self.center,plane.axis,copy)) + ['import Draft', + 'Draft.rotate('+sel+','+str(math.degrees(angle))+','+DraftVecUtils.toString(self.center)+',axis='+DraftVecUtils.toString(plane.axis)+',copy='+str(copy)+')']) def action(self,arg): "scene event handler" @@ -2194,13 +2255,18 @@ class Offset(Modifier): occmode = self.ui.occOffset.isChecked() if hasMod(arg,MODALT) or self.ui.isCopy.isChecked(): copymode = True if self.npts: + print "offset:npts=",self.npts self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.npts,copymode,occ=False)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+DraftVecUtils.toString(self.ntps)+',copy='+str(copymode)+')']) elif self.dvec: + if isinstance(self.dvec,float): + d = str(self.dvec) + else: + d = DraftVecUtils.toString(self.dvec) self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.dvec,copymode,occ=occmode)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) if hasMod(arg,MODALT): self.extendedCopy = True else: @@ -2222,9 +2288,13 @@ class Offset(Modifier): copymode = False occmode = self.ui.occOffset.isChecked() if self.ui.isCopy.isChecked(): copymode = True + if isinstance(self.dvec,float): + d = str(self.dvec) + else: + d = DraftVecUtils.toString(self.dvec) self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.dvec,copymode,occ=occmode)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) self.finish() @@ -2946,12 +3016,20 @@ class Scale(Modifier): def scale(self,delta,copy=False): "moving the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: self.commit(translate("draft","Copy"), - partial(Draft.scale,self.sel,delta,self.node[0],copy)) + ['import Draft', + 'Draft.scale('+sel+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+')']) else: self.commit(translate("draft","Scale"), - partial(Draft.scale,self.sel,delta,self.node[0],copy)) + ['import Draft', + 'Draft.scale('+sel+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+')']) def action(self,arg): "scene event handler" diff --git a/src/Mod/Draft/DraftVecUtils.py b/src/Mod/Draft/DraftVecUtils.py index 1f10a2c7ea..678965e85a 100644 --- a/src/Mod/Draft/DraftVecUtils.py +++ b/src/Mod/Draft/DraftVecUtils.py @@ -40,6 +40,10 @@ def typecheck (args_and_types, name="?"): FreeCAD.Console.PrintWarning("typecheck[" + str(name) + "]: " + str(v) + " is not " + str(t) + "\n") raise TypeError("fcvec." + str(name)) +def toString(u): + "returns a string containing a python command to recreate this vector" + return "FreeCAD.Vector("+str(u.x)+","+str(u.y)+","+str(u.z)+")" + def tup(u,array=False): "returns a tuple (x,y,z) with the vector coords, or an array if array=true" typecheck ([(u,Vector)], "tup"); From d50a5112765e80a5adc6d7af849f27607f9b3a4c Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 30 May 2012 12:58:08 -0300 Subject: [PATCH 27/69] Draft: fixes in commands --- src/Mod/Draft/DraftTools.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 93fb74fcc5..9135588a76 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -1363,15 +1363,15 @@ class Text(Creator): def createObject(self): "creates an object in the current doc" - tx = '' + tx = '[' for l in self.text: - if tx: + if len(tx) > 1: tx += ',' - tx += '"'+l+'"' -# self.commit(translate("draft","Create Text"), -# ['import Draft', -# 'Draft.makeText(['+tx+'],'+DraftVecUtils.toString(self.node[0])+')']) - self.commit(translate("draft","Create Text"),partial(Draft.makeText,self.text,self.node[0])) + tx += '"'+str(l)+'"' + tx += ']' + self.commit(translate("draft","Create Text"), + ['import Draft', + 'Draft.makeText('+tx+',point='+DraftVecUtils.toString(self.node[0])+')']) self.finish(cont=True) @@ -1911,19 +1911,21 @@ class ApplyStyle(Modifier): if self.ui: self.sel = Draft.getSelection() if (len(self.sel)>0): + c = ['import Draft'] for ob in self.sel: if (ob.Type == "App::DocumentObjectGroup"): - self.formatGroup(ob) + c.extend(self.formatGroup(ob)) else: - self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob)) + c.append('Draft.formatObject(FreeCAD.ActiveDocument.'+ob.Name+')') + self.commit(translate("draft","Change Style"),c) def formatGroup(self,grpob): + c=[] for ob in grpob.Group: if (ob.Type == "App::DocumentObjectGroup"): - self.formatGroup(ob) + c.extend(self.formatGroup(ob)) else: - self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob)) - + c.append('Draft.formatObject(FreeCAD.ActiveDocument.'+ob.Name+')') class Rotate(Modifier): "The Draft_Rotate FreeCAD command definition" From 850cdce3ed90138713fbfdfc4c9b4434d9d388e9 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 30 May 2012 19:40:23 -0300 Subject: [PATCH 28/69] Draft: finished the switch to Gui.doCommand() --- src/Mod/Draft/DraftTools.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 9135588a76..b37e07c00f 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -30,7 +30,6 @@ __url__ = "http://free-cad.sourceforge.net" #--------------------------------------------------------------------------- import os, FreeCAD, FreeCADGui, WorkingPlane, math, re, importSVG, Draft, Draft_rc, DraftVecUtils -from functools import partial from FreeCAD import Vector from DraftGui import todo,QtCore,QtGui from DraftSnap import * @@ -1482,16 +1481,16 @@ class Dimension(Creator): "creates an object in the current doc" if self.angledata: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeAngularDimension,self.center, - self.angledata,self.node[-1])) + ['import Draft', + 'Draft.makeAngularDimension(center='+DraftVecUtils.toString(self.center)+',angles=['+str(self.angledata[0])+','+str(self.angledata[1])+'],p3='+DraftVecUtils.toString(self.node[-1])+')']) elif self.link and (not self.arcmode): self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.link[0],self.link[1], - self.link[2],self.node[2])) + ['import Draft', + 'Draft.makeDimension(FreeCAD.ActiveDocument.'+self.link[0].Name+','+str(self.link[1])+','+str(self.link[2])+','+DraftVecUtils.toString(self.node[2])+')']) elif self.arcmode: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.link[0],self.link[1], - self.arcmode,self.node[2])) + ['import Draft', + 'Draft.makeDimension(FreeCAD.ActiveDocument.'+self.link[0].Name+','+str(self.link[1])+',"'+str(self.arcmode)+'",'+DraftVecUtils.toString(self.node[2])+')']) else: self.commit(translate("draft","Create Dimension"), ['import Draft', From 194a422265bc239be10072bfc769f3a7ff87ecfa Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 30 May 2012 21:34:51 -0300 Subject: [PATCH 29/69] Arch: Wall tool is now translatable + uses Gui.doCommand --- src/Mod/Arch/ArchWall.py | 48 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index c26125136d..4b9db7ee03 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Wall" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeWall(baseobj=None,width=None,height=None,align="Center",name="Wall"): +def makeWall(baseobj=None,width=None,height=None,align="Center",name=str(translate("Arch","Wall"))): '''makeWall(obj,[width],[height],[align],[name]): creates a wall based on the given object, which can be a sketch, a draft object, a face or a solid. align can be "Center","Left" or "Right"''' @@ -117,10 +118,12 @@ class _CommandWall: if sel: import Draft if Draft.getType(sel[0]) != "Wall": - FreeCAD.ActiveDocument.openTransaction("Wall") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Wall"))) + FreeCADGui.doCommand('import Arch') for obj in sel: - makeWall(obj) + FreeCADGui.doCommand('Arch.makeWall(FreeCAD.ActiveDocument.'+obj.Name+')') FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() done = True if not done: import DraftTrackers @@ -146,32 +149,33 @@ class _CommandWall: add = False l = Part.Line(self.points[0],self.points[1]) self.tracker.finalize() - FreeCAD.ActiveDocument.openTransaction("Wall") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Wall"))) + FreeCADGui.doCommand('import Arch') + FreeCADGui.doCommand('import Part') + FreeCADGui.doCommand('trace=Part.Line(FreeCAD.'+str(l.StartPoint)+',FreeCAD.'+str(l.EndPoint)+')') if not self.existing: self.addDefault(l) else: w = joinWalls(self.existing) if w: if areSameWallTypes([w,self]): - w.Base.addGeometry(l) + FreeCADGui.doCommand('FreeCAD.ActiveDocument.'+w.Name+'.Base.addGeometry(trace)') else: - nw = self.addDefault(l) + self.addDefault(l) add = True else: self.addDefault(l) + if add: + FreeCADGui.doCommand('Arch.addComponents(FreeCAD.ActiveDocument.'+FreeCAD.ActiveDocument.Objects[-1].Name+',FreeCAD.ActiveDocument.'+w.Name+')') FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() - if add: - import ArchCommands - ArchCommands.addComponents(nw,w) if self.continueCmd: self.Activated() def addDefault(self,l): - s = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace") - s.addGeometry(l) - w = makeWall(s,width=self.Width,height=self.Height,align=self.Align) - return w + FreeCADGui.doCommand('base=FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","'+str(translate('Arch','WallTrace'))+'")') + FreeCADGui.doCommand('base.addGeometry(trace)') + FreeCADGui.doCommand('Arch.makeWall(base,width='+str(self.Width)+',height='+str(self.Height)+',align="'+str(self.Align)+'")') def update(self,point): "this function is called by the Snapper when the mouse is moved" @@ -191,11 +195,11 @@ class _CommandWall: def taskbox(self): "sets up a taskbox widget" w = QtGui.QWidget() - w.setWindowTitle("Wall options") + w.setWindowTitle(str(translate("Arch","Wall options"))) lay0 = QtGui.QVBoxLayout(w) lay1 = QtGui.QHBoxLayout() lay0.addLayout(lay1) - label1 = QtGui.QLabel("Width") + label1 = QtGui.QLabel(str(translate("Arch","Width"))) lay1.addWidget(label1) value1 = QtGui.QDoubleSpinBox() value1.setDecimals(2) @@ -203,7 +207,7 @@ class _CommandWall: lay1.addWidget(value1) lay2 = QtGui.QHBoxLayout() lay0.addLayout(lay2) - label2 = QtGui.QLabel("Height") + label2 = QtGui.QLabel(str(translate("Arch","Height"))) lay2.addWidget(label2) value2 = QtGui.QDoubleSpinBox() value2.setDecimals(2) @@ -211,14 +215,14 @@ class _CommandWall: lay2.addWidget(value2) lay3 = QtGui.QHBoxLayout() lay0.addLayout(lay3) - label3 = QtGui.QLabel("Alignment") + label3 = QtGui.QLabel(str(translate("Arch","Alignment"))) lay3.addWidget(label3) value3 = QtGui.QComboBox() items = ["Center","Left","Right"] value3.addItems(items) value3.setCurrentIndex(items.index(self.Align)) lay3.addWidget(value3) - value4 = QtGui.QCheckBox("Continue") + value4 = QtGui.QCheckBox(str(translate("Arch","Continue"))) lay0.addWidget(value4) QtCore.QObject.connect(value1,QtCore.SIGNAL("valueChanged(double)"),self.setWidth) QtCore.QObject.connect(value2,QtCore.SIGNAL("valueChanged(double)"),self.setHeight) @@ -245,13 +249,13 @@ class _Wall(ArchComponent.Component): def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyLength","Width","Base", - "The width of this wall. Not used if this wall is based on a face") + str(translate("Arch","The width of this wall. Not used if this wall is based on a face"))) obj.addProperty("App::PropertyLength","Height","Base", - "The height of this wall. Keep 0 for automatic. Not used if this wall is based on a solid") + str(translate("Arch","The height of this wall. Keep 0 for automatic. Not used if this wall is based on a solid"))) obj.addProperty("App::PropertyEnumeration","Align","Base", - "The alignment of this wall on its base object, if applicable") + str(translate("Arch","The alignment of this wall on its base object, if applicable"))) obj.addProperty("App::PropertyVector","Normal","Base", - "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)") + str(translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))) obj.Align = ['Left','Right','Center'] self.Type = "Wall" obj.Width = 0.1 From d88813d52b66f0da8b2159732bce34b6069c3fb6 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 31 May 2012 12:30:20 -0300 Subject: [PATCH 30/69] Arch: Adapted all other tools to doCommand() + translatable --- src/Mod/Arch/ArchAxis.py | 18 ++++---- src/Mod/Arch/ArchBuilding.py | 24 +++++++--- src/Mod/Arch/ArchCommands.py | 34 ++++++++++++--- src/Mod/Arch/ArchFloor.py | 27 ++++++++---- src/Mod/Arch/ArchRoof.py | 26 ++++++----- src/Mod/Arch/ArchSectionPlane.py | 75 +++++++++++++++++++++----------- src/Mod/Arch/ArchSite.py | 21 +++++++-- src/Mod/Arch/ArchStructure.py | 24 +++++----- src/Mod/Arch/ArchWindow.py | 10 +++-- 9 files changed, 173 insertions(+), 86 deletions(-) diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index 37cdf9f545..d165e69eaf 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -25,12 +25,13 @@ import FreeCAD,FreeCADGui,Draft,math,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore, QtGui from pivy import coin +from DraftTools import translate __title__="FreeCAD Axis System" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeAxis(num=0,size=0,name="Axes"): +def makeAxis(num=5,size=1,name=str(translate("Arch","Axes"))): '''makeAxis(num,size): makes an Axis System based on the given number of axes and interval distances''' obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) @@ -56,16 +57,17 @@ class _CommandAxis: 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Axis","Creates an axis system.")} def Activated(self): - FreeCAD.ActiveDocument.openTransaction("Axis") - makeAxis(5,1) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Axis"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeAxis()") FreeCAD.ActiveDocument.commitTransaction() class _Axis: "The Axis object" def __init__(self,obj): - obj.addProperty("App::PropertyFloatList","Distances","Base", "The intervals between axes") - obj.addProperty("App::PropertyFloatList","Angles","Base", "The angles of each axis") - obj.addProperty("App::PropertyFloat","Length","Base", "The length of the axes") + obj.addProperty("App::PropertyFloatList","Distances","Base", str(translate("Arch","The intervals between axes"))) + obj.addProperty("App::PropertyFloatList","Angles","Base", str(translate("Arch","The angles of each axis"))) + obj.addProperty("App::PropertyFloat","Length","Base", str(translate("Arch","The length of the axes"))) self.Type = "Axis" obj.Length=1.0 obj.Proxy = self @@ -98,8 +100,8 @@ class _ViewProviderAxis: "A View Provider for the Axis object" def __init__(self,vobj): - vobj.addProperty("App::PropertyLength","BubbleSize","Base", "The size of the axis bubbles") - vobj.addProperty("App::PropertyEnumeration","NumerationStyle","Base", "The numeration style") + vobj.addProperty("App::PropertyLength","BubbleSize","Base", str(translate("Arch","The size of the axis bubbles"))) + vobj.addProperty("App::PropertyEnumeration","NumerationStyle","Base", str(translate("Arch","The numeration style"))) vobj.NumerationStyle = ["1,2,3","01,02,03","001,002,003","A,B,C","a,b,c","I,II,III","L0,L1,L2"] vobj.Proxy = self vobj.BubbleSize = .1 diff --git a/src/Mod/Arch/ArchBuilding.py b/src/Mod/Arch/ArchBuilding.py index f8ed4e8ca3..4b0011b34d 100644 --- a/src/Mod/Arch/ArchBuilding.py +++ b/src/Mod/Arch/ArchBuilding.py @@ -23,12 +23,13 @@ import FreeCAD,FreeCADGui,Draft,ArchCommands from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Building" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeBuilding(objectslist=None,join=False,name="Building"): +def makeBuilding(objectslist=None,join=False,name=str(translate("Arch","Building"))): '''makeBuilding(objectslist,[joinmode]): creates a building including the objects from the given list. If joinmode is True, components will be joined.''' obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name) @@ -51,15 +52,24 @@ class _CommandBuilding: ok = False if (len(sel) == 1): if Draft.getType(sel[0]) in ["Cell","Site","Floor"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") - nobj = makeBuilding() - ArchCommands.copyProperties(sel[0],nobj) - FreeCAD.ActiveDocument.removeObject(sel[0].Name) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Type conversion"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("obj = Arch.makeBuilding()") + FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") + FreeCADGui.doCommand("FreeCAD.ActiveDocument.removeObject("+sel[0].Name+")") FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: - FreeCAD.ActiveDocument.openTransaction("Building") - makeBuilding(sel) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch"," Create Building"))) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Floor"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeBuilding("+ss+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 2ebfad0d0b..2025a6f308 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -24,6 +24,7 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Arch Commands" __author__ = "Yorik van Havre" @@ -341,11 +342,20 @@ class _CommandAdd: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Grouping") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Grouping"))) if not mergeCells(sel): host = sel.pop() - addComponents(sel,host) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.addComponents("+ss+",FreeCAD.ActiveDocument."+host.Name+")") FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + class _CommandRemove: "the Arch Add command definition" @@ -362,13 +372,22 @@ class _CommandRemove: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Ungrouping") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Ungrouping"))) if Draft.getType(sel[-1]) in ["Wall","Structure"]: host = sel.pop() - removeComponents(sel,host) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.removeComponents("+ss+",FreeCAD.ActiveDocument."+host.Name+")") else: - removeComponents(sel) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.removeComponents("+ss+")") FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() class _CommandSplitMesh: @@ -387,7 +406,7 @@ class _CommandSplitMesh: def Activated(self): if FreeCADGui.Selection.getSelection(): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Split Mesh") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Split Mesh"))) for obj in sel: n = obj.Name nobjs = splitMesh(obj) @@ -396,6 +415,7 @@ class _CommandSplitMesh: for o in nobjs: g.addObject(o) FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() class _CommandMeshToShape: @@ -424,7 +444,7 @@ class _CommandMeshToShape: if f.InList: if f.InList[0].isDerivedFrom("App::DocumentObjectGroup"): g = f.InList[0] - FreeCAD.ActiveDocument.openTransaction("Mesh to Shape") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Mesh to Shape"))) for obj in FreeCADGui.Selection.getSelection(): newobj = meshToShape(obj) if g and newobj: diff --git a/src/Mod/Arch/ArchFloor.py b/src/Mod/Arch/ArchFloor.py index 606395961d..922d8aca0c 100644 --- a/src/Mod/Arch/ArchFloor.py +++ b/src/Mod/Arch/ArchFloor.py @@ -23,12 +23,13 @@ import FreeCAD,FreeCADGui,Draft,ArchCommands from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Arch Floor" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeFloor(objectslist=None,join=True,name="Floor"): +def makeFloor(objectslist=None,join=True,name=str(translate("Arch","Floor"))): '''makeFloor(objectslist,[joinmode]): creates a floor including the objects from the given list. If joinmode is False, components will not be joined.''' @@ -52,23 +53,31 @@ class _CommandFloor: ok = False if (len(sel) == 1): if Draft.getType(sel[0]) in ["Cell","Site","Building"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") - nobj = makeFloor() - ArchCommands.copyProperties(sel[0],nobj) - FreeCAD.ActiveDocument.removeObject(sel[0].Name) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Type conversion"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("obj = Arch.makeFloor()") + FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") + FreeCADGui.doCommand("FreeCAD.ActiveDocument.removeObject("+sel[0].Name+")") FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: - FreeCAD.ActiveDocument.openTransaction("Floor") - makeFloor(sel) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Floor"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeFloor("+ss+")") FreeCAD.ActiveDocument.commitTransaction() - FreeCAD.ActiveDocument.recompute() + FreeCAD.ActiveDocument.recompute() class _Floor: "The Cell object" def __init__(self,obj): obj.addProperty("App::PropertyLength","Height","Base", - "The height of this floor") + str(translate("Arch","The height of this floor"))) self.Type = "Floor" obj.Proxy = self self.Object = obj diff --git a/src/Mod/Arch/ArchRoof.py b/src/Mod/Arch/ArchRoof.py index 31515afbc8..4c8d6964c1 100644 --- a/src/Mod/Arch/ArchRoof.py +++ b/src/Mod/Arch/ArchRoof.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent, DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Roof" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeRoof(baseobj,facenr=1,angle=45,name="Roof"): +def makeRoof(baseobj,facenr=1,angle=45,name=str(translate("Arch","Roof"))): '''makeRoof(baseobj,[facenr],[angle],[name]) : Makes a roof based on a face from an existing object. You can provide the number of the face to build the roof on (default = 1), the angle (default=45) and a name (default @@ -64,35 +65,38 @@ class _CommandRoof: if sel.HasSubObjects: if "Face" in sel.SubElementNames[0]: idx = int(sel.SubElementNames[0][4:]) - FreeCAD.ActiveDocument.openTransaction("Create Roof") - makeRoof(obj,idx) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Roof"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeRoof(FreeCAD.ActiveDocument."+obj.Name+","+str(idx)+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() elif obj.isDerivedFrom("Part::Feature"): if len(obj.Shape.Faces) == 1: - FreeCAD.ActiveDocument.openTransaction("Create Roof") - makeRoof(obj,1) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Roof"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeRoof(FreeCAD.ActiveDocument."+obj.Name+",1)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() elif obj.isDerivedFrom("Part::Feature"): if len(obj.Shape.Faces) == 1: - FreeCAD.ActiveDocument.openTransaction("Create Roof") - makeRoof(obj,1) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Roof"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeRoof(FreeCAD.ActiveDocument."+obj.Name+",1)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() else: - FreeCAD.Console.PrintMessage("Unable to create a roof") + FreeCAD.Console.PrintMessage(str(translate("Arch","Unable to create a roof"))) else: - FreeCAD.Console.PrintMessage("No object selected") + FreeCAD.Console.PrintMessage(str(translate("Arch","No object selected"))) class _Roof(ArchComponent.Component): "The Roof object" def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyAngle","Angle","Base", - "The angle of this roof") + str(translate("Arch","The angle of this roof"))) obj.addProperty("App::PropertyInteger","Face","Base", - "The face number of the base object used to build this roof") + str(translate("Arch","The face number of the base object used to build this roof"))) self.Type = "Structure" def execute(self,obj): diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index e1230e59f7..8f695d7082 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -25,7 +25,45 @@ import FreeCAD,FreeCADGui,ArchComponent,WorkingPlane,math,Draft,ArchCommands, Dr from FreeCAD import Vector from PyQt4 import QtCore from pivy import coin +from DraftTools import translate +def makeSectionPlane(objectslist=None): + """makeSectionPlane([objectslist]) : Creates a Section plane objects including the + given objects. If no object is given, the whole document will be considered.""" + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Section") + _SectionPlane(obj) + _ViewProviderSectionPlane(obj.ViewObject) + if objectslist: + g = [] + for o in objectslist: + if o.isDerivedFrom("Part::Feature"): + g.append(o) + elif o.isDerivedFrom("App::DocumentObjectGroup"): + g.append(o) + obj.Objects = g + +def makeSectionView(section): + """makeSectionView(section) : Creates a Drawing view of the given Section Plane + in the active Page object (a new page will be created if none exists""" + page = None + for o in FreeCAD.ActiveDocument.Objects: + if o.isDerivedFrom("Drawing::FeaturePage"): + page = o + break + if not page: + page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage",str(translate("Arch","Page"))) + template = Draft.getParam("template") + if not template: + template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg' + page.ViewObject.HintOffsetX = 200 + page.ViewObject.HintOffsetY = 100 + page.ViewObject.HintScale = 20 + page.Template = template + + view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View") + page.addObject(view) + _ArchDrawingView(view) + view.Source = section class _CommandSectionPlane: "the Arch SectionPlane command definition" @@ -37,30 +75,17 @@ class _CommandSectionPlane: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Section Plane") - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Section") - _SectionPlane(obj) - _ViewProviderSectionPlane(obj.ViewObject) - FreeCAD.ActiveDocument.commitTransaction() - g = [] + ss = "[" for o in sel: - if o.isDerivedFrom("Part::Feature"): - g.append(o) - elif o.isDerivedFrom("App::DocumentObjectGroup"): - g.append(o) - obj.Objects = g - page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage","Page") - template = Draft.getParam("template") - if not template: - template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg' - page.ViewObject.HintOffsetX = 200 - page.ViewObject.HintOffsetY = 100 - page.ViewObject.HintScale = 20 - page.Template = template - view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View") - page.addObject(view) - _ArchDrawingView(view) - view.Source = obj + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Section Plane"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("section = Arch.makeSectionPlane("+ss+")") + FreeCADGui.doCommand("Arch.makeSectionView(section)") + FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() class _SectionPlane: @@ -68,7 +93,7 @@ class _SectionPlane: def __init__(self,obj): obj.Proxy = self obj.addProperty("App::PropertyLinkList","Objects","Base", - "The objects that must be considered by this section plane. Empty means all document") + str(translate("Arch","The objects that must be considered by this section plane. Empty means all document"))) self.Type = "SectionPlane" def execute(self,obj): @@ -89,7 +114,7 @@ class _ViewProviderSectionPlane(ArchComponent.ViewProviderComponent): "A View Provider for Section Planes" def __init__(self,vobj): vobj.addProperty("App::PropertyLength","DisplaySize","Base", - "The display size of the section plane") + str(translate("Arch","The display size of this section plane"))) vobj.DisplaySize = 1 vobj.Transparency = 85 vobj.LineWidth = 1 diff --git a/src/Mod/Arch/ArchSite.py b/src/Mod/Arch/ArchSite.py index 300fd09eff..d05a7e8ffa 100644 --- a/src/Mod/Arch/ArchSite.py +++ b/src/Mod/Arch/ArchSite.py @@ -23,12 +23,13 @@ import FreeCAD,FreeCADGui,Draft,ArchCommands from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Site" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeSite(objectslist=None,name="Site"): +def makeSite(objectslist=None,name=str(translate("Arch","Site"))): '''makeBuilding(objectslist): creates a site including the objects from the given list.''' obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name) @@ -51,15 +52,27 @@ class _CommandSite: ok = False if (len(sel) == 1): if Draft.getType(sel[0]) in ["Cell","Building","Floor"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Type conversion"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("obj = Arch.makeSite()") + FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") + FreeCADGui.doCommand("FreeCAD.ActiveDocument.removeObject("+sel[0].Name+")") + nobj = makeSite() ArchCommands.copyProperties(sel[0],nobj) FreeCAD.ActiveDocument.removeObject(sel[0].Name) FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: - FreeCAD.ActiveDocument.openTransaction("Site") - makeSite(sel) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Site"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeSite("+ss+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index 2327ad7cac..7bf73cc1e0 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Structure" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeStructure(baseobj=None,length=None,width=None,height=None,name="Structure"): +def makeStructure(baseobj=None,length=None,width=None,height=None,name=str(translate("Arch","Structure"))): '''makeStructure([obj],[length],[width],[heigth],[swap]): creates a structure element based on the given profile object and the given extrusion height. If no base object is given, you can also specify @@ -64,29 +65,31 @@ class _CommandStructure: 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Structure","Creates a structure object from scratch or from a selected object (sketch, wire, face or solid)")} def Activated(self): + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Structure"))) + FreeCADGui.doCommand("import Arch") sel = FreeCADGui.Selection.getSelection() if sel: - FreeCAD.ActiveDocument.openTransaction("Structure") for obj in sel: - makeStructure(obj) - FreeCAD.ActiveDocument.commitTransaction() + FreeCADGui.doCommand("Arch.makeStructure(FreeCAD.ActiveDocument."+obj.Name+")") else: - makeStructure() + FreeCADGui.doCommand("Arch.makeStructure()") + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() class _Structure(ArchComponent.Component): "The Structure object" def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyLength","Length","Base", - "The length of this element, if not based on a profile") + str(translate("Arch","The length of this element, if not based on a profile"))) obj.addProperty("App::PropertyLength","Width","Base", - "The width of this element, if not based on a profile") + str(translate("Arch","The width of this element, if not based on a profile"))) obj.addProperty("App::PropertyLength","Height","Base", - "The height or extrusion depth of this element. Keep 0 for automatic") + str(translate("Arch","The height or extrusion depth of this element. Keep 0 for automatic"))) obj.addProperty("App::PropertyLinkList","Axes","Base", - "Axes systems this structure is built on") + str(translate("Arch","Axes systems this structure is built on"))) obj.addProperty("App::PropertyVector","Normal","Base", - "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)") + str(translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))) self.Type = "Structure" def execute(self,obj): @@ -167,7 +170,6 @@ class _Structure(ArchComponent.Component): if hasattr(hole,"Proxy"): if hasattr(hole.Proxy,"Subvolume"): if hole.Proxy.Subvolume: - print "cutting subvolume",hole.Proxy.Subvolume base = base.cut(hole.Proxy.Subvolume) cut = True if not cut: diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 35813ddfc8..40b038aed0 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore,QtGui +from DraftTools import translate __title__="FreeCAD Wall" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeWindow(baseobj=None,width=None,name="Window"): +def makeWindow(baseobj=None,width=None,name=str(translate("Arch","Window"))): '''makeWindow(obj,[name]): creates a window based on the given object''' if baseobj: @@ -87,9 +88,10 @@ class _CommandWindow: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Create Window") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Window"))) + FreeCADGui.doCommand("import Arch") for obj in sel: - makeWindow(obj) + FreeCADGui.doCommand("Arch.makeWindow(FreeCAD.ActiveDocument."+obj.Name+")") FreeCAD.ActiveDocument.commitTransaction() class _Window(ArchComponent.Component): @@ -97,7 +99,7 @@ class _Window(ArchComponent.Component): def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyStringList","WindowParts","Base", - "the components of this window") + str(translate("Arch","the components of this window"))) self.Type = "Window" def execute(self,obj): From 7fadf83ff5162c94b6ec1700a0a5a8dd2253c184 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 30 May 2012 15:18:37 +0200 Subject: [PATCH 31/69] Keep faces colors on boolean operations --- src/Mod/Part/App/FeaturePartBoolean.cpp | 18 ++++- src/Mod/Part/App/FeaturePartBoolean.h | 8 ++- src/Mod/Part/App/FeaturePartCommon.cpp | 8 +-- src/Mod/Part/App/FeaturePartCommon.h | 2 +- src/Mod/Part/App/FeaturePartCut.cpp | 8 +-- src/Mod/Part/App/FeaturePartCut.h | 2 +- src/Mod/Part/App/FeaturePartFuse.cpp | 8 +-- src/Mod/Part/App/FeaturePartFuse.h | 2 +- src/Mod/Part/App/FeaturePartSection.cpp | 8 +-- src/Mod/Part/App/FeaturePartSection.h | 2 +- src/Mod/Part/Gui/ViewProviderBoolean.cpp | 84 ++++++++++++++++++++++++ src/Mod/Part/Gui/ViewProviderBoolean.h | 1 + 12 files changed, 119 insertions(+), 32 deletions(-) diff --git a/src/Mod/Part/App/FeaturePartBoolean.cpp b/src/Mod/Part/App/FeaturePartBoolean.cpp index f21296a8b2..7418db97a6 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.cpp +++ b/src/Mod/Part/App/FeaturePartBoolean.cpp @@ -23,6 +23,8 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include +#include #endif #include "FeaturePartBoolean.h" @@ -33,7 +35,7 @@ using namespace Part; PROPERTY_SOURCE_ABSTRACT(Part::Boolean, Part::Feature) -Boolean::Boolean(void) +Boolean::Boolean(void) : myBoolOp(0) { ADD_PROPERTY(Base,(0)); ADD_PROPERTY(Tool,(0)); @@ -66,13 +68,23 @@ App::DocumentObjectExecReturn *Boolean::execute(void) TopoDS_Shape BaseShape = base->Shape.getValue(); TopoDS_Shape ToolShape = tool->Shape.getValue(); - TopoDS_Shape resShape = runOperation(BaseShape, ToolShape); - if (resShape.IsNull()) + std::auto_ptr mkBool(makeOperation(BaseShape, ToolShape)); + if (!mkBool->IsDone()) { + return new App::DocumentObjectExecReturn("Boolean operation failed"); + } + const TopoDS_Shape& resShape = mkBool->Shape(); + if (resShape.IsNull()) { return new App::DocumentObjectExecReturn("Resulting shape is invalid"); + } + + // tmp. set boolean operation pointer + this->myBoolOp = mkBool.get(); this->Shape.setValue(resShape); + this->myBoolOp = 0; return App::DocumentObject::StdReturn; } catch (...) { + this->myBoolOp = 0; return new App::DocumentObjectExecReturn("A fatal error occurred when running boolean operation"); } } diff --git a/src/Mod/Part/App/FeaturePartBoolean.h b/src/Mod/Part/App/FeaturePartBoolean.h index 742af5098e..13305dffe5 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.h +++ b/src/Mod/Part/App/FeaturePartBoolean.h @@ -27,6 +27,8 @@ #include #include "PartFeature.h" +class BRepAlgoAPI_BooleanOperation; + namespace Part { @@ -51,9 +53,13 @@ public: const char* getViewProviderName(void) const { return "PartGui::ViewProviderBoolean"; } + BRepAlgoAPI_BooleanOperation* getBooleanOperation() const { return myBoolOp; } protected: - virtual TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const = 0; + virtual BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const = 0; + +private: + BRepAlgoAPI_BooleanOperation* myBoolOp; }; } diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index 828512bc35..1835cdfc14 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -41,14 +41,10 @@ Common::Common(void) { } -TopoDS_Shape Common::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Common::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a section operation: - BRepAlgoAPI_Common mkCommon(base, tool); - // Let's check if the section has been successful - if (!mkCommon.IsDone()) - throw Base::Exception("Intersection failed"); - return mkCommon.Shape(); + return new BRepAlgoAPI_Common(base, tool); } // ---------------------------------------------------- diff --git a/src/Mod/Part/App/FeaturePartCommon.h b/src/Mod/Part/App/FeaturePartCommon.h index 295ec2e4f2..19df8cdebb 100644 --- a/src/Mod/Part/App/FeaturePartCommon.h +++ b/src/Mod/Part/App/FeaturePartCommon.h @@ -41,7 +41,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; diff --git a/src/Mod/Part/App/FeaturePartCut.cpp b/src/Mod/Part/App/FeaturePartCut.cpp index 1d48c1783f..6b6d792c5e 100644 --- a/src/Mod/Part/App/FeaturePartCut.cpp +++ b/src/Mod/Part/App/FeaturePartCut.cpp @@ -40,12 +40,8 @@ Cut::Cut(void) { } -TopoDS_Shape Cut::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Cut::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a cut operation: - BRepAlgoAPI_Cut mkCut(base, tool); - // Let's check if the cut has been successful - if (!mkCut.IsDone()) - throw Base::Exception("Cut failed"); - return mkCut.Shape(); + return new BRepAlgoAPI_Cut(base, tool); } diff --git a/src/Mod/Part/App/FeaturePartCut.h b/src/Mod/Part/App/FeaturePartCut.h index 63b0de61a6..11187781c9 100644 --- a/src/Mod/Part/App/FeaturePartCut.h +++ b/src/Mod/Part/App/FeaturePartCut.h @@ -42,7 +42,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index d0f4a0e972..ea25450fc6 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -41,14 +41,10 @@ Fuse::Fuse(void) { } -TopoDS_Shape Fuse::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Fuse::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(base, tool); - // Let's check if the fusion has been successful - if (!mkFuse.IsDone()) - throw Base::Exception("Fusion failed"); - return mkFuse.Shape(); + return new BRepAlgoAPI_Fuse(base, tool); } // ---------------------------------------------------- diff --git a/src/Mod/Part/App/FeaturePartFuse.h b/src/Mod/Part/App/FeaturePartFuse.h index 251d7ec580..7e2e88d322 100644 --- a/src/Mod/Part/App/FeaturePartFuse.h +++ b/src/Mod/Part/App/FeaturePartFuse.h @@ -42,7 +42,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; diff --git a/src/Mod/Part/App/FeaturePartSection.cpp b/src/Mod/Part/App/FeaturePartSection.cpp index 6beb1eeef3..55cd6da6ea 100644 --- a/src/Mod/Part/App/FeaturePartSection.cpp +++ b/src/Mod/Part/App/FeaturePartSection.cpp @@ -39,12 +39,8 @@ Section::Section(void) { } -TopoDS_Shape Section::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Section::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a section operation: - BRepAlgoAPI_Section mkSection(base, tool); - // Let's check if the section has been successful - if (!mkSection.IsDone()) - throw Base::Exception("Section failed"); - return mkSection.Shape(); + return new BRepAlgoAPI_Section(base, tool); } diff --git a/src/Mod/Part/App/FeaturePartSection.h b/src/Mod/Part/App/FeaturePartSection.h index 911c2a5b7d..69ec64c927 100644 --- a/src/Mod/Part/App/FeaturePartSection.h +++ b/src/Mod/Part/App/FeaturePartSection.h @@ -42,7 +42,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index 5857b2ef71..d35cd0dde2 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -24,9 +24,15 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include +# include +# include +# include +# include #endif #include "ViewProviderBoolean.h" +#include #include #include #include @@ -71,6 +77,84 @@ QIcon ViewProviderBoolean::getIcon(void) const return ViewProviderPart::getIcon(); } +void findFaces(BRepAlgoAPI_BooleanOperation* mkBool, + const TopTools_IndexedMapOfShape& M1, + const TopTools_IndexedMapOfShape& M3, + const std::vector& colBase, + std::vector& colBool) +{ + for (int i=1; i<=M1.Extent(); i++) { + bool modified=false, generated=false; + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(mkBool->Modified(M1(i))); it.More(); it.Next()) { + modified = true; + for (int j=1; j<=M3.Extent(); j++) { + if (M3(j).IsPartner(it.Value())) { + colBool[j-1] = colBase[i-1]; + break; + } + } + } + + for (it.Initialize(mkBool->Generated(M1(i))); it.More(); it.Next()) { + generated = true; + for (int j=1; j<=M3.Extent(); j++) { + if (M3(j).IsPartner(it.Value())) { + colBool[j-1] = colBase[i-1]; + break; + } + } + } + + if (!modified && !generated && !mkBool->IsDeleted(M1(i))) { + for (int j=1; j<=M3.Extent(); j++) { + if (M3(j).IsPartner(M1(i))) { + colBool[j-1] = colBase[i-1]; + break; + } + } + } + } +} + +void ViewProviderBoolean::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyPartShape::getClassTypeId()) { + Part::Boolean* objBool = dynamic_cast(getObject()); + Part::Feature* objBase = dynamic_cast(objBool->Base.getValue()); + Part::Feature* objTool = dynamic_cast(objBool->Tool.getValue()); + + BRepAlgoAPI_BooleanOperation* mkBool = objBool->getBooleanOperation(); + if (mkBool && objBase && objTool) { + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + const TopoDS_Shape& toolShape = objTool->Shape.getValue(); + const TopoDS_Shape& boolShape = objBool->Shape.getValue(); + + TopTools_IndexedMapOfShape M1, M2, M3; + TopExp::MapShapes(baseShape, TopAbs_FACE, M1); + TopExp::MapShapes(toolShape, TopAbs_FACE, M2); + TopExp::MapShapes(boolShape, TopAbs_FACE, M3); + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + Gui::ViewProvider* vpTool = Gui::Application::Instance->getViewProvider(objTool); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + std::vector colTool = static_cast(vpTool)->DiffuseColor.getValues(); + std::vector colBool; + colBool.resize(M3.Extent(), this->ShapeColor.getValue()); + bool applyColor=false; + if (colBase.size() == M1.Extent()) { + findFaces(mkBool, M1, M3, colBase, colBool); + applyColor = true; + } + if (colTool.size() == M2.Extent()) { + findFaces(mkBool, M2, M3, colTool, colBool); + applyColor = true; + } + if (applyColor) + this->DiffuseColor.setValues(colBool); + } + } +} PROPERTY_SOURCE(PartGui::ViewProviderMultiFuse,PartGui::ViewProviderPart) diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.h b/src/Mod/Part/Gui/ViewProviderBoolean.h index 0fb245bb4e..0a34127b16 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.h +++ b/src/Mod/Part/Gui/ViewProviderBoolean.h @@ -42,6 +42,7 @@ public: /// grouping handling std::vector claimChildren(void) const; QIcon getIcon(void) const; + void updateData(const App::Property*); }; /// ViewProvider for the MultiFuse feature From 09e7c897d0381ac7667e38b45843ad49b93122aa Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 30 May 2012 21:39:01 +0200 Subject: [PATCH 32/69] Keep faces colors on boolean operations --- src/Mod/Part/App/AppPart.cpp | 1 + src/Mod/Part/App/FeaturePartBoolean.cpp | 16 ++- src/Mod/Part/App/FeaturePartBoolean.h | 5 +- src/Mod/Part/App/FeaturePartCommon.cpp | 7 + src/Mod/Part/App/FeaturePartCommon.h | 1 + src/Mod/Part/App/FeaturePartFuse.cpp | 7 + src/Mod/Part/App/FeaturePartFuse.h | 1 + src/Mod/Part/App/PartFeature.cpp | 55 ++++++++ src/Mod/Part/App/PartFeature.h | 6 +- src/Mod/Part/App/PropertyTopoShape.cpp | 66 +++++++++ src/Mod/Part/App/PropertyTopoShape.h | 62 +++++++- src/Mod/Part/Gui/ViewProviderBoolean.cpp | 171 ++++++++++++++++------- src/Mod/Part/Gui/ViewProviderBoolean.h | 2 + 13 files changed, 334 insertions(+), 66 deletions(-) diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 875f04cd4c..716597e2b1 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -134,6 +134,7 @@ void PartExport initPart() Part::TopoShape ::init(); Part::PropertyPartShape ::init(); Part::PropertyGeometryList ::init(); + Part::PropertyShapeHistory ::init(); Part::PropertyFilletEdges ::init(); Part::Feature ::init(); diff --git a/src/Mod/Part/App/FeaturePartBoolean.cpp b/src/Mod/Part/App/FeaturePartBoolean.cpp index 7418db97a6..1a2608797b 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.cpp +++ b/src/Mod/Part/App/FeaturePartBoolean.cpp @@ -24,7 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include -#include +# include #endif #include "FeaturePartBoolean.h" @@ -35,10 +35,13 @@ using namespace Part; PROPERTY_SOURCE_ABSTRACT(Part::Boolean, Part::Feature) -Boolean::Boolean(void) : myBoolOp(0) +Boolean::Boolean(void) { ADD_PROPERTY(Base,(0)); ADD_PROPERTY(Tool,(0)); + ADD_PROPERTY_TYPE(History,(ShapeHistory()), "Boolean", (App::PropertyType) + (App::Prop_Output|App::Prop_Transient|App::Prop_Hidden), "Shape history"); + History.setSize(0); } short Boolean::mustExecute() const @@ -77,14 +80,15 @@ App::DocumentObjectExecReturn *Boolean::execute(void) return new App::DocumentObjectExecReturn("Resulting shape is invalid"); } - // tmp. set boolean operation pointer - this->myBoolOp = mkBool.get(); + std::vector history; + history.push_back(buildHistory(*mkBool.get(), TopAbs_FACE, resShape, BaseShape)); + history.push_back(buildHistory(*mkBool.get(), TopAbs_FACE, resShape, ToolShape)); + this->Shape.setValue(resShape); - this->myBoolOp = 0; + this->History.setValues(history); return App::DocumentObject::StdReturn; } catch (...) { - this->myBoolOp = 0; return new App::DocumentObjectExecReturn("A fatal error occurred when running boolean operation"); } } diff --git a/src/Mod/Part/App/FeaturePartBoolean.h b/src/Mod/Part/App/FeaturePartBoolean.h index 13305dffe5..2730af82f2 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.h +++ b/src/Mod/Part/App/FeaturePartBoolean.h @@ -41,6 +41,7 @@ public: App::PropertyLink Base; App::PropertyLink Tool; + PropertyShapeHistory History; /** @name methods overide Feature */ //@{ @@ -53,13 +54,9 @@ public: const char* getViewProviderName(void) const { return "PartGui::ViewProviderBoolean"; } - BRepAlgoAPI_BooleanOperation* getBooleanOperation() const { return myBoolOp; } protected: virtual BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const = 0; - -private: - BRepAlgoAPI_BooleanOperation* myBoolOp; }; } diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index 1835cdfc14..ea4be0edd8 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -56,6 +56,9 @@ MultiCommon::MultiCommon(void) { ADD_PROPERTY(Shapes,(0)); Shapes.setSize(0); + ADD_PROPERTY_TYPE(History,(ShapeHistory()), "Boolean", (App::PropertyType) + (App::Prop_Output|App::Prop_Transient|App::Prop_Hidden), "Shape history"); + History.setSize(0); } short MultiCommon::mustExecute() const @@ -78,6 +81,7 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) } if (s.size() >= 2) { + std::vector history; TopoDS_Shape res = s.front(); for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { // Let's call algorithm computing a fuse operation: @@ -86,10 +90,13 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) if (!mkCommon.IsDone()) throw Base::Exception("Intersection failed"); res = mkCommon.Shape(); + history.push_back(buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape1())); + history.push_back(buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape2())); } if (res.IsNull()) throw Base::Exception("Resulting shape is invalid"); this->Shape.setValue(res); + this->History.setValues(history); } else { throw Base::Exception("Not enough shape objects linked"); diff --git a/src/Mod/Part/App/FeaturePartCommon.h b/src/Mod/Part/App/FeaturePartCommon.h index 19df8cdebb..ccb568aed9 100644 --- a/src/Mod/Part/App/FeaturePartCommon.h +++ b/src/Mod/Part/App/FeaturePartCommon.h @@ -53,6 +53,7 @@ public: MultiCommon(); App::PropertyLinkList Shapes; + PropertyShapeHistory History; /** @name methods override feature */ //@{ diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index ea25450fc6..8493b7b88b 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -56,6 +56,9 @@ MultiFuse::MultiFuse(void) { ADD_PROPERTY(Shapes,(0)); Shapes.setSize(0); + ADD_PROPERTY_TYPE(History,(ShapeHistory()), "Boolean", (App::PropertyType) + (App::Prop_Output|App::Prop_Transient|App::Prop_Hidden), "Shape history"); + History.setSize(0); } short MultiFuse::mustExecute() const @@ -79,6 +82,7 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) if (s.size() >= 2) { try { + std::vector history; TopoDS_Shape res = s.front(); for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { // Let's call algorithm computing a fuse operation: @@ -87,10 +91,13 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) if (!mkFuse.IsDone()) throw Base::Exception("Fusion failed"); res = mkFuse.Shape(); + history.push_back(buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape1())); + history.push_back(buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape2())); } if (res.IsNull()) throw Base::Exception("Resulting shape is invalid"); this->Shape.setValue(res); + this->History.setValues(history); } catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); diff --git a/src/Mod/Part/App/FeaturePartFuse.h b/src/Mod/Part/App/FeaturePartFuse.h index 7e2e88d322..f1c83bb6f8 100644 --- a/src/Mod/Part/App/FeaturePartFuse.h +++ b/src/Mod/Part/App/FeaturePartFuse.h @@ -54,6 +54,7 @@ public: MultiFuse(); App::PropertyLinkList Shapes; + PropertyShapeHistory History; /** @name methods override feature */ //@{ diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index c9483f37c1..e71edcc7d9 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -26,6 +26,10 @@ #ifndef _PreComp_ # include # include +# include +# include +# include +# include #endif @@ -128,6 +132,57 @@ TopLoc_Location Feature::getLocation() const return TopLoc_Location(trf); } +ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS) +{ + ShapeHistory history; + history.type = type; + + TopTools_IndexedMapOfShape newM, oldM; + TopExp::MapShapes(newS, type, newM); + TopExp::MapShapes(oldS, type, oldM); + + for (int i=1; i<=oldM.Extent(); i++) { + bool modified=false, generated=false; + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) { + modified = true; + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(it.Value())) { + history.modified[i-1].push_back(j-1); + break; + } + } + } + + for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) { + generated = true; + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(it.Value())) { + history.generated[i-1].push_back(j-1); + break; + } + } + } + + if (!modified && !generated) { + if (mkShape.IsDeleted(oldM(i))) { + history.deleted.insert(i-1); + } + else { + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(oldM(i))) { + history.accepted[i-1] = j-1; + break; + } + } + } + } + } + + return history; +} + /// returns the type name of the ViewProvider const char* Feature::getViewProviderName(void) const { return "PartGui::ViewProviderPart"; diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index efa0b7a836..159f0473e1 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -30,6 +30,8 @@ #include #include +class BRepBuilderAPI_MakeShape; + namespace Part { @@ -63,9 +65,9 @@ public: protected: void onChanged(const App::Property* prop); - -protected: TopLoc_Location getLocation() const; + ShapeHistory buildHistory(BRepBuilderAPI_MakeShape&, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS); }; class FilletBase : public Part::Feature diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index d0cc3981c2..f3e83a809a 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -343,6 +343,72 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader) // ------------------------------------------------------------------------- +TYPESYSTEM_SOURCE(Part::PropertyShapeHistory , App::PropertyLists); + +PropertyShapeHistory::PropertyShapeHistory() +{ +} + +PropertyShapeHistory::~PropertyShapeHistory() +{ +} + +void PropertyShapeHistory::setValue(const ShapeHistory& sh) +{ + aboutToSetValue(); + _lValueList.resize(1); + _lValueList[0] = sh; + hasSetValue(); +} + +void PropertyShapeHistory::setValues(const std::vector& values) +{ + aboutToSetValue(); + _lValueList = values; + hasSetValue(); +} + +PyObject *PropertyShapeHistory::getPyObject(void) +{ + return Py::new_reference_to(Py::None()); +} + +void PropertyShapeHistory::setPyObject(PyObject *value) +{ +} + +void PropertyShapeHistory::Save (Base::Writer &writer) const +{ +} + +void PropertyShapeHistory::Restore(Base::XMLReader &reader) +{ +} + +void PropertyShapeHistory::SaveDocFile (Base::Writer &writer) const +{ +} + +void PropertyShapeHistory::RestoreDocFile(Base::Reader &reader) +{ +} + +App::Property *PropertyShapeHistory::Copy(void) const +{ + PropertyShapeHistory *p= new PropertyShapeHistory(); + p->_lValueList = _lValueList; + return p; +} + +void PropertyShapeHistory::Paste(const Property &from) +{ + aboutToSetValue(); + _lValueList = dynamic_cast(from)._lValueList; + hasSetValue(); +} + +// ------------------------------------------------------------------------- + TYPESYSTEM_SOURCE(Part::PropertyFilletEdges , App::PropertyLists); PropertyFilletEdges::PropertyFilletEdges() diff --git a/src/Mod/Part/App/PropertyTopoShape.h b/src/Mod/Part/App/PropertyTopoShape.h index 3113ed4561..190a82d2a6 100644 --- a/src/Mod/Part/App/PropertyTopoShape.h +++ b/src/Mod/Part/App/PropertyTopoShape.h @@ -21,12 +21,15 @@ ***************************************************************************/ -#ifndef PROPERTYTOPOSHAPE_H -#define PROPERTYTOPOSHAPE_H +#ifndef PART_PROPERTYTOPOSHAPE_H +#define PART_PROPERTYTOPOSHAPE_H #include "TopoShape.h" +#include #include #include +#include +#include namespace Part { @@ -95,6 +98,59 @@ private: TopoShape _Shape; }; +struct PartExport ShapeHistory { + TopAbs_ShapeEnum type; + std::map > modified; + std::map > generated; + std::map accepted; + std::set deleted; +}; + +class PartExport PropertyShapeHistory : public App::PropertyLists +{ + TYPESYSTEM_HEADER(); + +public: + PropertyShapeHistory(); + ~PropertyShapeHistory(); + + virtual void setSize(int newSize) { + _lValueList.resize(newSize); + } + virtual int getSize(void) const { + return _lValueList.size(); + } + + /** Sets the property + */ + void setValue(const ShapeHistory&); + + void setValues (const std::vector& values); + + const std::vector &getValues(void) const { + return _lValueList; + } + + virtual PyObject *getPyObject(void); + virtual void setPyObject(PyObject *); + + virtual void Save (Base::Writer &writer) const; + virtual void Restore(Base::XMLReader &reader); + + virtual void SaveDocFile (Base::Writer &writer) const; + virtual void RestoreDocFile(Base::Reader &reader); + + virtual Property *Copy(void) const; + virtual void Paste(const Property &from); + + virtual unsigned int getMemSize (void) const { + return _lValueList.size() * sizeof(ShapeHistory); + } + +private: + std::vector _lValueList; +}; + /** A property class to store hash codes and two radii for the fillet algorithm. * @author Werner Mayer */ @@ -151,4 +207,4 @@ private: } //namespace Part -#endif // PROPERTYTOPOSHAPE_H +#endif // PART_PROPERTYTOPOSHAPE_H diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index d35cd0dde2..46c85bb658 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -77,80 +77,69 @@ QIcon ViewProviderBoolean::getIcon(void) const return ViewProviderPart::getIcon(); } -void findFaces(BRepAlgoAPI_BooleanOperation* mkBool, - const TopTools_IndexedMapOfShape& M1, - const TopTools_IndexedMapOfShape& M3, - const std::vector& colBase, - std::vector& colBool) +void applyColor(const Part::ShapeHistory& hist, + const std::vector& colBase, + std::vector& colBool) { - for (int i=1; i<=M1.Extent(); i++) { - bool modified=false, generated=false; - TopTools_ListIteratorOfListOfShape it; - for (it.Initialize(mkBool->Modified(M1(i))); it.More(); it.Next()) { - modified = true; - for (int j=1; j<=M3.Extent(); j++) { - if (M3(j).IsPartner(it.Value())) { - colBool[j-1] = colBase[i-1]; - break; - } - } + std::map >::const_iterator jt; + // apply color from modified faces + for (jt = hist.modified.begin(); jt != hist.modified.end(); ++jt) { + std::vector::const_iterator kt; + for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { + colBool[*kt] = colBase[jt->first]; } - - for (it.Initialize(mkBool->Generated(M1(i))); it.More(); it.Next()) { - generated = true; - for (int j=1; j<=M3.Extent(); j++) { - if (M3(j).IsPartner(it.Value())) { - colBool[j-1] = colBase[i-1]; - break; - } - } - } - - if (!modified && !generated && !mkBool->IsDeleted(M1(i))) { - for (int j=1; j<=M3.Extent(); j++) { - if (M3(j).IsPartner(M1(i))) { - colBool[j-1] = colBase[i-1]; - break; - } - } + } + // apply color from generated faces + for (jt = hist.generated.begin(); jt != hist.generated.end(); ++jt) { + std::vector::const_iterator kt; + for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { + colBool[*kt] = colBase[jt->first]; } } + // apply data from accepted faces + for (std::map::const_iterator kt = hist.accepted.begin(); kt != hist.accepted.end(); ++kt) { + colBool[kt->second] = colBase[kt->first]; + } } void ViewProviderBoolean::updateData(const App::Property* prop) { PartGui::ViewProviderPart::updateData(prop); - if (prop->getTypeId() == Part::PropertyPartShape::getClassTypeId()) { + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + if (hist.size() != 2) + return; Part::Boolean* objBool = dynamic_cast(getObject()); Part::Feature* objBase = dynamic_cast(objBool->Base.getValue()); Part::Feature* objTool = dynamic_cast(objBool->Tool.getValue()); - - BRepAlgoAPI_BooleanOperation* mkBool = objBool->getBooleanOperation(); - if (mkBool && objBase && objTool) { + if (objBase && objTool) { const TopoDS_Shape& baseShape = objBase->Shape.getValue(); const TopoDS_Shape& toolShape = objTool->Shape.getValue(); const TopoDS_Shape& boolShape = objBool->Shape.getValue(); - TopTools_IndexedMapOfShape M1, M2, M3; - TopExp::MapShapes(baseShape, TopAbs_FACE, M1); - TopExp::MapShapes(toolShape, TopAbs_FACE, M2); - TopExp::MapShapes(boolShape, TopAbs_FACE, M3); + TopTools_IndexedMapOfShape baseMap, toolMap, boolMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + TopExp::MapShapes(toolShape, TopAbs_FACE, toolMap); + TopExp::MapShapes(boolShape, TopAbs_FACE, boolMap); + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); Gui::ViewProvider* vpTool = Gui::Application::Instance->getViewProvider(objTool); std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); std::vector colTool = static_cast(vpTool)->DiffuseColor.getValues(); std::vector colBool; - colBool.resize(M3.Extent(), this->ShapeColor.getValue()); - bool applyColor=false; - if (colBase.size() == M1.Extent()) { - findFaces(mkBool, M1, M3, colBase, colBool); - applyColor = true; + colBool.resize(boolMap.Extent(), this->ShapeColor.getValue()); + + bool setColor=false; + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[0], colBase, colBool); + setColor = true; } - if (colTool.size() == M2.Extent()) { - findFaces(mkBool, M2, M3, colTool, colBool); - applyColor = true; + if (colTool.size() == toolMap.Extent()) { + applyColor(hist[1], colTool, colBool); + setColor = true; } - if (applyColor) + if (setColor) this->DiffuseColor.setValues(colBool); } } @@ -176,6 +165,46 @@ QIcon ViewProviderMultiFuse::getIcon(void) const return Gui::BitmapFactory().pixmap("Part_Fuse"); } +void ViewProviderMultiFuse::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + Part::MultiFuse* objBool = dynamic_cast(getObject()); + std::vector sources = objBool->Shapes.getValues(); + if (hist.size() != sources.size()) + return; + + const TopoDS_Shape& boolShape = objBool->Shape.getValue(); + TopTools_IndexedMapOfShape boolMap; + TopExp::MapShapes(boolShape, TopAbs_FACE, boolMap); + + std::vector colBool; + colBool.resize(boolMap.Extent(), this->ShapeColor.getValue()); + + bool setColor=false; + int index=0; + for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { + Part::Feature* objBase = dynamic_cast(*it); + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[index], colBase, colBool); + setColor = true; + } + } + + if (setColor) + this->DiffuseColor.setValues(colBool); + } +} + PROPERTY_SOURCE(PartGui::ViewProviderMultiCommon,PartGui::ViewProviderPart) @@ -196,3 +225,43 @@ QIcon ViewProviderMultiCommon::getIcon(void) const { return Gui::BitmapFactory().pixmap("Part_Common"); } + +void ViewProviderMultiCommon::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + Part::MultiCommon* objBool = dynamic_cast(getObject()); + std::vector sources = objBool->Shapes.getValues(); + if (hist.size() != sources.size()) + return; + + const TopoDS_Shape& boolShape = objBool->Shape.getValue(); + TopTools_IndexedMapOfShape boolMap; + TopExp::MapShapes(boolShape, TopAbs_FACE, boolMap); + + std::vector colBool; + colBool.resize(boolMap.Extent(), this->ShapeColor.getValue()); + + bool setColor=false; + int index=0; + for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { + Part::Feature* objBase = dynamic_cast(*it); + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[index], colBase, colBool); + setColor = true; + } + } + + if (setColor) + this->DiffuseColor.setValues(colBool); + } +} diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.h b/src/Mod/Part/Gui/ViewProviderBoolean.h index 0a34127b16..29c5311092 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.h +++ b/src/Mod/Part/Gui/ViewProviderBoolean.h @@ -59,6 +59,7 @@ public: /// grouping handling std::vector claimChildren(void) const; QIcon getIcon(void) const; + void updateData(const App::Property*); }; /// ViewProvider for the MultiFuse feature @@ -75,6 +76,7 @@ public: /// grouping handling std::vector claimChildren(void) const; QIcon getIcon(void) const; + void updateData(const App::Property*); }; From 5875c1bd1c4a2c4e9cc0f889342fce43d0e67146 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 31 May 2012 15:28:15 +0200 Subject: [PATCH 33/69] Keep faces colors on boolean operations --- src/Mod/Part/App/FeaturePartCommon.cpp | 47 ++++++++++++++++-------- src/Mod/Part/App/FeaturePartFuse.cpp | 14 ++++++- src/Mod/Part/App/PartFeature.cpp | 37 +++++++++++++++---- src/Mod/Part/App/PartFeature.h | 1 + src/Mod/Part/App/PropertyTopoShape.h | 8 ++-- src/Mod/Part/Gui/ViewProviderBoolean.cpp | 13 +------ 6 files changed, 79 insertions(+), 41 deletions(-) diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index ea4be0edd8..44c93a01c3 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -25,6 +25,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include #endif @@ -81,22 +82,38 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) } if (s.size() >= 2) { - std::vector history; - TopoDS_Shape res = s.front(); - for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { - // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Common mkCommon(res, *it); - // Let's check if the fusion has been successful - if (!mkCommon.IsDone()) - throw Base::Exception("Intersection failed"); - res = mkCommon.Shape(); - history.push_back(buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape1())); - history.push_back(buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape2())); + try { + std::vector history; + TopoDS_Shape res = s.front(); + for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { + // Let's call algorithm computing a fuse operation: + BRepAlgoAPI_Common mkCommon(res, *it); + // Let's check if the fusion has been successful + if (!mkCommon.IsDone()) + throw Base::Exception("Intersection failed"); + res = mkCommon.Shape(); + + ShapeHistory hist1 = buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape1()); + ShapeHistory hist2 = buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape2()); + if (history.empty()) { + history.push_back(hist1); + history.push_back(hist2); + } + else { + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist1); + history.push_back(hist2); + } + } + if (res.IsNull()) + throw Base::Exception("Resulting shape is invalid"); + this->Shape.setValue(res); + this->History.setValues(history); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); } - if (res.IsNull()) - throw Base::Exception("Resulting shape is invalid"); - this->Shape.setValue(res); - this->History.setValues(history); } else { throw Base::Exception("Not enough shape objects linked"); diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index 8493b7b88b..47efb7a445 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -91,8 +91,18 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) if (!mkFuse.IsDone()) throw Base::Exception("Fusion failed"); res = mkFuse.Shape(); - history.push_back(buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape1())); - history.push_back(buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape2())); + + ShapeHistory hist1 = buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape1()); + ShapeHistory hist2 = buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape2()); + if (history.empty()) { + history.push_back(hist1); + history.push_back(hist2); + } + else { + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist1); + history.push_back(hist2); + } } if (res.IsNull()) throw Base::Exception("Resulting shape is invalid"); diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index e71edcc7d9..ef43452df9 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -143,36 +143,36 @@ ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_Sha TopExp::MapShapes(oldS, type, oldM); for (int i=1; i<=oldM.Extent(); i++) { - bool modified=false, generated=false; + bool found = false; TopTools_ListIteratorOfListOfShape it; for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) { - modified = true; + found = true; for (int j=1; j<=newM.Extent(); j++) { if (newM(j).IsPartner(it.Value())) { - history.modified[i-1].push_back(j-1); + history.shapeMap[i-1].push_back(j-1); break; } } } for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) { - generated = true; + found = true; for (int j=1; j<=newM.Extent(); j++) { if (newM(j).IsPartner(it.Value())) { - history.generated[i-1].push_back(j-1); + history.shapeMap[i-1].push_back(j-1); break; } } } - if (!modified && !generated) { + if (!found) { if (mkShape.IsDeleted(oldM(i))) { - history.deleted.insert(i-1); + history.shapeMap[i-1] = std::vector(); } else { for (int j=1; j<=newM.Extent(); j++) { if (newM(j).IsPartner(oldM(i))) { - history.accepted[i-1] = j-1; + history.shapeMap[i-1].push_back(j-1); break; } } @@ -183,6 +183,27 @@ ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_Sha return history; } +ShapeHistory Feature::joinHistory(const ShapeHistory& oldH, const ShapeHistory& newH) +{ + ShapeHistory join; + join.type = oldH.type; + + for (ShapeHistory::MapList::const_iterator it = oldH.shapeMap.begin(); it != oldH.shapeMap.end(); ++it) { + int old_shape_index = it->first; + if (it->second.empty()) + join.shapeMap[old_shape_index] = ShapeHistory::List(); + for (ShapeHistory::List::const_iterator jt = it->second.begin(); jt != it->second.end(); ++jt) { + ShapeHistory::MapList::const_iterator kt = newH.shapeMap.find(*jt); + if (kt != newH.shapeMap.end()) { + ShapeHistory::List& ary = join.shapeMap[old_shape_index]; + ary.insert(ary.end(), kt->second.begin(), kt->second.end()); + } + } + } + + return join; +} + /// returns the type name of the ViewProvider const char* Feature::getViewProviderName(void) const { return "PartGui::ViewProviderPart"; diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index 159f0473e1..689736a420 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -68,6 +68,7 @@ protected: TopLoc_Location getLocation() const; ShapeHistory buildHistory(BRepBuilderAPI_MakeShape&, TopAbs_ShapeEnum type, const TopoDS_Shape& newS, const TopoDS_Shape& oldS); + ShapeHistory joinHistory(const ShapeHistory&, const ShapeHistory&); }; class FilletBase : public Part::Feature diff --git a/src/Mod/Part/App/PropertyTopoShape.h b/src/Mod/Part/App/PropertyTopoShape.h index 190a82d2a6..a75c85ccd0 100644 --- a/src/Mod/Part/App/PropertyTopoShape.h +++ b/src/Mod/Part/App/PropertyTopoShape.h @@ -99,11 +99,11 @@ private: }; struct PartExport ShapeHistory { + typedef std::map > MapList; + typedef std::vector List; + TopAbs_ShapeEnum type; - std::map > modified; - std::map > generated; - std::map accepted; - std::set deleted; + MapList shapeMap; }; class PartExport PropertyShapeHistory : public App::PropertyLists diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index 46c85bb658..22a9c8a4ea 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -83,23 +83,12 @@ void applyColor(const Part::ShapeHistory& hist, { std::map >::const_iterator jt; // apply color from modified faces - for (jt = hist.modified.begin(); jt != hist.modified.end(); ++jt) { + for (jt = hist.shapeMap.begin(); jt != hist.shapeMap.end(); ++jt) { std::vector::const_iterator kt; for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { colBool[*kt] = colBase[jt->first]; } } - // apply color from generated faces - for (jt = hist.generated.begin(); jt != hist.generated.end(); ++jt) { - std::vector::const_iterator kt; - for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { - colBool[*kt] = colBase[jt->first]; - } - } - // apply data from accepted faces - for (std::map::const_iterator kt = hist.accepted.begin(); kt != hist.accepted.end(); ++kt) { - colBool[kt->second] = colBase[kt->first]; - } } void ViewProviderBoolean::updateData(const App::Property* prop) From e820323177e3ac6e99688513b25aa93643c31830 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 31 May 2012 23:44:50 +0200 Subject: [PATCH 34/69] Keep faces colors on boolean operations --- src/Mod/Part/Gui/ViewProviderBoolean.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index 22a9c8a4ea..d73afe2966 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -124,10 +124,20 @@ void ViewProviderBoolean::updateData(const App::Property* prop) applyColor(hist[0], colBase, colBool); setColor = true; } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[0], colBase, colBool); + setColor = true; + } if (colTool.size() == toolMap.Extent()) { applyColor(hist[1], colTool, colBool); setColor = true; } + else if (!colTool.empty() && colTool[0] != this->ShapeColor.getValue()) { + colTool.resize(toolMap.Extent(), colTool[0]); + applyColor(hist[1], colTool, colBool); + setColor = true; + } if (setColor) this->DiffuseColor.setValues(colBool); } @@ -187,6 +197,11 @@ void ViewProviderMultiFuse::updateData(const App::Property* prop) applyColor(hist[index], colBase, colBool); setColor = true; } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[index], colBase, colBool); + setColor = true; + } } if (setColor) @@ -248,6 +263,11 @@ void ViewProviderMultiCommon::updateData(const App::Property* prop) applyColor(hist[index], colBase, colBool); setColor = true; } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[index], colBase, colBool); + setColor = true; + } } if (setColor) From dd227d83b9027cea091e2703d30b167f165b1319 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 1 Jun 2012 16:44:57 +0200 Subject: [PATCH 35/69] Make a convenient function to convert between gp_Trsf and Base::Matrix4D --- src/Mod/Part/App/TopoShape.cpp | 38 ++++++++++++++++++++-------------- src/Mod/Part/App/TopoShape.h | 2 ++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index fd90af18f5..9bf4870744 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -433,25 +433,19 @@ void TopoShape::operator = (const TopoShape& sh) } } -void TopoShape::setTransform(const Base::Matrix4D& rclTrf) +void TopoShape::convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf) { - gp_Trsf mov; - mov.SetValues(rclTrf[0][0],rclTrf[0][1],rclTrf[0][2],rclTrf[0][3], - rclTrf[1][0],rclTrf[1][1],rclTrf[1][2],rclTrf[1][3], - rclTrf[2][0],rclTrf[2][1],rclTrf[2][2],rclTrf[2][3], - 0.00001,0.00001); - TopLoc_Location loc(mov); - _Shape.Location(loc); + trsf.SetValues(mtrx[0][0],mtrx[0][1],mtrx[0][2],mtrx[0][3], + mtrx[1][0],mtrx[1][1],mtrx[1][2],mtrx[1][3], + mtrx[2][0],mtrx[2][1],mtrx[2][2],mtrx[2][3], + 0.00001,0.00001); } -Base::Matrix4D TopoShape::getTransform(void) const +void TopoShape::convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx) { - Base::Matrix4D mtrx; - gp_Trsf Trf = _Shape.Location().Transformation(); - - gp_Mat m = Trf._CSFDB_Getgp_Trsfmatrix(); - gp_XYZ p = Trf._CSFDB_Getgp_Trsfloc(); - Standard_Real scale = Trf._CSFDB_Getgp_Trsfscale(); + gp_Mat m = trsf._CSFDB_Getgp_Trsfmatrix(); + gp_XYZ p = trsf._CSFDB_Getgp_Trsfloc(); + Standard_Real scale = trsf._CSFDB_Getgp_Trsfscale(); // set Rotation matrix mtrx[0][0] = scale * m._CSFDB_Getgp_Matmatrix(0,0); @@ -470,7 +464,21 @@ Base::Matrix4D TopoShape::getTransform(void) const mtrx[0][3] = p._CSFDB_Getgp_XYZx(); mtrx[1][3] = p._CSFDB_Getgp_XYZy(); mtrx[2][3] = p._CSFDB_Getgp_XYZz(); +} +void TopoShape::setTransform(const Base::Matrix4D& rclTrf) +{ + gp_Trsf mov; + convertTogpTrsf(rclTrf, mov); + TopLoc_Location loc(mov); + _Shape.Location(loc); +} + +Base::Matrix4D TopoShape::getTransform(void) const +{ + Base::Matrix4D mtrx; + gp_Trsf Trf = _Shape.Location().Transformation(); + convertToMatrix(Trf, mtrx); return mtrx; } diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 1de76b1cad..1d16b9e073 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -73,6 +73,8 @@ public: Base::Matrix4D getTransform(void) const; /// Bound box from the CasCade shape Base::BoundBox3d getBoundBox(void)const; + static void convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf); + static void convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx); //@} /** @name Subelement management */ From e36334b94e7bdb201ef41dd2488d75c57fbe2b90 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 1 Jun 2012 15:19:21 -0300 Subject: [PATCH 36/69] Draft: Added Draft_ToggleGrid() command to toggle the Draft grid --- src/Mod/Arch/InitGui.py | 5 ++-- src/Mod/Draft/DraftSnap.py | 54 ++++++++++++++++++++----------------- src/Mod/Draft/DraftTools.py | 23 +++++++++++++++- src/Mod/Draft/InitGui.py | 6 ++--- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 1de87ed470..32e9d70db5 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -69,8 +69,9 @@ class ArchWorkbench(Workbench): "Draft_Offset","Draft_Upgrade", "Draft_Downgrade","Draft_Trimex"] self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode", - "Draft_AddToGroup","Draft_SelectGroup", - "Draft_SelectPlane","Draft_ToggleSnap"] + "Draft_AddToGroup","Draft_SelectGroup", + "Draft_SelectPlane","Draft_ToggleSnap", + "Draft_ShowSnapBar","Draft_ToggleGrid"] self.meshtools = ["Arch_SplitMesh","Arch_MeshToShape", "Arch_SelectNonSolidMeshes","Arch_RemoveShape"] self.appendToolbar(str(DraftTools.translate("arch","Arch tools")),self.archtools) diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 1479a65688..183f20a52d 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -73,6 +73,7 @@ class Snapper: self.snapInfo = None self.lastSnappedObject = None self.active = True + self.forceGridOff = False self.trackers = [[],[],[],[]] # view, grid, snap, extline self.polarAngles = [90,45] @@ -166,7 +167,7 @@ class Snapper: self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) # set the grid - if self.grid and Draft.getParam("grid"): + if self.grid and Draft.getParam("grid") and (not self.forceGridOff): self.grid.set() # activate snap @@ -243,18 +244,20 @@ class Snapper: if (not self.maxEdges) or (len(obj.Edges) <= self.maxEdges): if "Edge" in comp: # we are snapping to an edge - edge = obj.Shape.Edges[int(comp[4:])-1] - snaps.extend(self.snapToEndpoints(edge)) - snaps.extend(self.snapToMidpoint(edge)) - snaps.extend(self.snapToPerpendicular(edge,lastpoint)) - #snaps.extend(self.snapToOrtho(edge,lastpoint,constrain)) # now part of snapToPolar - snaps.extend(self.snapToIntersection(edge)) - snaps.extend(self.snapToElines(edge,eline)) + en = int(comp[4:])-1 + if len(obj.Shape.Edges) > en: + edge = obj.Shape.Edges[en] + snaps.extend(self.snapToEndpoints(edge)) + snaps.extend(self.snapToMidpoint(edge)) + snaps.extend(self.snapToPerpendicular(edge,lastpoint)) + #snaps.extend(self.snapToOrtho(edge,lastpoint,constrain)) # now part of snapToPolar + snaps.extend(self.snapToIntersection(edge)) + snaps.extend(self.snapToElines(edge,eline)) - if isinstance (edge.Curve,Part.Circle): - # the edge is an arc, we have extra options - snaps.extend(self.snapToAngles(edge)) - snaps.extend(self.snapToCenter(edge)) + if isinstance (edge.Curve,Part.Circle): + # the edge is an arc, we have extra options + snaps.extend(self.snapToAngles(edge)) + snaps.extend(self.snapToCenter(edge)) elif "Vertex" in comp: # directly snapped to a vertex @@ -418,18 +421,19 @@ class Snapper: def snapToGrid(self,point): "returns a grid snap point if available" if self.grid: - if self.isEnabled("grid"): - np = self.grid.getClosestNode(point) - if np: - if self.radius != 0: - dv = point.sub(np) - if dv.Length <= self.radius: - if self.tracker: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['grid']) - self.tracker.on() - self.setCursor('grid') - return np + if self.grid.Visible: + if self.isEnabled("grid"): + np = self.grid.getClosestNode(point) + if np: + if self.radius != 0: + dv = point.sub(np) + if dv.Length <= self.radius: + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['grid']) + self.tracker.on() + self.setCursor('grid') + return np return point def snapToEndpoints(self,shape): @@ -889,7 +893,7 @@ class Snapper: if self.grid: if self.grid.Visible: self.grid.set() - + if not hasattr(FreeCADGui,"Snapper"): FreeCADGui.Snapper = Snapper() if not hasattr(FreeCAD,"DraftWorkingPlane"): diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index b37e07c00f..559e75e268 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -3828,7 +3828,27 @@ class Draft_Clone(): return True else: return False - + + +class ToggleGrid(): + "The Draft ToggleGrid command definition" + + def GetResources(self): + return {'Pixmap' : 'Snap_Grid', + 'Accel' : "G,R", + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleGrid", "Toggle Grid"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_ToggleGrid", "Toggles the Draft gid on/off")} + + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if FreeCADGui.Snapper.grid: + if FreeCADGui.Snapper.grid.Visible: + FreeCADGui.Snapper.grid.off() + FreeCADGui.Snapper.forceGridOff=True + else: + FreeCADGui.Snapper.grid.on() + FreeCADGui.Snapper.forceGridOff=False + #--------------------------------------------------------------------------- # Adds the icons & commands to the FreeCAD command manager, and sets defaults #--------------------------------------------------------------------------- @@ -3876,6 +3896,7 @@ FreeCADGui.addCommand('Draft_SelectGroup',SelectGroup()) FreeCADGui.addCommand('Draft_Shape2DView',Shape2DView()) FreeCADGui.addCommand('Draft_ToggleSnap',ToggleSnap()) FreeCADGui.addCommand('Draft_ShowSnapBar',ShowSnapBar()) +FreeCADGui.addCommand('Draft_ToggleGrid',ToggleGrid()) # a global place to look for active draft Command FreeCAD.activeDraftCommand = None diff --git a/src/Mod/Draft/InitGui.py b/src/Mod/Draft/InitGui.py index 84d0b9b868..3e2bd5696d 100644 --- a/src/Mod/Draft/InitGui.py +++ b/src/Mod/Draft/InitGui.py @@ -192,12 +192,12 @@ class DraftWorkbench (Workbench): "Draft_Clone"] self.treecmdList = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup", "Draft_SelectGroup","Draft_SelectPlane","Draft_ToggleSnap", - "Draft_ShowSnapBar"] + "Draft_ShowSnapBar","Draft_ToggleGrid"] self.lineList = ["Draft_UndoLine","Draft_FinishLine","Draft_CloseLine"] self.appendToolbar(str(DraftTools.translate("draft","Draft creation tools")),self.cmdList) self.appendToolbar(str(DraftTools.translate("draft","Draft modification tools")),self.modList) self.appendMenu(str(DraftTools.translate("draft","&Draft")),self.cmdList+self.modList) - self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Display options"))],self.treecmdList) + self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Context tools"))],self.treecmdList) self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Wire tools"))],self.lineList) def Activated(self): @@ -211,7 +211,7 @@ class DraftWorkbench (Workbench): if (FreeCAD.activeDraftCommand == None): if (FreeCADGui.Selection.getSelection()): self.appendContextMenu("Draft",self.cmdList+self.modList) - self.appendContextMenu("Display options",self.treecmdList) + self.appendContextMenu("Draft context tools",self.treecmdList) else: self.appendContextMenu("Draft",self.cmdList) else: From 5118a51ead92cd0076374904e228b656b9a1d2b3 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 1 Jun 2012 16:26:31 -0300 Subject: [PATCH 37/69] Arch: Small fix in menu name --- src/Mod/Arch/InitGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 32e9d70db5..4c5384ff08 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -76,7 +76,7 @@ class ArchWorkbench(Workbench): "Arch_SelectNonSolidMeshes","Arch_RemoveShape"] self.appendToolbar(str(DraftTools.translate("arch","Arch tools")),self.archtools) self.appendToolbar(str(DraftTools.translate("arch","Draft tools")),self.drafttools) - self.appendMenu([str(DraftTools.translate("arch","&Architecture")),str(DraftTools.translate("arch","Tools"))],self.meshtools) + self.appendMenu([str(DraftTools.translate("arch","&Architecture")),str(DraftTools.translate("arch","Conversion Tools"))],self.meshtools) self.appendMenu(str(DraftTools.translate("arch","&Architecture")),self.archtools) self.appendMenu(str(DraftTools.translate("arch","&Draft")),self.drafttools+self.draftcontexttools) FreeCADGui.addIconPath(":/icons") From 278d41969e71ec85cc8824f125c666872841afaa Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 2 Jun 2012 18:52:47 +0200 Subject: [PATCH 38/69] 0000631: Support of more sophisticated switch to configure with cmake --- CMakeLists.txt | 13 ++++++++++++- src/3rdParty/CMakeLists.txt | 4 ++-- src/Base/CMakeLists.txt | 15 +++++---------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dda4867a28..2d2eaf6849 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,10 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_definitions(-Wno-write-strings) add_definitions(-Wno-deprecated) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + # get linker errors as soon as possible and not at runtime e.g. for modules + if(UNIX) + SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") + endif(UNIX) endif(CMAKE_COMPILER_IS_GNUCXX) @@ -98,7 +102,14 @@ OPTION(FREECAD_BUILD_FEM "Build the FreeCAD FEM module, be aware, unfinished cod OPTION(FREECAD_BUILD_SANDBOX "Build the FreeCAD Sandbox module which is only for testing purposes" OFF) OPTION(FREECAD_BUILD_TEMPLATE "Build the FreeCAD template module which is only for testing purposes" OFF) OPTION(FREECAD_BUILD_DEBIAN "Prepare for a build of a Debian package" OFF) -OPTION(USE_EXTERNAL_ZIPIOS "Use system installed zipios++ instead of the bundled." OFF) +OPTION(FREECAD_USE_EXTERNAL_ZIPIOS "Use system installed zipios++ instead of the bundled." OFF) +OPTION(FREECAD_USE_EXTERNAL_PIVY "Use system installed python-pivy instead of the bundled." OFF) + +# if this is set override some options +if (FREECAD_BUILD_DEBIAN) + set(FREECAD_USE_EXTERNAL_ZIPIOS ON) + set(FREECAD_USE_EXTERNAL_PIVY ON) +endif (FREECAD_BUILD_DEBIAN) # ============================================================================== diff --git a/src/3rdParty/CMakeLists.txt b/src/3rdParty/CMakeLists.txt index 5309c7a1fa..456c1f3253 100644 --- a/src/3rdParty/CMakeLists.txt +++ b/src/3rdParty/CMakeLists.txt @@ -20,7 +20,7 @@ elseif(FREECAD_BUILD_GUI AND FREECAD_LIBPACK_CHECKFILE7X) #endif(MINGW) # applies for Unix, MinGW and Windows with custom LibPack elseif(FREECAD_BUILD_GUI) - if (NOT FREECAD_BUILD_DEBIAN) + if (NOT FREECAD_USE_EXTERNAL_PIVY) find_path(COIN_VERSION3 Inventor/scxml/ScXML.h ${COIN3D_INCLUDE_DIR}) if (COIN_VERSION3) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy-0.5) @@ -31,7 +31,7 @@ elseif(FREECAD_BUILD_GUI) add_subdirectory(Pivy) endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy) endif(COIN_VERSION3) - endif (NOT FREECAD_BUILD_DEBIAN) + endif (NOT FREECAD_USE_EXTERNAL_PIVY) endif(FREECAD_BUILD_GUI AND FREECAD_LIBPACK_CHECKFILE6X) # For Windows we have all stuff in the LibPack diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index fdfb8a2b10..3cab433544 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -68,7 +68,7 @@ if(SWIG_FOUND) add_definitions(-DHAVE_SWIG=1) endif(SWIG_FOUND) -if (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_BUILD_DEBIAN) +if (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_USE_EXTERNAL_ZIPIOS) SET(zipios_SRCS ../zipios++/backbuffer.h ../zipios++/basicentry.cpp @@ -122,12 +122,7 @@ SET(zipios_SRCS ../zipios++/zipoutputstream.h ) SOURCE_GROUP("zipios" FILES ${zipios_SRCS}) -else (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_BUILD_DEBIAN) - set(FreeCADBase_LIBS - ${FreeCADBase_LIBS} - -lzipios - ) -endif (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_BUILD_DEBIAN) +endif () SET(pycxx_SRCS ../CXX/Config.hxx @@ -293,7 +288,7 @@ SET(FreeCADBase_SRCS ) # Use external zipios++ if specified. -if(USE_EXTERNAL_ZIPIOS) +if(FREECAD_USE_EXTERNAL_ZIPIOS) find_library(ZIPIOS_LIBRARY zipios) find_path(ZIPIOS_INCLUDES zipios++/zipios-config.h) if(ZIPIOS_LIBRARY) @@ -308,9 +303,9 @@ if(USE_EXTERNAL_ZIPIOS) else() message(FATAL_ERROR "Using external zipios++ was specified but was not found.") endif() -else(USE_EXTERNAL_ZIPIOS) +else(FREECAD_USE_EXTERNAL_ZIPIOS) list(APPEND FreeCADBase_SRCS ${zipios_SRCS}) -endif(USE_EXTERNAL_ZIPIOS) +endif(FREECAD_USE_EXTERNAL_ZIPIOS) if(MSVC) From 6d9b3155296efeef3387325d459bd7be93357f9d Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sat, 2 Jun 2012 14:12:43 -0300 Subject: [PATCH 39/69] Arch: Windows are now autosubtracted from support walls on creation --- src/Mod/Arch/ArchWindow.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 40b038aed0..de7df1b6e1 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -88,11 +88,27 @@ class _CommandWindow: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Window"))) - FreeCADGui.doCommand("import Arch") - for obj in sel: - FreeCADGui.doCommand("Arch.makeWindow(FreeCAD.ActiveDocument."+obj.Name+")") - FreeCAD.ActiveDocument.commitTransaction() + if sel: + if Draft.getType(sel[0]) == "Wall": + FreeCADGui.activateWorkbench("SketcherWorkbench") + FreeCADGui.runCommand("Sketcher_NewSketch") + else: + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Window"))) + FreeCADGui.doCommand("import Arch") + for obj in sel: + FreeCADGui.doCommand("Arch.makeWindow(FreeCAD.ActiveDocument."+obj.Name+")") + if hasattr(obj,"Support"): + if obj.Support: + if isinstance(obj.Support,tuple): + s = obj.Support[0] + else: + s = obj.Support + w = FreeCAD.ActiveDocument.Objects[-1] # last created object + FreeCADGui.doCommand("Arch.removeComponents(FreeCAD.ActiveDocument."+w.Name+",host=FreeCAD.ActiveDocument."+s.Name+")") + elif Draft.isClone(w,"Window"): + if w.Objects[0].Inlist: + FreeCADGui.doCommand("Arch.removeComponents(FreeCAD.ActiveDocument."+w.Name+",host=FreeCAD.ActiveDocument."+w.Objects[0].Inlist[0].Name+")") + FreeCAD.ActiveDocument.commitTransaction() class _Window(ArchComponent.Component): "The Window object" From 1897be268d87fdab0ed6e64d5982eaac26c8d9af Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 3 Jun 2012 01:26:55 +0200 Subject: [PATCH 40/69] Start implementing ActionSelector class --- src/3rdParty/Pivy-0.5/CMakeLists.txt | 2 +- src/Gui/Widgets.cpp | 111 +++++++++++++++++++++++---- src/Gui/Widgets.h | 24 ++++++ 3 files changed, 120 insertions(+), 17 deletions(-) diff --git a/src/3rdParty/Pivy-0.5/CMakeLists.txt b/src/3rdParty/Pivy-0.5/CMakeLists.txt index 3b71e24a17..fc810fb790 100644 --- a/src/3rdParty/Pivy-0.5/CMakeLists.txt +++ b/src/3rdParty/Pivy-0.5/CMakeLists.txt @@ -88,7 +88,7 @@ if(MSVC) debug ${PYTHON_DEBUG_LIBRARY} optimized ${PYTHON_LIBRARY}) else(MSVC) - set(CoinPy_LIBS + set(SoQtPy_LIBS ${SOQT_LIBRARIES} ${COIN3D_LIBRARY} ${PYTHON_LIBRARY}) diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index 1db07b7cd9..714774d061 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -23,7 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include +# include # include # include # include @@ -102,6 +102,85 @@ void CommandIconView::onSelectionChanged(QListWidgetItem * item, QListWidgetItem emitSelectionChanged(item->toolTip()); } +// ------------------------------------------------------------------------------ + +ActionSelector::ActionSelector(QWidget* parent) + : QWidget(parent) +{ + moveActionRightButton = new QPushButton(this); + moveActionRightButton->setMinimumSize(QSize(30, 30)); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/button_right.xpm"), QSize(), QIcon::Normal, QIcon::Off); + moveActionRightButton->setIcon(icon); + gridLayout->addWidget(moveActionRightButton, 1, 1, 1, 1); + + spacerItem = new QSpacerItem(33, 57, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem, 5, 1, 1, 1); + spacerItem1 = new QSpacerItem(33, 58, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem1, 0, 1, 1, 1); + + moveActionLeftButton = new QPushButton(this); + moveActionLeftButton->setMinimumSize(QSize(30, 30)); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/icons/button_left.xpm"), QSize(), QIcon::Normal, QIcon::Off); + moveActionLeftButton->setIcon(icon1); + moveActionLeftButton->setAutoDefault(true); + moveActionLeftButton->setDefault(false); + + gridLayout->addWidget(moveActionLeftButton, 2, 1, 1, 1); + + moveActionDownButton = new QPushButton(this); + moveActionDownButton->setMinimumSize(QSize(30, 30)); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); + moveActionDownButton->setIcon(icon2); + moveActionDownButton->setAutoDefault(true); + + gridLayout->addWidget(moveActionDownButton, 4, 1, 1, 1); + + moveActionUpButton = new QPushButton(this); + moveActionUpButton->setMinimumSize(QSize(30, 30)); + QIcon icon3; + icon3.addFile(QString::fromUtf8(":/icons/button_up.xpm"), QSize(), QIcon::Normal, QIcon::Off); + moveActionUpButton->setIcon(icon3); + + gridLayout->addWidget(moveActionUpButton, 3, 1, 1, 1); + + vboxLayout = new QVBoxLayout(); + vboxLayout->setContentsMargins(0, 0, 0, 0); + label_2 = new QLabel(this); + vboxLayout->addWidget(label_2); + + avalableTreeWidget = new QTreeWidget(this); + avalableTreeWidget->setRootIsDecorated(false); + avalableTreeWidget->setColumnCount(0); + vboxLayout->addWidget(avalableTreeWidget); + + gridLayout->addLayout(vboxLayout, 0, 0, 6, 1); + + vboxLayout1 = new QVBoxLayout(); + vboxLayout1->setContentsMargins(0, 0, 0, 0); + label = new QLabel(this); + vboxLayout1->addWidget(label); + + selectedTreeWidget = new QTreeWidget(this); + vboxLayout1->addWidget(selectedTreeWidget); + + gridLayout->addLayout(vboxLayout1, 0, 2, 6, 1); + + moveActionRightButton->setText(QString()); + moveActionLeftButton->setText(QString()); + moveActionDownButton->setText(QString()); + moveActionUpButton->setText(QString()); + label_2->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); + label->setText(QApplication::translate("Gui::ActionSelector", "Selected:", 0, QApplication::UnicodeUTF8)); +} + +ActionSelector::~ActionSelector() +{ +} + + // ------------------------------------------------------------------------------ /* TRANSLATOR Gui::AccelLineEdit */ @@ -628,10 +707,10 @@ StatusWidget::StatusWidget(QWidget* parent) label = new QLabel(this); label->setAlignment(Qt::AlignCenter); - QGridLayout* gridLayout = new QGridLayout(this); - gridLayout->setSpacing(6); - gridLayout->setMargin(9); - gridLayout->addWidget(label, 0, 0, 1, 1); + QGridLayout* gridLayout = new QGridLayout(this); + gridLayout->setSpacing(6); + gridLayout->setMargin(9); + gridLayout->addWidget(label, 0, 0, 1, 1); } StatusWidget::~StatusWidget() @@ -775,20 +854,20 @@ void LabelEditor::setText(const QString& s) void LabelEditor::changeText() { QDialog dlg(this); - QVBoxLayout* hboxLayout = new QVBoxLayout(&dlg); - QDialogButtonBox* buttonBox = new QDialogButtonBox(&dlg); - buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Close); + QVBoxLayout* hboxLayout = new QVBoxLayout(&dlg); + QDialogButtonBox* buttonBox = new QDialogButtonBox(&dlg); + buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Close); QPlainTextEdit *edit = new QPlainTextEdit(&dlg); edit->setPlainText(this->lineEdit->text()); - - hboxLayout->addWidget(edit); - hboxLayout->addWidget(buttonBox); - connect(buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject())); - if (dlg.exec() == QDialog::Accepted) { - this->lineEdit->setText(edit->toPlainText()); - } + + hboxLayout->addWidget(edit); + hboxLayout->addWidget(buttonBox); + connect(buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject())); + if (dlg.exec() == QDialog::Accepted) { + this->lineEdit->setText(edit->toPlainText()); + } } /** diff --git a/src/Gui/Widgets.h b/src/Gui/Widgets.h index 16baf74b7a..a13e407c6e 100644 --- a/src/Gui/Widgets.h +++ b/src/Gui/Widgets.h @@ -63,6 +63,30 @@ Q_SIGNALS: // ------------------------------------------------------------------------------ +class ActionSelector : public QWidget +{ +public: + ActionSelector(QWidget* parent=0); + ~ActionSelector(); + +private: + QGridLayout *gridLayout; + QPushButton *moveActionRightButton; + QSpacerItem *spacerItem; + QSpacerItem *spacerItem1; + QPushButton *moveActionLeftButton; + QPushButton *moveActionDownButton; + QPushButton *moveActionUpButton; + QVBoxLayout *vboxLayout; + QLabel *label_2; + QTreeWidget *avalableTreeWidget; + QVBoxLayout *vboxLayout1; + QLabel *label; + QTreeWidget *selectedTreeWidget; +}; + +// ------------------------------------------------------------------------------ + /** * The AccelLineEdit class provides a lineedit to specfify shortcuts. * \author Werner Mayer From ebb220d2f4ca70b484491cdc0e95fa455d5aa804 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 3 Jun 2012 12:11:38 +0200 Subject: [PATCH 41/69] 0000719: Implement a KActionSelector-like widget --- src/Gui/Widgets.cpp | 225 +++++++++++++++---- src/Gui/Widgets.h | 43 +++- src/Tools/plugins/widget/FreeCAD_widgets.sln | 20 -- src/Tools/plugins/widget/customwidgets.cpp | 88 ++++++++ src/Tools/plugins/widget/customwidgets.h | 27 +++ src/Tools/plugins/widget/plugin.cpp | 74 ++++++ 6 files changed, 407 insertions(+), 70 deletions(-) delete mode 100644 src/Tools/plugins/widget/FreeCAD_widgets.sln diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index 714774d061..98a87442c5 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -104,82 +104,229 @@ void CommandIconView::onSelectionChanged(QListWidgetItem * item, QListWidgetItem // ------------------------------------------------------------------------------ +/* TRANSLATOR Gui::ActionSelector */ + ActionSelector::ActionSelector(QWidget* parent) : QWidget(parent) { - moveActionRightButton = new QPushButton(this); - moveActionRightButton->setMinimumSize(QSize(30, 30)); + addButton = new QPushButton(this); + addButton->setMinimumSize(QSize(30, 30)); QIcon icon; icon.addFile(QString::fromUtf8(":/icons/button_right.xpm"), QSize(), QIcon::Normal, QIcon::Off); - moveActionRightButton->setIcon(icon); - gridLayout->addWidget(moveActionRightButton, 1, 1, 1, 1); + addButton->setIcon(icon); + gridLayout = new QGridLayout(this); + gridLayout->addWidget(addButton, 1, 1, 1, 1); spacerItem = new QSpacerItem(33, 57, QSizePolicy::Minimum, QSizePolicy::Expanding); gridLayout->addItem(spacerItem, 5, 1, 1, 1); spacerItem1 = new QSpacerItem(33, 58, QSizePolicy::Minimum, QSizePolicy::Expanding); gridLayout->addItem(spacerItem1, 0, 1, 1, 1); - moveActionLeftButton = new QPushButton(this); - moveActionLeftButton->setMinimumSize(QSize(30, 30)); + removeButton = new QPushButton(this); + removeButton->setMinimumSize(QSize(30, 30)); QIcon icon1; icon1.addFile(QString::fromUtf8(":/icons/button_left.xpm"), QSize(), QIcon::Normal, QIcon::Off); - moveActionLeftButton->setIcon(icon1); - moveActionLeftButton->setAutoDefault(true); - moveActionLeftButton->setDefault(false); + removeButton->setIcon(icon1); + removeButton->setAutoDefault(true); + removeButton->setDefault(false); - gridLayout->addWidget(moveActionLeftButton, 2, 1, 1, 1); + gridLayout->addWidget(removeButton, 2, 1, 1, 1); - moveActionDownButton = new QPushButton(this); - moveActionDownButton->setMinimumSize(QSize(30, 30)); - QIcon icon2; - icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); - moveActionDownButton->setIcon(icon2); - moveActionDownButton->setAutoDefault(true); - - gridLayout->addWidget(moveActionDownButton, 4, 1, 1, 1); - - moveActionUpButton = new QPushButton(this); - moveActionUpButton->setMinimumSize(QSize(30, 30)); + upButton = new QPushButton(this); + upButton->setMinimumSize(QSize(30, 30)); QIcon icon3; icon3.addFile(QString::fromUtf8(":/icons/button_up.xpm"), QSize(), QIcon::Normal, QIcon::Off); - moveActionUpButton->setIcon(icon3); + upButton->setIcon(icon3); - gridLayout->addWidget(moveActionUpButton, 3, 1, 1, 1); + gridLayout->addWidget(upButton, 3, 1, 1, 1); + + downButton = new QPushButton(this); + downButton->setMinimumSize(QSize(30, 30)); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); + downButton->setIcon(icon2); + downButton->setAutoDefault(true); + + gridLayout->addWidget(downButton, 4, 1, 1, 1); vboxLayout = new QVBoxLayout(); vboxLayout->setContentsMargins(0, 0, 0, 0); - label_2 = new QLabel(this); - vboxLayout->addWidget(label_2); + labelAvailable = new QLabel(this); + vboxLayout->addWidget(labelAvailable); - avalableTreeWidget = new QTreeWidget(this); - avalableTreeWidget->setRootIsDecorated(false); - avalableTreeWidget->setColumnCount(0); - vboxLayout->addWidget(avalableTreeWidget); + availableWidget = new QTreeWidget(this); + availableWidget->setRootIsDecorated(false); + availableWidget->setHeaderLabels(QStringList() << QString()); + availableWidget->header()->hide(); + vboxLayout->addWidget(availableWidget); gridLayout->addLayout(vboxLayout, 0, 0, 6, 1); vboxLayout1 = new QVBoxLayout(); vboxLayout1->setContentsMargins(0, 0, 0, 0); - label = new QLabel(this); - vboxLayout1->addWidget(label); + labelSelected = new QLabel(this); + vboxLayout1->addWidget(labelSelected); - selectedTreeWidget = new QTreeWidget(this); - vboxLayout1->addWidget(selectedTreeWidget); + selectedWidget = new QTreeWidget(this); + selectedWidget->setRootIsDecorated(false); + selectedWidget->setHeaderLabels(QStringList() << QString()); + selectedWidget->header()->hide(); + vboxLayout1->addWidget(selectedWidget); gridLayout->addLayout(vboxLayout1, 0, 2, 6, 1); - moveActionRightButton->setText(QString()); - moveActionLeftButton->setText(QString()); - moveActionDownButton->setText(QString()); - moveActionUpButton->setText(QString()); - label_2->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); - label->setText(QApplication::translate("Gui::ActionSelector", "Selected:", 0, QApplication::UnicodeUTF8)); + addButton->setText(QString()); + removeButton->setText(QString()); + upButton->setText(QString()); + downButton->setText(QString()); + + connect(addButton, SIGNAL(clicked()), + this, SLOT(on_addButton_clicked()) ); + connect(removeButton, SIGNAL(clicked()), + this, SLOT(on_removeButton_clicked()) ); + connect(upButton, SIGNAL(clicked()), + this, SLOT(on_upButton_clicked()) ); + connect(downButton, SIGNAL(clicked()), + this, SLOT(on_downButton_clicked()) ); + connect(availableWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)) ); + connect(selectedWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)) ); + connect(availableWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), + this, SLOT(onItemChanged(QTreeWidgetItem *,int)) ); + connect(selectedWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), + this, SLOT(onItemChanged(QTreeWidgetItem *,int)) ); + retranslateUi(); } ActionSelector::~ActionSelector() { } +void ActionSelector::retranslateUi() +{ + labelAvailable->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); + labelSelected->setText(QApplication::translate("Gui::ActionSelector", "Selected:", 0, QApplication::UnicodeUTF8)); + addButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Add", 0, QApplication::UnicodeUTF8)); + removeButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Remove", 0, QApplication::UnicodeUTF8)); + upButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move up", 0, QApplication::UnicodeUTF8)); + downButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move down", 0, QApplication::UnicodeUTF8)); +} + +void ActionSelector::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::LanguageChange) { + retranslateUi(); + } + QWidget::changeEvent(event); +} + +void ActionSelector::keyPressEvent(QKeyEvent* event) +{ + if ((event->modifiers() & Qt::ControlModifier)) { + switch (event->key()) + { + case Qt::Key_Right: + on_addButton_clicked(); + break; + case Qt::Key_Left: + on_removeButton_clicked(); + break; + case Qt::Key_Up: + on_upButton_clicked(); + break; + case Qt::Key_Down: + on_downButton_clicked(); + break; + default: + event->ignore(); + return; + } + } +} + +void ActionSelector::setButtonsEnabled() +{ + addButton->setEnabled(availableWidget->indexOfTopLevelItem(availableWidget->currentItem()) > -1); + removeButton->setEnabled(selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) > -1); + upButton->setEnabled(selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) > 0); + downButton->setEnabled(selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) > -1 && + selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) < selectedWidget->topLevelItemCount() - 1); +} + +void ActionSelector::onItemChanged(QTreeWidgetItem * /*item*/, int /*column*/) +{ + setButtonsEnabled(); +} + +void ActionSelector::onItemDoubleClicked(QTreeWidgetItem * item, int column) +{ + QTreeWidget* treeWidget = item->treeWidget(); + if (treeWidget == availableWidget) { + int index = availableWidget->indexOfTopLevelItem(item); + item = availableWidget->takeTopLevelItem(index); + availableWidget->setCurrentItem(0); + selectedWidget->addTopLevelItem(item); + selectedWidget->setCurrentItem(item); + } + else if (treeWidget == selectedWidget) { + int index = selectedWidget->indexOfTopLevelItem(item); + item = selectedWidget->takeTopLevelItem(index); + selectedWidget->setCurrentItem(0); + availableWidget->addTopLevelItem(item); + availableWidget->setCurrentItem(item); + } +} + +void ActionSelector::on_addButton_clicked() +{ + QTreeWidgetItem* item = availableWidget->currentItem(); + if (item) { + int index = availableWidget->indexOfTopLevelItem(item); + item = availableWidget->takeTopLevelItem(index); + availableWidget->setCurrentItem(0); + selectedWidget->addTopLevelItem(item); + selectedWidget->setCurrentItem(item); + } +} + +void ActionSelector::on_removeButton_clicked() +{ + QTreeWidgetItem* item = selectedWidget->currentItem(); + if (item) { + int index = selectedWidget->indexOfTopLevelItem(item); + item = selectedWidget->takeTopLevelItem(index); + selectedWidget->setCurrentItem(0); + availableWidget->addTopLevelItem(item); + availableWidget->setCurrentItem(item); + } +} + +void ActionSelector::on_upButton_clicked() +{ + QTreeWidgetItem* item = selectedWidget->currentItem(); + if (item && selectedWidget->isItemSelected(item)) { + int index = selectedWidget->indexOfTopLevelItem(item); + if (index > 0) { + selectedWidget->takeTopLevelItem(index); + selectedWidget->insertTopLevelItem(index-1, item); + selectedWidget->setCurrentItem(item); + } + } +} + +void ActionSelector::on_downButton_clicked() +{ + QTreeWidgetItem* item = selectedWidget->currentItem(); + if (item && selectedWidget->isItemSelected(item)) { + int index = selectedWidget->indexOfTopLevelItem(item); + if (index < selectedWidget->topLevelItemCount()-1) { + selectedWidget->takeTopLevelItem(index); + selectedWidget->insertTopLevelItem(index+1, item); + selectedWidget->setCurrentItem(item); + } + } +} // ------------------------------------------------------------------------------ diff --git a/src/Gui/Widgets.h b/src/Gui/Widgets.h index a13e407c6e..afdddba89f 100644 --- a/src/Gui/Widgets.h +++ b/src/Gui/Widgets.h @@ -63,26 +63,47 @@ Q_SIGNALS: // ------------------------------------------------------------------------------ -class ActionSelector : public QWidget +class GuiExport ActionSelector : public QWidget { + Q_OBJECT + public: ActionSelector(QWidget* parent=0); ~ActionSelector(); + QTreeWidget* availableTreeWidget() const + { return availableWidget; } + QTreeWidget* selectedTreeWidget() const + { return selectedWidget; } + +private: + void keyPressEvent(QKeyEvent *); + void changeEvent(QEvent*); + void retranslateUi(); + void setButtonsEnabled(); + +private Q_SLOTS: + void on_addButton_clicked(); + void on_removeButton_clicked(); + void on_upButton_clicked(); + void on_downButton_clicked(); + void onItemChanged(QTreeWidgetItem * item, int column); + void onItemDoubleClicked(QTreeWidgetItem * item, int column); + private: QGridLayout *gridLayout; - QPushButton *moveActionRightButton; + QVBoxLayout *vboxLayout; + QVBoxLayout *vboxLayout1; + QPushButton *addButton; + QPushButton *removeButton; + QPushButton *upButton; + QPushButton *downButton; + QLabel *labelAvailable; + QLabel *labelSelected; + QTreeWidget *availableWidget; + QTreeWidget *selectedWidget; QSpacerItem *spacerItem; QSpacerItem *spacerItem1; - QPushButton *moveActionLeftButton; - QPushButton *moveActionDownButton; - QPushButton *moveActionUpButton; - QVBoxLayout *vboxLayout; - QLabel *label_2; - QTreeWidget *avalableTreeWidget; - QVBoxLayout *vboxLayout1; - QLabel *label; - QTreeWidget *selectedTreeWidget; }; // ------------------------------------------------------------------------------ diff --git a/src/Tools/plugins/widget/FreeCAD_widgets.sln b/src/Tools/plugins/widget/FreeCAD_widgets.sln deleted file mode 100644 index 98489c937f..0000000000 --- a/src/Tools/plugins/widget/FreeCAD_widgets.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FreeCAD_widgets", "FreeCAD_widgets.vcproj", "{50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Debug|Win32.ActiveCfg = Debug|Win32 - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Debug|Win32.Build.0 = Debug|Win32 - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Release|Win32.ActiveCfg = Release|Win32 - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Tools/plugins/widget/customwidgets.cpp b/src/Tools/plugins/widget/customwidgets.cpp index 6a2280377e..9de8c0f823 100644 --- a/src/Tools/plugins/widget/customwidgets.cpp +++ b/src/Tools/plugins/widget/customwidgets.cpp @@ -330,6 +330,94 @@ void AccelLineEdit::keyPressEvent ( QKeyEvent * e) // ------------------------------------------------------------------------------ +ActionSelector::ActionSelector(QWidget* parent) + : QWidget(parent) +{ + addButton = new QPushButton(this); + addButton->setMinimumSize(QSize(30, 30)); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/button_right.xpm"), QSize(), QIcon::Normal, QIcon::Off); + addButton->setIcon(icon); + gridLayout = new QGridLayout(this); + gridLayout->addWidget(addButton, 1, 1, 1, 1); + + spacerItem = new QSpacerItem(33, 57, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem, 5, 1, 1, 1); + spacerItem1 = new QSpacerItem(33, 58, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem1, 0, 1, 1, 1); + + removeButton = new QPushButton(this); + removeButton->setMinimumSize(QSize(30, 30)); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/icons/button_left.xpm"), QSize(), QIcon::Normal, QIcon::Off); + removeButton->setIcon(icon1); + removeButton->setAutoDefault(true); + removeButton->setDefault(false); + + gridLayout->addWidget(removeButton, 2, 1, 1, 1); + + upButton = new QPushButton(this); + upButton->setMinimumSize(QSize(30, 30)); + QIcon icon3; + icon3.addFile(QString::fromUtf8(":/icons/button_up.xpm"), QSize(), QIcon::Normal, QIcon::Off); + upButton->setIcon(icon3); + + gridLayout->addWidget(upButton, 3, 1, 1, 1); + + downButton = new QPushButton(this); + downButton->setMinimumSize(QSize(30, 30)); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); + downButton->setIcon(icon2); + downButton->setAutoDefault(true); + + gridLayout->addWidget(downButton, 4, 1, 1, 1); + + vboxLayout = new QVBoxLayout(); + vboxLayout->setContentsMargins(0, 0, 0, 0); + labelAvailable = new QLabel(this); + vboxLayout->addWidget(labelAvailable); + + availableWidget = new QTreeWidget(this); + availableWidget->setRootIsDecorated(false); + availableWidget->setHeaderLabels(QStringList() << QString()); + availableWidget->header()->hide(); + vboxLayout->addWidget(availableWidget); + + gridLayout->addLayout(vboxLayout, 0, 0, 6, 1); + + vboxLayout1 = new QVBoxLayout(); + vboxLayout1->setContentsMargins(0, 0, 0, 0); + labelSelected = new QLabel(this); + vboxLayout1->addWidget(labelSelected); + + selectedWidget = new QTreeWidget(this); + selectedWidget->setRootIsDecorated(false); + selectedWidget->setHeaderLabels(QStringList() << QString()); + selectedWidget->header()->hide(); + vboxLayout1->addWidget(selectedWidget); + + gridLayout->addLayout(vboxLayout1, 0, 2, 6, 1); + + addButton->setText(QString()); + removeButton->setText(QString()); + upButton->setText(QString()); + downButton->setText(QString()); + + labelAvailable->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); + labelSelected->setText(QApplication::translate("Gui::ActionSelector", "Selected:", 0, QApplication::UnicodeUTF8)); + addButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Add", 0, QApplication::UnicodeUTF8)); + removeButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Remove", 0, QApplication::UnicodeUTF8)); + upButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move up", 0, QApplication::UnicodeUTF8)); + downButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move down", 0, QApplication::UnicodeUTF8)); +} + +ActionSelector::~ActionSelector() +{ +} + +// -------------------------------------------------------------------- + CommandIconView::CommandIconView ( QWidget * parent ) : QListWidget(parent) { diff --git a/src/Tools/plugins/widget/customwidgets.h b/src/Tools/plugins/widget/customwidgets.h index 2543a119ef..013f6ba4ad 100644 --- a/src/Tools/plugins/widget/customwidgets.h +++ b/src/Tools/plugins/widget/customwidgets.h @@ -37,6 +37,7 @@ #include #include #include +#include namespace Gui { @@ -176,6 +177,32 @@ protected: // ------------------------------------------------------------------------------ +class ActionSelector : public QWidget +{ + Q_OBJECT + +public: + ActionSelector(QWidget* parent=0); + ~ActionSelector(); + +private: + QGridLayout *gridLayout; + QVBoxLayout *vboxLayout; + QVBoxLayout *vboxLayout1; + QPushButton *addButton; + QPushButton *removeButton; + QPushButton *upButton; + QPushButton *downButton; + QLabel *labelAvailable; + QLabel *labelSelected; + QTreeWidget *availableWidget; + QTreeWidget *selectedWidget; + QSpacerItem *spacerItem; + QSpacerItem *spacerItem1; +}; + +// ------------------------------------------------------------------------------ + class CommandIconView : public QListWidget { Q_OBJECT diff --git a/src/Tools/plugins/widget/plugin.cpp b/src/Tools/plugins/widget/plugin.cpp index d0a2aca923..3769f37d48 100644 --- a/src/Tools/plugins/widget/plugin.cpp +++ b/src/Tools/plugins/widget/plugin.cpp @@ -340,6 +340,79 @@ public: } }; +/* XPM */ +static const char *actionselector_pixmap[]={ +"22 22 6 1", +"a c #000000", +"# c #000080", +"b c #008080", +"c c #808080", +"d c #c0c0c0", +". c #ffffff", +"......................", +"......................", +"......................", +"...#aaaaaaaaaaaaaa#...", +".baccccccccccccccccab.", +".acccddddddddddddddca.", +"#ccd................d#", +"acc.................da", +"acd.......d....ca.ac.a", +"acd......db......a...a", +"acd.dbbb.dbbbd...a...a", +"acd.ccdbddb.db...a...a", +"acd.dbbbddb..b...a...a", +"acd.bd.bddb..b...a...a", +"acd.bbbbddbbbc...a...a", +"acd..d.....dd..ca.acda", +"#cd.................d#", +".ac................da.", +".badd............dda#.", +"...#aaaaaaaaaaaaaa#...", +"......................", +"......................"}; + +class ActionSelectorPlugin : public QDesignerCustomWidgetInterface +{ + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + ActionSelectorPlugin() + { + } + QWidget *createWidget(QWidget *parent) + { + return new Gui::ActionSelector(parent); + } + QString group() const + { + return QLatin1String("Input Widgets"); + } + QIcon icon() const + { + return QIcon( QPixmap( actionselector_pixmap ) ); + } + QString includeFile() const + { + return QLatin1String("Gui/Widgets.h"); + } + QString toolTip() const + { + return QLatin1String("Action Selector"); + } + QString whatsThis() const + { + return QLatin1String("A widget to select actions."); + } + bool isContainer() const + { + return false; + } + QString name() const + { + return QLatin1String("Gui::ActionSelector"); + } +}; + /* XPM */ static const char *iconview_pixmap[]={ "22 22 10 1", @@ -1061,6 +1134,7 @@ QList CustomWidgetPlugin::customWidgets () con cw.append(new LocationWidgetPlugin); cw.append(new FileChooserPlugin); cw.append(new AccelLineEditPlugin); + cw.append(new ActionSelectorPlugin); cw.append(new CommandIconViewPlugin); cw.append(new UIntSpinBoxPlugin); cw.append(new ColorButtonPlugin); From 4e588567356f961c2ab8d77f47269f5b83f356fd Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 3 Jun 2012 14:22:05 +0200 Subject: [PATCH 42/69] Use ActionSelector in Loft panel and expose to Python by UiLoader --- src/Gui/Application.cpp | 4 + src/Gui/WidgetFactory.cpp | 74 +++++++++++++ src/Gui/WidgetFactory.h | 23 +++- src/Gui/Widgets.cpp | 38 ++++++- src/Gui/Widgets.h | 6 +- src/Gui/resource.cpp | 1 + src/Mod/Part/Gui/TaskLoft.cpp | 66 ++---------- src/Mod/Part/Gui/TaskLoft.h | 4 - src/Mod/Part/Gui/TaskLoft.ui | 192 +++++----------------------------- 9 files changed, 174 insertions(+), 234 deletions(-) diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index d62c36b4ef..a4c3542b39 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -341,6 +341,10 @@ Application::Application(bool GUIenabled) "workbenches."); Py::Module(module).setAttr(std::string("ActiveDocument"),Py::None()); + UiLoaderPy::init_type(); + Base::Interpreter().addType(UiLoaderPy::type_object(), + module,"UiLoader"); + //insert Selection module PyObject* pSelectionModule = Py_InitModule3("Selection", SelectionSingleton::Methods, "Selection module"); diff --git a/src/Gui/WidgetFactory.cpp b/src/Gui/WidgetFactory.cpp index c074fcd7e3..fd734154f2 100644 --- a/src/Gui/WidgetFactory.cpp +++ b/src/Gui/WidgetFactory.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" +#include #include #include #include @@ -194,6 +195,79 @@ QWidget* UiLoader::createWidget(const QString & className, QWidget * parent, // ---------------------------------------------------- +PyObject *UiLoaderPy::PyMake(struct _typeobject *type, PyObject * args, PyObject * kwds) +{ + if (!PyArg_ParseTuple(args, "")) + return 0; + return new UiLoaderPy(); +} + +void UiLoaderPy::init_type() +{ + behaviors().name("UiLoader"); + behaviors().doc("UiLoader to create widgets"); + behaviors().type_object()->tp_new = &PyMake; + // you must have overwritten the virtual functions + behaviors().supportRepr(); + behaviors().supportGetattr(); + behaviors().supportSetattr(); + add_varargs_method("createWidget",&UiLoaderPy::createWidget,"createWidget()"); +} + +UiLoaderPy::UiLoaderPy() +{ +} + +UiLoaderPy::~UiLoaderPy() +{ +} + +Py::Object UiLoaderPy::repr() +{ + std::string s; + std::ostringstream s_out; + s_out << "Ui loader"; + return Py::String(s_out.str()); +} + +Py::Object UiLoaderPy::createWidget(const Py::Tuple& args) +{ + Py::Module sipmod(PyImport_AddModule((char*)"sip")); + Py::Module qtmod(PyImport_ImportModule((char*)"PyQt4.Qt")); + + // 1st argument + std::string className = (std::string)Py::String(args[0]); + + // 2nd argument + QWidget* parent = 0; + if (args.size() > 1) { + Py::Callable func = sipmod.getDict().getItem("unwrapinstance"); + Py::Tuple arguments(1); + arguments[0] = args[1]; //PyQt pointer + Py::Object result = func.apply(arguments); + void* ptr = PyLong_AsVoidPtr(result.ptr()); + QObject* object = reinterpret_cast(ptr); + if (object) + parent = qobject_cast(object); + } + + // 3rd argument + std::string objectName; + if (args.size() > 2) { + objectName = (std::string)Py::String(args[2]); + } + + QWidget* widget = loader.createWidget(QString::fromAscii(className.c_str()), parent, + QString::fromAscii(objectName.c_str())); + Py::Callable func = sipmod.getDict().getItem("wrapinstance"); + Py::Tuple arguments(2); + arguments[0] = Py::asObject(PyLong_FromVoidPtr(widget)); + arguments[1] = qtmod.getDict().getItem("QWidget"); + return func.apply(arguments); +} + +// ---------------------------------------------------- + WidgetFactorySupplier* WidgetFactorySupplier::_pcSingleton = 0L; WidgetFactorySupplier & WidgetFactorySupplier::instance() diff --git a/src/Gui/WidgetFactory.h b/src/Gui/WidgetFactory.h index 8f20c88643..705e4c36b1 100644 --- a/src/Gui/WidgetFactory.h +++ b/src/Gui/WidgetFactory.h @@ -32,6 +32,7 @@ #include "DlgPreferencesImp.h" #include "DlgCustomizeImp.h" #include "PropertyPage.h" +#include namespace Gui { namespace Dialog{ @@ -85,13 +86,33 @@ public: * Fore more details see the documentation to QWidgetFactory. */ QWidget* createWidget(const QString & className, QWidget * parent=0, - const QString& name =QString()); + const QString& name = QString()); private: QStringList cw; }; // -------------------------------------------------------------------- +class UiLoaderPy : public Py::PythonExtension +{ +public: + static void init_type(void); // announce properties and methods + + UiLoaderPy(); + ~UiLoaderPy(); + + Py::Object repr(); + Py::Object createWidget(const Py::Tuple&); + +private: + static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *); + +private: + UiLoader loader; +}; + +// -------------------------------------------------------------------- + /** * The WidgetProducer class is a value-based template class that provides * the ability to create widgets dynamically. diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index 98a87442c5..d4e66e3bea 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -110,6 +110,7 @@ ActionSelector::ActionSelector(QWidget* parent) : QWidget(parent) { addButton = new QPushButton(this); + addButton->setObjectName(QLatin1String("addButton")); addButton->setMinimumSize(QSize(30, 30)); QIcon icon; icon.addFile(QString::fromUtf8(":/icons/button_right.xpm"), QSize(), QIcon::Normal, QIcon::Off); @@ -123,6 +124,7 @@ ActionSelector::ActionSelector(QWidget* parent) gridLayout->addItem(spacerItem1, 0, 1, 1, 1); removeButton = new QPushButton(this); + removeButton->setObjectName(QLatin1String("removeButton")); removeButton->setMinimumSize(QSize(30, 30)); QIcon icon1; icon1.addFile(QString::fromUtf8(":/icons/button_left.xpm"), QSize(), QIcon::Normal, QIcon::Off); @@ -133,6 +135,7 @@ ActionSelector::ActionSelector(QWidget* parent) gridLayout->addWidget(removeButton, 2, 1, 1, 1); upButton = new QPushButton(this); + upButton->setObjectName(QLatin1String("upButton")); upButton->setMinimumSize(QSize(30, 30)); QIcon icon3; icon3.addFile(QString::fromUtf8(":/icons/button_up.xpm"), QSize(), QIcon::Normal, QIcon::Off); @@ -141,6 +144,7 @@ ActionSelector::ActionSelector(QWidget* parent) gridLayout->addWidget(upButton, 3, 1, 1, 1); downButton = new QPushButton(this); + downButton->setObjectName(QLatin1String("downButton")); downButton->setMinimumSize(QSize(30, 30)); QIcon icon2; icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); @@ -155,6 +159,7 @@ ActionSelector::ActionSelector(QWidget* parent) vboxLayout->addWidget(labelAvailable); availableWidget = new QTreeWidget(this); + availableWidget->setObjectName(QLatin1String("availableTreeWidget")); availableWidget->setRootIsDecorated(false); availableWidget->setHeaderLabels(QStringList() << QString()); availableWidget->header()->hide(); @@ -168,6 +173,7 @@ ActionSelector::ActionSelector(QWidget* parent) vboxLayout1->addWidget(labelSelected); selectedWidget = new QTreeWidget(this); + selectedWidget->setObjectName(QLatin1String("selectedTreeWidget")); selectedWidget->setRootIsDecorated(false); selectedWidget->setHeaderLabels(QStringList() << QString()); selectedWidget->header()->hide(); @@ -192,17 +198,39 @@ ActionSelector::ActionSelector(QWidget* parent) this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)) ); connect(selectedWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)) ); - connect(availableWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), - this, SLOT(onItemChanged(QTreeWidgetItem *,int)) ); - connect(selectedWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), - this, SLOT(onItemChanged(QTreeWidgetItem *,int)) ); + connect(availableWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem *)), + this, SLOT(onCurrentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)) ); + connect(selectedWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem *)), + this, SLOT(onCurrentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)) ); retranslateUi(); + setButtonsEnabled(); } ActionSelector::~ActionSelector() { } +void ActionSelector::setSelectedLabel(const QString& label) +{ + labelSelected->setText(label); +} + +QString ActionSelector::selectedLabel() const +{ + return labelSelected->text(); +} + +void ActionSelector::setAvailableLabel(const QString& label) +{ + labelAvailable->setText(label); +} + +QString ActionSelector::availableLabel() const +{ + return labelAvailable->text(); +} + + void ActionSelector::retranslateUi() { labelAvailable->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); @@ -254,7 +282,7 @@ void ActionSelector::setButtonsEnabled() selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) < selectedWidget->topLevelItemCount() - 1); } -void ActionSelector::onItemChanged(QTreeWidgetItem * /*item*/, int /*column*/) +void ActionSelector::onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*) { setButtonsEnabled(); } diff --git a/src/Gui/Widgets.h b/src/Gui/Widgets.h index afdddba89f..70366a67f6 100644 --- a/src/Gui/Widgets.h +++ b/src/Gui/Widgets.h @@ -75,6 +75,10 @@ public: { return availableWidget; } QTreeWidget* selectedTreeWidget() const { return selectedWidget; } + void setSelectedLabel(const QString&); + QString selectedLabel() const; + void setAvailableLabel(const QString&); + QString availableLabel() const; private: void keyPressEvent(QKeyEvent *); @@ -87,7 +91,7 @@ private Q_SLOTS: void on_removeButton_clicked(); void on_upButton_clicked(); void on_downButton_clicked(); - void onItemChanged(QTreeWidgetItem * item, int column); + void onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); void onItemDoubleClicked(QTreeWidgetItem * item, int column); private: diff --git a/src/Gui/resource.cpp b/src/Gui/resource.cpp index 3143f1502a..b7f3ce8fbd 100644 --- a/src/Gui/resource.cpp +++ b/src/Gui/resource.cpp @@ -91,6 +91,7 @@ WidgetFactorySupplier::WidgetFactorySupplier() new WidgetProducer; new WidgetProducer; new WidgetProducer; + new WidgetProducer; new WidgetProducer; new WidgetProducer; new WidgetProducer; diff --git a/src/Mod/Part/Gui/TaskLoft.cpp b/src/Mod/Part/Gui/TaskLoft.cpp index b6d58885df..96c3417a27 100644 --- a/src/Mod/Part/Gui/TaskLoft.cpp +++ b/src/Mod/Part/Gui/TaskLoft.cpp @@ -68,10 +68,14 @@ LoftWidget::LoftWidget(QWidget* parent) Gui::Application::Instance->runPythonCode("import Part"); d->ui.setupUi(this); - connect(d->ui.treeWidgetWire, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + d->ui.selector->setAvailableLabel(tr("Vertex/Wire")); + d->ui.selector->setSelectedLabel(tr("Loft")); + + connect(d->ui.selector->availableTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); - connect(d->ui.treeWidgetLoft, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + connect(d->ui.selector->selectedTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); + findShapes(); } @@ -105,7 +109,7 @@ void LoftWidget::findShapes() child->setData(0, Qt::UserRole, name); Gui::ViewProvider* vp = activeGui->getViewProvider(*it); if (vp) child->setIcon(0, vp->getIcon()); - d->ui.treeWidgetWire->addTopLevelItem(child); + d->ui.selector->availableTreeWidget()->addTopLevelItem(child); } } } @@ -125,13 +129,13 @@ bool LoftWidget::accept() QTextStream str(&list); - int count = d->ui.treeWidgetLoft->topLevelItemCount(); + int count = d->ui.selector->selectedTreeWidget()->topLevelItemCount(); if (count < 2) { QMessageBox::critical(this, tr("Too few elements"), tr("At least two vertices, edges or wires are required.")); return false; } for (int i=0; iui.treeWidgetLoft->topLevelItem(i); + QTreeWidgetItem* child = d->ui.selector->selectedTreeWidget()->topLevelItem(i); QString name = child->data(0, Qt::UserRole).toString(); str << "App.getDocument('" << d->document.c_str() << "')." << name << ", "; } @@ -177,61 +181,13 @@ void LoftWidget::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* } } -void LoftWidget::on_addButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetWire->currentItem(); - if (item) { - int index = d->ui.treeWidgetWire->indexOfTopLevelItem(item); - item = d->ui.treeWidgetWire->takeTopLevelItem(index); - d->ui.treeWidgetWire->setCurrentItem(0); - d->ui.treeWidgetLoft->addTopLevelItem(item); - d->ui.treeWidgetLoft->setCurrentItem(item); - } -} - -void LoftWidget::on_removeButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetLoft->currentItem(); - if (item) { - int index = d->ui.treeWidgetLoft->indexOfTopLevelItem(item); - item = d->ui.treeWidgetLoft->takeTopLevelItem(index); - d->ui.treeWidgetLoft->setCurrentItem(0); - d->ui.treeWidgetWire->addTopLevelItem(item); - d->ui.treeWidgetWire->setCurrentItem(item); - } -} - -void LoftWidget::on_upButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetLoft->currentItem(); - if (item && d->ui.treeWidgetLoft->isItemSelected(item)) { - int index = d->ui.treeWidgetLoft->indexOfTopLevelItem(item); - if (index > 0) { - d->ui.treeWidgetLoft->takeTopLevelItem(index); - d->ui.treeWidgetLoft->insertTopLevelItem(index-1, item); - d->ui.treeWidgetLoft->setCurrentItem(item); - } - } -} - -void LoftWidget::on_downButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetLoft->currentItem(); - if (item && d->ui.treeWidgetLoft->isItemSelected(item)) { - int index = d->ui.treeWidgetLoft->indexOfTopLevelItem(item); - if (index < d->ui.treeWidgetLoft->topLevelItemCount()-1) { - d->ui.treeWidgetLoft->takeTopLevelItem(index); - d->ui.treeWidgetLoft->insertTopLevelItem(index+1, item); - d->ui.treeWidgetLoft->setCurrentItem(item); - } - } -} - void LoftWidget::changeEvent(QEvent *e) { QWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) { d->ui.retranslateUi(this); + d->ui.selector->setAvailableLabel(tr("Vertex/Wire")); + d->ui.selector->setSelectedLabel(tr("Loft")); } } diff --git a/src/Mod/Part/Gui/TaskLoft.h b/src/Mod/Part/Gui/TaskLoft.h index 82ac877a51..50d30771d1 100644 --- a/src/Mod/Part/Gui/TaskLoft.h +++ b/src/Mod/Part/Gui/TaskLoft.h @@ -43,10 +43,6 @@ public: bool reject(); private Q_SLOTS: - void on_addButton_clicked(); - void on_removeButton_clicked(); - void on_upButton_clicked(); - void on_downButton_clicked(); void onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); private: diff --git a/src/Mod/Part/Gui/TaskLoft.ui b/src/Mod/Part/Gui/TaskLoft.ui index 1dd0c6159f..dd461047d9 100644 --- a/src/Mod/Part/Gui/TaskLoft.ui +++ b/src/Mod/Part/Gui/TaskLoft.ui @@ -6,7 +6,7 @@ 0 0 - 324 + 336 326 @@ -14,172 +14,8 @@ Loft - - - - - Vertex/Wire - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 33 - 58 - - - - - - - - true - - - - 30 - 30 - - - - Move right - - - <b>Move the selected item one level down.</b><p>This will also change the level of the parent item.</p> - - - - - - - :/icons/button_right.xpm:/icons/button_right.xpm - - - - - - - true - - - - 30 - 30 - - - - Move left - - - <b>Move the selected item one level up.</b><p>This will also change the level of the parent item.</p> - - - - - - - :/icons/button_left.xpm:/icons/button_left.xpm - - - true - - - false - - - - - - - true - - - - 30 - 30 - - - - Move up - - - <b>Move the selected item up.</b><p>The item will be moved within the hierarchy level.</p> - - - - - - - :/icons/button_up.xpm:/icons/button_up.xpm - - - - - - - true - - - - 30 - 30 - - - - Move down - - - <b>Move the selected item down.</b><p>The item will be moved within the hierarchy level.</p> - - - - - - - :/icons/button_down.xpm:/icons/button_down.xpm - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 33 - 57 - - - - - - - - - - - Loft - - - + + @@ -188,15 +24,35 @@ - + Ruled surface + + + + Qt::Horizontal + + + + 130 + 20 + + + + + + + Gui::ActionSelector + QWidget +
Gui/Widgets.h
+
+
From 08bfa1465fbb3e1e99248b5a5d524d14c69eb4ca Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 3 Jun 2012 16:36:00 +0200 Subject: [PATCH 43/69] 0000706: copy- paste- crash --- src/App/PropertyLinks.cpp | 14 ++++++++++---- src/Gui/MergeDocuments.cpp | 13 +++++-------- src/Gui/Tree.cpp | 11 ++++++++--- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index 6cc1058083..9ffb1d0336 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -130,12 +130,18 @@ void PropertyLink::Restore(Base::XMLReader &reader) assert(getContainer()->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId()) ); if (name != "") { - DocumentObject *pcObject = static_cast(getContainer())-> - getDocument()->getObject(name.c_str()); - if (!pcObject) + DocumentObject* parent = static_cast(getContainer()); + DocumentObject* object = parent->getDocument()->getObject(name.c_str()); + if (!object) { Base::Console().Warning("Lost link to '%s' while loading, maybe " "an object was not loaded correctly\n",name.c_str()); - setValue(pcObject); + } + else if (parent == object) { + Base::Console().Warning("Object '%s' links to itself, nullify it\n",name.c_str()); + object = 0; + } + + setValue(object); } else { setValue(0); diff --git a/src/Gui/MergeDocuments.cpp b/src/Gui/MergeDocuments.cpp index 7addd29bed..52713fa474 100644 --- a/src/Gui/MergeDocuments.cpp +++ b/src/Gui/MergeDocuments.cpp @@ -122,16 +122,13 @@ MergeDocuments::importObjects(std::istream& input) reader.readElement("Object"); std::string type = reader.getAttribute("type"); std::string name = reader.getAttribute("name"); - std::string docn = name; - - // remove number from end to avoid lengthy names - size_t lastpos = docn.length()-1; - while (docn[lastpos] >= 48 && docn[lastpos] <= 57) - lastpos--; - docn = docn.substr(0, lastpos+1); try { - App::DocumentObject* o = appdoc->addObject(type.c_str(),docn.c_str()); + // Use name from XML as is and do NOT remove trailing digits because + // otherwise we may cause a dependency to itself + // Example: Object 'Cut001' references object 'Cut' and removing the + // digits we make an object 'Cut' referencing itself. + App::DocumentObject* o = appdoc->addObject(type.c_str(),name.c_str()); objs.push_back(o); // use this name for the later access because an object with // the given name may already exist diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index ba3672d8cd..c453662a59 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -830,9 +830,14 @@ void DocumentItem::slotChangeObject(const Gui::ViewProviderDocumentObject& view) children.insert(kt->second); QTreeWidgetItem* parent = kt->second->parent(); if (parent && parent != it->second) { - int index = parent->indexOfChild(kt->second); - parent->takeChild(index); - it->second->addChild(kt->second); + if (it->second != kt->second) { + int index = parent->indexOfChild(kt->second); + parent->takeChild(index); + it->second->addChild(kt->second); + } + else { + Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Object references to itself.\n"); + } } } else { From 58736b75ca5e0aa5c954ebf32d0cb787a39a5d81 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 4 Jun 2012 01:31:26 +0200 Subject: [PATCH 44/69] Tmp. switch off gcc linker settings --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d2eaf6849..6461a3e103 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ if(CMAKE_COMPILER_IS_GNUCXX) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # get linker errors as soon as possible and not at runtime e.g. for modules if(UNIX) - SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") + # SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") endif(UNIX) endif(CMAKE_COMPILER_IS_GNUCXX) From e277bca4780f2561c540f800fa44e009af07d471 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sun, 3 Jun 2012 23:44:20 -0300 Subject: [PATCH 45/69] Arch: Several bugfixes in arch objects --- src/Mod/Arch/ArchComponent.py | 4 +- src/Mod/Arch/ArchStructure.py | 20 +++++----- src/Mod/Arch/ArchWall.py | 33 ++++++++------- src/Mod/Arch/ArchWindow.py | 75 ++++++++++++++++++----------------- 4 files changed, 69 insertions(+), 63 deletions(-) diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index c9fa5a347e..13ad37204e 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -257,9 +257,9 @@ class Component: obj.addProperty("App::PropertyLink","Base","Base", "The base object this component is built upon") obj.addProperty("App::PropertyLinkList","Additions","Base", - "Other shapes that are appended to this wall") + "Other shapes that are appended to this object") obj.addProperty("App::PropertyLinkList","Subtractions","Base", - "Other shapes that are subtracted from this wall") + "Other shapes that are subtracted from this object") obj.Proxy = self self.Type = "Component" self.Subvolume = None diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index 7bf73cc1e0..d257c4d92f 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -163,17 +163,13 @@ class _Structure(ArchComponent.Component): base = Part.Face(base) base = base.extrude(normal) for app in obj.Additions: - base = base.oldFuse(app.Shape) - app.ViewObject.hide() # to be removed + if hasattr(app,"Shape"): + if not app.Shape.isNull(): + base = base.fuse(app.Shape) + app.ViewObject.hide() # to be removed for hole in obj.Subtractions: - cut = False - if hasattr(hole,"Proxy"): - if hasattr(hole.Proxy,"Subvolume"): - if hole.Proxy.Subvolume: - base = base.cut(hole.Proxy.Subvolume) - cut = True - if not cut: - if hasattr(obj,"Shape"): + if hasattr(hole,"Shape"): + if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if base: @@ -186,7 +182,9 @@ class _Structure(ArchComponent.Component): fsh.append(sh) obj.Shape = Part.makeCompound(fsh) else: - obj.Shape = base + if not base.isNull(): + base = base.removeSplitter() + obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl class _ViewProviderStructure(ArchComponent.ViewProviderComponent): diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 4b9db7ee03..0bfa8f6e48 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -272,20 +272,23 @@ class _Wall(ArchComponent.Component): "returns a subvolume from a base object" import Part max_length = 0 + f = None for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w - f = Part.Face(f) - n = f.normalAt(0,0) - v1 = DraftVecUtils.scaleTo(n,width) - f.translate(v1) - v2 = DraftVecUtils.neg(v1) - v2 = DraftVecUtils.scale(v1,-2) - f = f.extrude(v2) - if delta: - f.translate(delta) - return f + if f: + f = Part.Face(f) + n = f.normalAt(0,0) + v1 = DraftVecUtils.scaleTo(n,width) + f.translate(v1) + v2 = DraftVecUtils.neg(v1) + v2 = DraftVecUtils.scale(v1,-2) + f = f.extrude(v2) + if delta: + f.translate(delta) + return f + return None def createGeometry(self,obj): "builds the wall shape" @@ -368,27 +371,29 @@ class _Wall(ArchComponent.Component): else: temp = sh base = temp - base = base.removeSplitter() for app in obj.Additions: - base = base.oldFuse(app.Shape) + base = base.fuse(app.Shape) app.ViewObject.hide() #to be removed for hole in obj.Subtractions: if Draft.getType(hole) == "Window": # window if hole.Base and obj.Width: f = self.getSubVolume(hole.Base,width) - base = base.cut(f) + if f: + base = base.cut(f) elif Draft.isClone(hole,"Window"): if hole.Objects[0].Base and width: f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement.Base) - base = base.cut(f) + if f: + base = base.cut(f) elif hasattr(hole,"Shape"): if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if base: + base.removeSplitter() obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index de7df1b6e1..2c20b03ca2 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -117,6 +117,7 @@ class _Window(ArchComponent.Component): obj.addProperty("App::PropertyStringList","WindowParts","Base", str(translate("Arch","the components of this window"))) self.Type = "Window" + obj.Proxy = self def execute(self,obj): self.createGeometry(obj) @@ -130,43 +131,45 @@ class _Window(ArchComponent.Component): pl = obj.Placement if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): - if obj.WindowParts and (len(obj.WindowParts)%5 == 0): - shapes = [] - for i in range(len(obj.WindowParts)/5): - wires = [] - wstr = obj.WindowParts[(i*5)+2].split(',') - for s in wstr: - j = int(s[4:]) - if obj.Base.Shape.Wires: - if len(obj.Base.Shape.Wires) >= j: - wires.append(obj.Base.Shape.Wires[j]) - if wires: - max_length = 0 - for w in wires: - if w.BoundBox.DiagonalLength > max_length: - max_length = w.BoundBox.DiagonalLength - ext = w - wires.remove(ext) - shape = Part.Face(ext) - norm = shape.normalAt(0,0) - thk = float(obj.WindowParts[(i*5)+3]) - if thk: - exv = DraftVecUtils.scaleTo(norm,thk) - shape = shape.extrude(exv) + if hasattr(obj,"WindowParts"): + if obj.WindowParts and (len(obj.WindowParts)%5 == 0): + shapes = [] + for i in range(len(obj.WindowParts)/5): + wires = [] + wstr = obj.WindowParts[(i*5)+2].split(',') + for s in wstr: + j = int(s[4:]) + if obj.Base.Shape.Wires: + if len(obj.Base.Shape.Wires) >= j: + wires.append(obj.Base.Shape.Wires[j]) + if wires: + max_length = 0 for w in wires: - f = Part.Face(w) - f = f.extrude(exv) - shape = shape.cut(f) - if obj.WindowParts[(i*5)+4]: - zof = float(obj.WindowParts[(i*5)+4]) - if zof: - zov = DraftVecUtils.scaleTo(norm,zof) - shape.translate(zov) - print shape - shapes.append(shape) - obj.Shape = Part.makeCompound(shapes) - if not DraftGeomUtils.isNull(pl): - obj.Placement = pl + if w.BoundBox.DiagonalLength > max_length: + max_length = w.BoundBox.DiagonalLength + ext = w + wires.remove(ext) + shape = Part.Face(ext) + norm = shape.normalAt(0,0) + thk = float(obj.WindowParts[(i*5)+3]) + if thk: + exv = DraftVecUtils.scaleTo(norm,thk) + shape = shape.extrude(exv) + for w in wires: + f = Part.Face(w) + f = f.extrude(exv) + shape = shape.cut(f) + if obj.WindowParts[(i*5)+4]: + zof = float(obj.WindowParts[(i*5)+4]) + if zof: + zov = DraftVecUtils.scaleTo(norm,zof) + shape.translate(zov) + print shape + shapes.append(shape) + if shapes: + obj.Shape = Part.makeCompound(shapes) + if not DraftGeomUtils.isNull(pl): + obj.Placement = pl class _ViewProviderWindow(ArchComponent.ViewProviderComponent): "A View Provider for the Window object" From 33fb5211378cf760c074c2ee0fbc83f45e19ffd6 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 4 Jun 2012 11:43:20 +0200 Subject: [PATCH 46/69] 0000727: Keep faces colors on boolean operations --- src/Mod/Part/App/FeaturePartBoolean.cpp | 16 ++- src/Mod/Part/App/FeaturePartCommon.cpp | 30 +++-- src/Mod/Part/App/FeaturePartFuse.cpp | 30 +++-- src/Mod/Part/App/modelRefine.cpp | 147 ++++++++++++++++++++++++ src/Mod/Part/App/modelRefine.h | 31 ++++- src/Mod/Part/Gui/DlgSettingsGeneral.cpp | 2 + src/Mod/Part/Gui/DlgSettingsGeneral.ui | 110 ++++++++++-------- 7 files changed, 302 insertions(+), 64 deletions(-) diff --git a/src/Mod/Part/App/FeaturePartBoolean.cpp b/src/Mod/Part/App/FeaturePartBoolean.cpp index 1a2608797b..2a1781b6cf 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.cpp +++ b/src/Mod/Part/App/FeaturePartBoolean.cpp @@ -28,6 +28,9 @@ #endif #include "FeaturePartBoolean.h" +#include "modelRefine.h" +#include +#include using namespace Part; @@ -75,7 +78,7 @@ App::DocumentObjectExecReturn *Boolean::execute(void) if (!mkBool->IsDone()) { return new App::DocumentObjectExecReturn("Boolean operation failed"); } - const TopoDS_Shape& resShape = mkBool->Shape(); + TopoDS_Shape resShape = mkBool->Shape(); if (resShape.IsNull()) { return new App::DocumentObjectExecReturn("Resulting shape is invalid"); } @@ -84,6 +87,17 @@ App::DocumentObjectExecReturn *Boolean::execute(void) history.push_back(buildHistory(*mkBool.get(), TopAbs_FACE, resShape, BaseShape)); history.push_back(buildHistory(*mkBool.get(), TopAbs_FACE, resShape, ToolShape)); + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); + if (hGrp->GetBool("RefineModel", false)) { + TopoDS_Shape oldShape = resShape; + BRepBuilderAPI_RefineModel mkRefine(oldShape); + resShape = mkRefine.Shape(); + ShapeHistory hist = buildHistory(mkRefine, TopAbs_FACE, resShape, oldShape); + history[0] = joinHistory(history[0], hist); + history[1] = joinHistory(history[1], hist); + } + this->Shape.setValue(resShape); this->History.setValues(history); return App::DocumentObject::StdReturn; diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index 44c93a01c3..01ef09e417 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -30,7 +30,9 @@ #include "FeaturePartCommon.h" - +#include "modelRefine.h" +#include +#include #include using namespace Part; @@ -84,17 +86,17 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) if (s.size() >= 2) { try { std::vector history; - TopoDS_Shape res = s.front(); + TopoDS_Shape resShape = s.front(); for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Common mkCommon(res, *it); + BRepAlgoAPI_Common mkCommon(resShape, *it); // Let's check if the fusion has been successful if (!mkCommon.IsDone()) throw Base::Exception("Intersection failed"); - res = mkCommon.Shape(); + resShape = mkCommon.Shape(); - ShapeHistory hist1 = buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape1()); - ShapeHistory hist2 = buildHistory(mkCommon, TopAbs_FACE, res, mkCommon.Shape2()); + ShapeHistory hist1 = buildHistory(mkCommon, TopAbs_FACE, resShape, mkCommon.Shape1()); + ShapeHistory hist2 = buildHistory(mkCommon, TopAbs_FACE, resShape, mkCommon.Shape2()); if (history.empty()) { history.push_back(hist1); history.push_back(hist2); @@ -105,9 +107,21 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) history.push_back(hist2); } } - if (res.IsNull()) + if (resShape.IsNull()) throw Base::Exception("Resulting shape is invalid"); - this->Shape.setValue(res); + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); + if (hGrp->GetBool("RefineModel", false)) { + TopoDS_Shape oldShape = resShape; + BRepBuilderAPI_RefineModel mkRefine(oldShape); + resShape = mkRefine.Shape(); + ShapeHistory hist = buildHistory(mkRefine, TopAbs_FACE, resShape, oldShape); + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist); + } + + this->Shape.setValue(resShape); this->History.setValues(history); } catch (Standard_Failure) { diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index 47efb7a445..ba73877ea5 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -29,7 +29,9 @@ #include "FeaturePartFuse.h" - +#include "modelRefine.h" +#include +#include #include using namespace Part; @@ -83,17 +85,17 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) if (s.size() >= 2) { try { std::vector history; - TopoDS_Shape res = s.front(); + TopoDS_Shape resShape = s.front(); for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(res, *it); + BRepAlgoAPI_Fuse mkFuse(resShape, *it); // Let's check if the fusion has been successful if (!mkFuse.IsDone()) throw Base::Exception("Fusion failed"); - res = mkFuse.Shape(); + resShape = mkFuse.Shape(); - ShapeHistory hist1 = buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape1()); - ShapeHistory hist2 = buildHistory(mkFuse, TopAbs_FACE, res, mkFuse.Shape2()); + ShapeHistory hist1 = buildHistory(mkFuse, TopAbs_FACE, resShape, mkFuse.Shape1()); + ShapeHistory hist2 = buildHistory(mkFuse, TopAbs_FACE, resShape, mkFuse.Shape2()); if (history.empty()) { history.push_back(hist1); history.push_back(hist2); @@ -104,9 +106,21 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) history.push_back(hist2); } } - if (res.IsNull()) + if (resShape.IsNull()) throw Base::Exception("Resulting shape is invalid"); - this->Shape.setValue(res); + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); + if (hGrp->GetBool("RefineModel", false)) { + TopoDS_Shape oldShape = resShape; + BRepBuilderAPI_RefineModel mkRefine(oldShape); + resShape = mkRefine.Shape(); + ShapeHistory hist = buildHistory(mkRefine, TopAbs_FACE, resShape, oldShape); + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist); + } + + this->Shape.setValue(resShape); this->History.setValues(history); } catch (Standard_Failure) { diff --git a/src/Mod/Part/App/modelRefine.cpp b/src/Mod/Part/App/modelRefine.cpp index 1a68b8ffea..516cf0551b 100644 --- a/src/Mod/Part/App/modelRefine.cpp +++ b/src/Mod/Part/App/modelRefine.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -547,6 +548,8 @@ bool FaceUniter::process() { if (workShell.IsNull()) return false; + modifiedShapes.clear(); + deletedShapes.clear(); typeObjects.push_back(&getPlaneObject()); typeObjects.push_back(&getCylinderObject()); //add more face types. @@ -583,6 +586,12 @@ bool FaceUniter::process() facesToRemove.reserve(facesToRemove.size() + adjacencySplitter.getGroup(adjacentIndex).size()); FaceVectorType temp = adjacencySplitter.getGroup(adjacentIndex); facesToRemove.insert(facesToRemove.end(), temp.begin(), temp.end()); + // the first shape will be marked as modified, i.e. replaced by newFace, all others are marked as deleted + if (!temp.empty()) + { + modifiedShapes.push_back(std::make_pair(temp.front(), newFace)); + deletedShapes.insert(deletedShapes.end(), temp.begin()+1, temp.end()); + } } } } @@ -632,3 +641,141 @@ bool FaceUniter::process() } return true; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +//TODO: Implement a way to log all modifications + +Part::BRepBuilderAPI_RefineModel::BRepBuilderAPI_RefineModel(const TopoDS_Shape& shape) +{ + myShape = shape; + Build(); +} + +void Part::BRepBuilderAPI_RefineModel::Build() +{ + if (myShape.IsNull()) + Standard_Failure::Raise("Cannot remove splitter from empty shape"); + + if (myShape.ShapeType() == TopAbs_SOLID) { + const TopoDS_Solid &solid = TopoDS::Solid(myShape); + BRepTools_ReShape reshape; + TopExp_Explorer it; + for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) { + const TopoDS_Shell ¤tShell = TopoDS::Shell(it.Current()); + ModelRefine::FaceUniter uniter(currentShell); + if (uniter.process()) { + if (uniter.isModified()) { + const TopoDS_Shell &newShell = uniter.getShell(); + reshape.Replace(currentShell, newShell); + LogModifications(uniter); + } + } + else { + Standard_Failure::Raise("Removing splitter failed"); + } + } + myShape = reshape.Apply(solid); + } + else if (myShape.ShapeType() == TopAbs_SHELL) { + const TopoDS_Shell& shell = TopoDS::Shell(myShape); + ModelRefine::FaceUniter uniter(shell); + if (uniter.process()) { + myShape = uniter.getShell(); + LogModifications(uniter); + } + else { + Standard_Failure::Raise("Removing splitter failed"); + } + } + else if (myShape.ShapeType() == TopAbs_COMPOUND) { + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + + TopExp_Explorer xp; + // solids + for (xp.Init(myShape, TopAbs_SOLID); xp.More(); xp.Next()) { + const TopoDS_Solid &solid = TopoDS::Solid(xp.Current()); + BRepTools_ReShape reshape; + TopExp_Explorer it; + for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) { + const TopoDS_Shell ¤tShell = TopoDS::Shell(it.Current()); + ModelRefine::FaceUniter uniter(currentShell); + if (uniter.process()) { + if (uniter.isModified()) { + const TopoDS_Shell &newShell = uniter.getShell(); + reshape.Replace(currentShell, newShell); + LogModifications(uniter); + } + } + } + builder.Add(comp, reshape.Apply(solid)); + } + // free shells + for (xp.Init(myShape, TopAbs_SHELL, TopAbs_SOLID); xp.More(); xp.Next()) { + const TopoDS_Shell& shell = TopoDS::Shell(xp.Current()); + ModelRefine::FaceUniter uniter(shell); + if (uniter.process()) { + builder.Add(comp, uniter.getShell()); + LogModifications(uniter); + } + } + // the rest + for (xp.Init(myShape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + for (xp.Init(myShape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + for (xp.Init(myShape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + for (xp.Init(myShape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + + myShape = comp; + } + + Done(); +} + +void Part::BRepBuilderAPI_RefineModel::LogModifications(const ModelRefine::FaceUniter& uniter) +{ + const std::vector& modShapes = uniter.getModifiedShapes(); + for (std::vector::const_iterator it = modShapes.begin(); it != modShapes.end(); ++it) { + TopTools_ListOfShape list; + list.Append(it->second); + myModified.Bind(it->first, list); + } + const ShapeVectorType& delShapes = uniter.getDeletedShapes(); + for (ShapeVectorType::const_iterator it = delShapes.begin(); it != delShapes.end(); ++it) { + myDeleted.Append(*it); + } +} + +const TopTools_ListOfShape& Part::BRepBuilderAPI_RefineModel::Modified(const TopoDS_Shape& S) +{ + if (myModified.IsBound(S)) + return myModified.Find(S); + else + return myEmptyList; +} + +Standard_Boolean Part::BRepBuilderAPI_RefineModel::IsDeleted(const TopoDS_Shape& S) +{ + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(myDeleted); it.More(); it.Next()) + { + if (it.Value().IsSame(S)) + return Standard_True; + } + + return Standard_False; +} + diff --git a/src/Mod/Part/App/modelRefine.h b/src/Mod/Part/App/modelRefine.h index ed1267f984..32c897ff70 100644 --- a/src/Mod/Part/App/modelRefine.h +++ b/src/Mod/Part/App/modelRefine.h @@ -36,12 +36,15 @@ #include #include #include +#include namespace ModelRefine { - typedef std::vector FaceVectorType; - typedef std::vector EdgeVectorType; + typedef std::vector FaceVectorType; + typedef std::vector EdgeVectorType; + typedef std::vector ShapeVectorType; + typedef std::pair ShapePairType; void getFaceEdges(const TopoDS_Face &face, EdgeVectorType &edges); void boundaryEdges(const FaceVectorType &faces, EdgeVectorType &edgesOut); @@ -147,10 +150,16 @@ namespace ModelRefine bool process(); const TopoDS_Shell& getShell() const {return workShell;} bool isModified(){return modifiedSignal;} + const std::vector& getModifiedShapes() const + {return modifiedShapes;} + const ShapeVectorType& getDeletedShapes() const + {return deletedShapes;} private: TopoDS_Shell workShell; std::vector typeObjects; + std::vector modifiedShapes; + ShapeVectorType deletedShapes; bool modifiedSignal; }; } @@ -170,5 +179,23 @@ GeomAbs_OffsetSurface, GeomAbs_OtherSurface }; */ +namespace Part { +class BRepBuilderAPI_RefineModel : public BRepBuilderAPI_MakeShape +{ +public: + BRepBuilderAPI_RefineModel(const TopoDS_Shape&); + void Build(); + const TopTools_ListOfShape& Modified(const TopoDS_Shape& S); + Standard_Boolean IsDeleted(const TopoDS_Shape& S); + +private: + void LogModifications(const ModelRefine::FaceUniter& uniter); + +private: + TopTools_DataMapOfShapeListOfShape myModified; + TopTools_ListOfShape myEmptyList; + TopTools_ListOfShape myDeleted; +}; +} #endif // MODELREFINE_H diff --git a/src/Mod/Part/Gui/DlgSettingsGeneral.cpp b/src/Mod/Part/Gui/DlgSettingsGeneral.cpp index 61bce31f2f..a9c917a86e 100644 --- a/src/Mod/Part/Gui/DlgSettingsGeneral.cpp +++ b/src/Mod/Part/Gui/DlgSettingsGeneral.cpp @@ -69,6 +69,7 @@ void DlgSettingsGeneral::saveSettings() Interface_Static::SetCVal("write.step.unit","MM"); break; } + ui->checkBooleanRefine->onSave(); } void DlgSettingsGeneral::loadSettings() @@ -77,6 +78,7 @@ void DlgSettingsGeneral::loadSettings() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part"); int unit = hGrp->GetInt("Unit", 0); ui->comboBoxUnits->setCurrentIndex(unit); + ui->checkBooleanRefine->onRestore(); } /** diff --git a/src/Mod/Part/Gui/DlgSettingsGeneral.ui b/src/Mod/Part/Gui/DlgSettingsGeneral.ui index 4bca9519f0..4ea3f5222d 100644 --- a/src/Mod/Part/Gui/DlgSettingsGeneral.ui +++ b/src/Mod/Part/Gui/DlgSettingsGeneral.ui @@ -1,10 +1,8 @@ - - - - + + PartGui::DlgSettingsGeneral - - + + 0 0 @@ -12,73 +10,54 @@ 333 - + General - - - 9 - - - 6 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - + + + + Export - - + + 9 - + 6 - - + + - + Millimeter - + Meter - + Inch - - - + + + Units for export of STEP/IGES - + - + Qt::Horizontal - + 40 20 @@ -89,9 +68,50 @@ + + + + Boolean operation + + + + + + Automatically refine model after boolean operation + + + RefineModel + + + Mod/Part/Boolean + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + - + + + Gui::PrefCheckBox + QCheckBox +
Gui/PrefWidgets.h
+
+
From fc46edbedc864191880b868d12debdd93c5b2856 Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 4 Jun 2012 22:58:37 +0200 Subject: [PATCH 47/69] 0000727: Keep faces colors on boolean operations --- src/Mod/Part/App/modelRefine.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/modelRefine.cpp b/src/Mod/Part/App/modelRefine.cpp index 516cf0551b..25bb9378ac 100644 --- a/src/Mod/Part/App/modelRefine.cpp +++ b/src/Mod/Part/App/modelRefine.cpp @@ -617,6 +617,15 @@ bool FaceUniter::process() sew.Add(*sewIt); sew.Perform(); workShell = TopoDS::Shell(sew.SewedShape()); + // update the list of modifications + for (std::vector::iterator it = modifiedShapes.begin(); it != modifiedShapes.end(); ++it) + { + if (sew.IsModified(it->second)) + { + it->second = sew.Modified(it->second); + break; + } + } } else { @@ -638,13 +647,25 @@ bool FaceUniter::process() faceFixer.Perform(); } workShell = TopoDS::Shell(edgeFuse.Shape()); + // update the list of modifications + TopTools_DataMapOfShapeShape faceMap; + edgeFuse.Faces(faceMap); + for (std::vector::iterator it = modifiedShapes.begin(); it != modifiedShapes.end(); ++it) + { + if (faceMap.IsBound(it->second)) + { + const TopoDS_Shape& value = faceMap.Find(it->second); + if (!value.IsSame(it->second)) + it->second = value; + } + } } return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////// -//TODO: Implement a way to log all modifications +//BRepBuilderAPI_RefineModel implement a way to log all modifications on the faces Part::BRepBuilderAPI_RefineModel::BRepBuilderAPI_RefineModel(const TopoDS_Shape& shape) { From f312e9585d46d404d52124750481a2cc0c4e189a Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 4 Jun 2012 18:08:10 -0300 Subject: [PATCH 48/69] Arch: more bugfixing --- src/Mod/Arch/ArchBuilding.py | 44 +++++++--------------------------- src/Mod/Arch/ArchFloor.py | 22 +++++++++++++---- src/Mod/Arch/ArchSite.py | 46 ++++++++---------------------------- src/Mod/Arch/ArchWall.py | 16 +++++++++---- 4 files changed, 46 insertions(+), 82 deletions(-) diff --git a/src/Mod/Arch/ArchBuilding.py b/src/Mod/Arch/ArchBuilding.py index 4b0011b34d..0d6d5cc403 100644 --- a/src/Mod/Arch/ArchBuilding.py +++ b/src/Mod/Arch/ArchBuilding.py @@ -21,7 +21,7 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Draft,ArchCommands +import FreeCAD,FreeCADGui,Draft,ArchCommands,ArchFloor from PyQt4 import QtCore from DraftTools import translate @@ -56,7 +56,7 @@ class _CommandBuilding: FreeCADGui.doCommand("import Arch") FreeCADGui.doCommand("obj = Arch.makeBuilding()") FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") - FreeCADGui.doCommand("FreeCAD.ActiveDocument.removeObject("+sel[0].Name+")") + FreeCADGui.doCommand('FreeCAD.ActiveDocument.removeObject("'+sel[0].Name+'")') FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: @@ -73,47 +73,19 @@ class _CommandBuilding: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() -class _Building: +class _Building(ArchFloor._Floor): "The Building object" def __init__(self,obj): + ArchFloor._Floor.__init__(self,obj) self.Type = "Building" - obj.Proxy = self - self.Object = obj - - def execute(self,obj): - self.Object = obj - - def onChanged(self,obj,prop): - pass - - def addObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if not child in g: - g.append(child) - self.Object.Group = g - - def removeObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if child in g: - g.remove(child) - self.Object.Group = g - -class _ViewProviderBuilding: + obj.setEditorMode('Height',2) + +class _ViewProviderBuilding(ArchFloor._ViewProviderFloor): "A View Provider for the Building object" def __init__(self,vobj): - vobj.Proxy = self + ArchFloor._ViewProviderFloor.__init__(self,vobj) def getIcon(self): return ":/icons/Arch_Building_Tree.svg" - def attach(self,vobj): - self.Object = vobj.Object - return - - def claimChildren(self): - return self.Object.Group - - FreeCADGui.addCommand('Arch_Building',_CommandBuilding()) diff --git a/src/Mod/Arch/ArchFloor.py b/src/Mod/Arch/ArchFloor.py index 922d8aca0c..f7ccab17ae 100644 --- a/src/Mod/Arch/ArchFloor.py +++ b/src/Mod/Arch/ArchFloor.py @@ -57,7 +57,7 @@ class _CommandFloor: FreeCADGui.doCommand("import Arch") FreeCADGui.doCommand("obj = Arch.makeFloor()") FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") - FreeCADGui.doCommand("FreeCAD.ActiveDocument.removeObject("+sel[0].Name+")") + FreeCADGui.doCommand('FreeCAD.ActiveDocument.removeObject("'+sel[0].Name+'")') FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: @@ -74,7 +74,7 @@ class _CommandFloor: FreeCAD.ActiveDocument.recompute() class _Floor: - "The Cell object" + "The Floor object" def __init__(self,obj): obj.addProperty("App::PropertyLength","Height","Base", str(translate("Arch","The height of this floor"))) @@ -82,11 +82,17 @@ class _Floor: obj.Proxy = self self.Object = obj + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + def execute(self,obj): - self.Object = obj + pass def onChanged(self,obj,prop): - pass + self.Object = obj def addObject(self,child): if hasattr(self,"Object"): @@ -103,7 +109,7 @@ class _Floor: self.Object.Group = g class _ViewProviderFloor: - "A View Provider for the Cell object" + "A View Provider for the Floor object" def __init__(self,vobj): vobj.Proxy = self @@ -117,4 +123,10 @@ class _ViewProviderFloor: def claimChildren(self): return self.Object.Group + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + FreeCADGui.addCommand('Arch_Floor',_CommandFloor()) diff --git a/src/Mod/Arch/ArchSite.py b/src/Mod/Arch/ArchSite.py index d05a7e8ffa..69cfda8bab 100644 --- a/src/Mod/Arch/ArchSite.py +++ b/src/Mod/Arch/ArchSite.py @@ -21,7 +21,7 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Draft,ArchCommands +import FreeCAD,FreeCADGui,Draft,ArchCommands,ArchFloor from PyQt4 import QtCore from DraftTools import translate @@ -56,7 +56,7 @@ class _CommandSite: FreeCADGui.doCommand("import Arch") FreeCADGui.doCommand("obj = Arch.makeSite()") FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") - FreeCADGui.doCommand("FreeCAD.ActiveDocument.removeObject("+sel[0].Name+")") + FreeCADGui.doCommand('FreeCAD.ActiveDocument.removeObject("'+sel[0].Name+'")') nobj = makeSite() ArchCommands.copyProperties(sel[0],nobj) @@ -75,47 +75,21 @@ class _CommandSite: FreeCADGui.doCommand("Arch.makeSite("+ss+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() - -class _Site: + +class _Site(ArchFloor._Floor): "The Site object" def __init__(self,obj): + ArchFloor._Floor.__init__(self,obj) self.Type = "Site" - obj.Proxy = self - self.Object = obj - - def execute(self,obj): - self.Object = obj - - def onChanged(self,obj,prop): - pass - - def addObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if not child in g: - g.append(child) - self.Object.Group = g - - def removeObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if child in g: - g.remove(child) - self.Object.Group = g - -class _ViewProviderSite: + obj.setEditorMode('Height',2) + +class _ViewProviderSite(ArchFloor._ViewProviderFloor): "A View Provider for the Site object" def __init__(self,vobj): - vobj.Proxy = self + ArchFloor._ViewProviderFloor.__init__(self,vobj) def getIcon(self): return ":/icons/Arch_Site_Tree.svg" - def attach(self,vobj): - self.Object = vobj.Object - return - - def claimChildren(self): - return self.Object.Group - + FreeCADGui.addCommand('Arch_Site',_CommandSite()) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 0bfa8f6e48..19b3b29bad 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -265,6 +265,7 @@ class _Wall(ArchComponent.Component): self.createGeometry(obj) def onChanged(self,obj,prop): + print prop if prop in ["Base","Height","Width","Align","Additions","Subtractions"]: self.createGeometry(obj) @@ -373,7 +374,8 @@ class _Wall(ArchComponent.Component): base = temp for app in obj.Additions: - base = base.fuse(app.Shape) + if hasattr(app,"Shape"): + base = base.fuse(app.Shape) app.ViewObject.hide() #to be removed for hole in obj.Subtractions: if Draft.getType(hole) == "Window": @@ -393,10 +395,14 @@ class _Wall(ArchComponent.Component): hole.ViewObject.hide() # to be removed if base: - base.removeSplitter() - obj.Shape = base - if not DraftGeomUtils.isNull(pl): - obj.Placement = pl + if not base.isNull(): + try: + base.removeSplitter() + except: + print "Wall: Error removing splitter" + obj.Shape = base + if not DraftGeomUtils.isNull(pl): + obj.Placement = pl class _ViewProviderWall(ArchComponent.ViewProviderComponent): "A View Provider for the Wall object" From c85f66c16ba355616397b591ab76d4bb4297a07a Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 4 Jun 2012 23:06:12 -0300 Subject: [PATCH 49/69] Arch: Fixed bug in wall --- src/Mod/Arch/ArchWall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 19b3b29bad..a6e1cc1f73 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -397,7 +397,7 @@ class _Wall(ArchComponent.Component): if base: if not base.isNull(): try: - base.removeSplitter() + base = base.removeSplitter() except: print "Wall: Error removing splitter" obj.Shape = base From 07ab39cd16856e79a6e89786cff95e2e00dcb4d3 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 5 Jun 2012 00:00:43 -0300 Subject: [PATCH 50/69] Arch: Small improvements to section plane --- src/Mod/Arch/ArchSectionPlane.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index 8f695d7082..ea9fac7dcf 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -41,6 +41,7 @@ def makeSectionPlane(objectslist=None): elif o.isDerivedFrom("App::DocumentObjectGroup"): g.append(o) obj.Objects = g + return obj def makeSectionView(section): """makeSectionView(section) : Creates a Drawing view of the given Section Plane @@ -55,15 +56,14 @@ def makeSectionView(section): template = Draft.getParam("template") if not template: template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg' - page.ViewObject.HintOffsetX = 200 - page.ViewObject.HintOffsetY = 100 - page.ViewObject.HintScale = 20 page.Template = template view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View") page.addObject(view) _ArchDrawingView(view) view.Source = section + view.Label = str(translate("Arch","View of"))+" "+section.Name + return view class _CommandSectionPlane: "the Arch SectionPlane command definition" From 53b0f9e9525839b4a9a249f43e1e50083237925a Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 5 Jun 2012 11:55:45 +0200 Subject: [PATCH 51/69] Method Part.Edge.split added --- src/Mod/Part/App/TopoShapeEdgePy.xml | 12 ++- src/Mod/Part/App/TopoShapeEdgePyImp.cpp | 103 ++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/TopoShapeEdgePy.xml b/src/Mod/Part/App/TopoShapeEdgePy.xml index 873b3fb726..40def10305 100644 --- a/src/Mod/Part/App/TopoShapeEdgePy.xml +++ b/src/Mod/Part/App/TopoShapeEdgePy.xml @@ -24,7 +24,12 @@ Vector = valueAt(pos) - Get the point at the given parameter [0|Length] if defined - + + + Float = parameterAt(Vertex) - Get the parameter at the given vertex if lying on the edge + + + Vector = normalAt(pos) - Get the normal vector at the given parameter [0|Length] if defined @@ -64,6 +69,11 @@ Discretizes the edge using a given deflection or number of points and returns a list of points + + + Splits the edge at the given parameter values and builds a wire out of it + + Set or get the tolerance of the vertex diff --git a/src/Mod/Part/App/TopoShapeEdgePyImp.cpp b/src/Mod/Part/App/TopoShapeEdgePyImp.cpp index ec5bc09054..329c6bd6c6 100644 --- a/src/Mod/Part/App/TopoShapeEdgePyImp.cpp +++ b/src/Mod/Part/App/TopoShapeEdgePyImp.cpp @@ -23,10 +23,13 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include # include +# include +# include # include # include # include @@ -47,6 +50,7 @@ # include # include # include +# include # include #endif @@ -55,12 +59,14 @@ #include #include +#include #include #include #include "TopoShape.h" #include "TopoShapeFacePy.h" #include "TopoShapeVertexPy.h" +#include "TopoShapeWirePy.h" #include "TopoShapeEdgePy.h" #include "TopoShapeEdgePy.cpp" @@ -185,6 +191,35 @@ PyObject* TopoShapeEdgePy::valueAt(PyObject *args) return new Base::VectorPy(new Base::Vector3d(V.X(),V.Y(),V.Z())); } +PyObject* TopoShapeEdgePy::parameterAt(PyObject *args) +{ + PyObject* pnt; + PyObject* face=0; + if (!PyArg_ParseTuple(args, "O!|O!",&TopoShapeVertexPy::Type,&pnt, + &TopoShapeFacePy::Type,&face)) + return 0; + + try { + const TopoDS_Shape& v = static_cast(pnt)->getTopoShapePtr()->_Shape; + const TopoDS_Edge& e = TopoDS::Edge(getTopoShapePtr()->_Shape); + + if (face) { + const TopoDS_Shape& f = static_cast(face)->getTopoShapePtr()->_Shape; + Standard_Real par = BRep_Tool::Parameter(TopoDS::Vertex(v), e, TopoDS::Face(f)); + return PyFloat_FromDouble(par); + } + else { + Standard_Real par = BRep_Tool::Parameter(TopoDS::Vertex(v), e); + return PyFloat_FromDouble(par); + } + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + PyErr_SetString(PyExc_Exception, e->GetMessageString()); + return 0; + } +} + PyObject* TopoShapeEdgePy::tangentAt(PyObject *args) { double u; @@ -443,6 +478,74 @@ PyObject* TopoShapeEdgePy::discretize(PyObject *args) return 0; } +PyObject* TopoShapeEdgePy::split(PyObject *args) +{ + PyObject* float_or_list; + if (!PyArg_ParseTuple(args, "O", &float_or_list)) + return 0; + + try { + BRepAdaptor_Curve adapt(TopoDS::Edge(getTopoShapePtr()->_Shape)); + Standard_Real f = adapt.FirstParameter(); + Standard_Real l = adapt.LastParameter(); + + std::vector par; + par.push_back(f); + if (PyFloat_Check(float_or_list)) { + double val = PyFloat_AsDouble(float_or_list); + if (val == f || val == l) { + PyErr_SetString(PyExc_ValueError, "Cannot split edge at start or end point"); + return 0; + } + else if (val < f || val > l) { + PyErr_SetString(PyExc_ValueError, "Value out of parameter range"); + return 0; + } + par.push_back(val); + } + else if (PyList_Check(float_or_list)) { + Py::List list(float_or_list); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + double val = (double)Py::Float(*it); + if (val == f || val == l) { + PyErr_SetString(PyExc_ValueError, "Cannot split edge at start or end point"); + return 0; + } + else if (val < f || val > l) { + PyErr_SetString(PyExc_ValueError, "Value out of parameter range"); + return 0; + } + par.push_back(val); + } + } + else { + PyErr_SetString(PyExc_TypeError, "Either float or list of floats expected"); + return 0; + } + + par.push_back(l); + std::sort(par.begin(), par.end()); + + BRepBuilderAPI_MakeWire mkWire; + Handle_Geom_Curve c = adapt.Curve().Curve(); + std::vector::iterator end = par.end() - 1; + for (std::vector::iterator it = par.begin(); it != end; ++it) { + BRepBuilderAPI_MakeEdge mkBuilder(c, it[0], it[1]); + mkWire.Add(mkBuilder.Edge()); + } + + return new TopoShapeWirePy(new TopoShape(mkWire.Shape())); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + PyErr_SetString(PyExc_Exception, e->GetMessageString()); + return 0; + } + + PyErr_SetString(PyExc_Exception, "Geometry is not a curve"); + return 0; +} + PyObject* TopoShapeEdgePy::setTolerance(PyObject *args) { double tol; From 2a677be827de6129c464a60a6d66867ce0b80e3c Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 5 Jun 2012 13:42:21 +0200 Subject: [PATCH 52/69] 0000734: Cannot export Drawing Page to SVG --- src/Gui/ApplicationPy.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 9a2a51af4e..6f0bfd5ba2 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -362,10 +362,18 @@ PyObject* Application::sExport(PyObject * /*self*/, PyObject *args,PyObject * /* if (ext == QLatin1String("iv") || ext == QLatin1String("wrl") || ext == QLatin1String("vrml") || ext == QLatin1String("wrz") || ext == QLatin1String("svg") || ext == QLatin1String("idtf")) { - QString cmd = QString::fromLatin1( - "Gui.getDocument(\"%1\").ActiveView.dump(\"%2\")" - ).arg(QLatin1String(doc->getName())).arg(fi.absoluteFilePath()); - Base::Interpreter().runString(cmd.toUtf8()); + Gui::Document* gui_doc = Application::Instance->getDocument(doc); + std::list view3d = gui_doc->getMDIViewsOfType(View3DInventor::getClassTypeId()); + if (view3d.empty()) { + PyErr_SetString(PyExc_Exception, "Cannot export to SVG because document doesn't have a 3d view"); + return 0; + } + else { + QString cmd = QString::fromLatin1( + "Gui.getDocument(\"%1\").mdiViewsOfType('Gui::View3DInventor')[0].dump(\"%2\")" + ).arg(QLatin1String(doc->getName())).arg(fi.absoluteFilePath()); + Base::Interpreter().runString(cmd.toUtf8()); + } } else if (ext == QLatin1String("pdf")) { Gui::Document* gui_doc = Application::Instance->getDocument(doc); From 6c1195574934bd0dda5d16b05efd7814d206d3bd Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 5 Jun 2012 13:55:56 +0200 Subject: [PATCH 53/69] Raise exception in Drawing module for unsupported objects --- src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp b/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp index 07aa2c4717..a4cb65ef90 100644 --- a/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp +++ b/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp @@ -139,6 +139,10 @@ exporter(PyObject *self, PyObject *args) str_out.close(); break; } + else { + PyErr_SetString(PyExc_TypeError, "Export as SVG of this object type is not supported by Drawing module"); + return 0; + } } } } PY_CATCH; From 8a4a232f9b93748bddd4c6c868928f847dfaed5d Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 5 Jun 2012 12:37:17 -0300 Subject: [PATCH 54/69] Arch: Friendlier pycollada warning --- src/Mod/Arch/InitGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 4c5384ff08..6267d9077e 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -105,7 +105,7 @@ FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ") try: import collada except: - FreeCAD.Console.PrintError("pycollada not found, no collada support.\n") + FreeCAD.Console.PrintMessage(str(DraftTools.translate("arch","pycollada not found, collada support will be disabled.\n"))) else: FreeCAD.addImportType("Collada (*.dae)","importDAE") FreeCAD.addExportType("Collada (*.dae)","importDAE") From 463e3551e2c618fca216172a56869e896b31affe Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 5 Jun 2012 13:31:34 -0300 Subject: [PATCH 55/69] Arch: More bugfixes --- src/Mod/Arch/ArchComponent.py | 15 +++++++++++---- src/Mod/Arch/ArchSectionPlane.py | 1 + src/Mod/Arch/ArchVRM.py | 4 ++-- src/Mod/Arch/ArchWall.py | 15 ++++++++++++--- src/Mod/Arch/ArchWindow.py | 2 ++ 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index 13ad37204e..5e0206fe93 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -308,16 +308,23 @@ class ViewProviderComponent: return False class ArchSelectionObserver: - def __init__(self,origin,watched): + def __init__(self,origin,watched,hide=True,nextCommand=None): self.origin = origin self.watched = watched + self.hide = hide + self.nextCommand = nextCommand def addSelection(self,document, object, element, position): if object == self.watched.Name: if not element: print "closing Sketch edit" - self.origin.ViewObject.Transparency = 0 - self.origin.ViewObject.Selectable = True - self.watched.ViewObject.hide() + if self.hide: + self.origin.ViewObject.Transparency = 0 + self.origin.ViewObject.Selectable = True + self.watched.ViewObject.hide() FreeCADGui.activateWorkbench("ArchWorkbench") FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver) + if self.nextCommand: + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection(self.watched) + FreeCADGui.runCommand(self.nextCommand) del FreeCAD.ArchObserver diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index ea9fac7dcf..aa1d8bb990 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -234,6 +234,7 @@ class _ArchDrawingView: svgf = Drawing.projectToSVG(base,DraftVecUtils.neg(direction)) if svgf: svgf = svgf.replace('stroke-width="0.35"','stroke-width="' + str(linewidth) + 'px"') + svgf = svgf.replace('stroke-width:0.01','stroke-width:' + str(linewidth) + 'px') svg += svgf result = '' diff --git a/src/Mod/Arch/ArchVRM.py b/src/Mod/Arch/ArchVRM.py index 2de8ad0b3b..2eefd8f279 100644 --- a/src/Mod/Arch/ArchVRM.py +++ b/src/Mod/Arch/ArchVRM.py @@ -574,7 +574,7 @@ class Renderer: svg += '" ' svg += 'stroke="#000000" ' svg += 'stroke-width="' + str(linewidth) + '" ' - svg += 'style="stroke-width:0.01;' + svg += 'style="stroke-width:' + str(linewidth) + ';' svg += 'stroke-miterlimit:1;' svg += 'stroke-linejoin:round;' svg += 'stroke-dasharray:none;' @@ -598,7 +598,7 @@ class Renderer: svg += '" ' svg += 'stroke="#000000" ' svg += 'stroke-width="' + str(linewidth) + '" ' - svg += 'style="stroke-width:0.01;' + svg += 'style="stroke-width:' + str(linewidth) + ';' svg += 'stroke-miterlimit:1;' svg += 'stroke-linejoin:round;' svg += 'stroke-dasharray:none;' diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index a6e1cc1f73..25fb891eee 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -256,7 +256,10 @@ class _Wall(ArchComponent.Component): str(translate("Arch","The alignment of this wall on its base object, if applicable"))) obj.addProperty("App::PropertyVector","Normal","Base", str(translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))) + obj.addProperty("App::PropertyBool","ForceWire","Base", + str(translate("Arch","If True, if this wall is based on a face, it will use its border wire as trace, and disconsider the face."))) obj.Align = ['Left','Right','Center'] + obj.ForceWire = False self.Type = "Wall" obj.Width = 0.1 obj.Height = 0 @@ -265,7 +268,6 @@ class _Wall(ArchComponent.Component): self.createGeometry(obj) def onChanged(self,obj,prop): - print prop if prop in ["Base","Height","Width","Align","Additions","Subtractions"]: self.createGeometry(obj) @@ -359,7 +361,7 @@ class _Wall(ArchComponent.Component): base = obj.Base.Shape.copy() if base.Solids: pass - elif base.Faces: + elif base.Faces and (not obj.ForceWire): if height: norm = normal.multiply(height) base = base.extrude(norm) @@ -372,6 +374,13 @@ class _Wall(ArchComponent.Component): else: temp = sh base = temp + elif base.Edges: + wire = Part.Wire(base.Edges) + sh = getbase(wire) + if sh: + base = sh + else: + FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object"))) for app in obj.Additions: if hasattr(app,"Shape"): @@ -399,7 +408,7 @@ class _Wall(ArchComponent.Component): try: base = base.removeSplitter() except: - print "Wall: Error removing splitter" + FreeCAD.Console.PrintError(str(translate("Arch","Error removing splitter from wall shape"))) obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 2c20b03ca2..2f593257fb 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -92,6 +92,8 @@ class _CommandWindow: if Draft.getType(sel[0]) == "Wall": FreeCADGui.activateWorkbench("SketcherWorkbench") FreeCADGui.runCommand("Sketcher_NewSketch") + FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(sel[0],FreeCAD.ActiveDocument.Objects[-1],hide=False,nextCommand="Arch_Window") + FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) else: FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Window"))) FreeCADGui.doCommand("import Arch") From 99b02e44cb4a3a17390896e1fdb9ca40f98a2d88 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 5 Jun 2012 16:33:01 -0300 Subject: [PATCH 56/69] 0000722: Deleting Draft dimensions --- src/Mod/Draft/Draft.py | 36 +++++++++++++++++++++++++----------- src/Mod/Draft/DraftTools.py | 2 +- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 7b1c5dc8f3..49ff4ad04b 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -1782,7 +1782,6 @@ class _ViewProviderDimension: obj.Proxy = self obj.FontSize=getParam("textheight") obj.FontName=getParam("textfont") - obj.DisplayMode = ["2D","3D"] obj.ExtLines=0.3 obj.Override = '' @@ -1825,18 +1824,19 @@ class _ViewProviderDimension: if hasattr(obj.ViewObject,"DisplayMode"): if obj.ViewObject.DisplayMode == "3D": offset = DraftVecUtils.neg(offset) - if hasattr(obj.ViewObject,"TextPosition"): - if obj.ViewObject.TextPosition == Vector(0,0,0): - tbase = midpoint.add(offset) + if hasattr(obj.ViewObject,"TextPosition"): + if obj.ViewObject.TextPosition == Vector(0,0,0): + tbase = midpoint.add(offset) + else: + tbase = obj.ViewObject.TextPosition else: - tbase = obj.ViewObject.TextPosition + tbase = midpoint.add(offset) else: - tbase = midpoint.add(offset) + tbase = midpoint rot = FreeCAD.Placement(DraftVecUtils.getPlaneRotation(u,v,norm)).Rotation.Q return p1,p2,p3,p4,tbase,norm,rot def attach(self, obj): - obj.DisplayMode = ["2D","3D"] self.Object = obj.Object p1,p2,p3,p4,tbase,norm,rot = self.calcGeom(obj.Object) self.color = coin.SoBaseColor() @@ -1903,6 +1903,10 @@ class _ViewProviderDimension: self.onChanged(obj,"FontName") def updateData(self, obj, prop): + try: + dm = obj.ViewObject.DisplayMode + except: + dm = "2D" text = None if obj.Base and obj.LinkedVertices: if "Shape" in obj.Base.PropertiesList: @@ -1936,7 +1940,7 @@ class _ViewProviderDimension: self.text.string = self.text3d.string = text self.textpos.rotation = coin.SbRotation(rot[0],rot[1],rot[2],rot[3]) self.textpos.translation.setValue([tbase.x,tbase.y,tbase.z]) - if obj.ViewObject.DisplayMode == "2D": + if dm == "2D": self.coords.point.setValues([[p1.x,p1.y,p1.z], [p2.x,p2.y,p2.z], [p3.x,p3.y,p3.z], @@ -1958,6 +1962,7 @@ class _ViewProviderDimension: self.coord2.point.setValue((p3.x,p3.y,p3.z)) def onChanged(self, vp, prop): + self.Object = vp.Object if prop == "FontSize": self.font.size = vp.FontSize self.font3d.size = vp.FontSize*100 @@ -1969,13 +1974,20 @@ class _ViewProviderDimension: elif prop == "LineWidth": self.drawstyle.lineWidth = vp.LineWidth else: + self.drawstyle.lineWidth = vp.LineWidth self.updateData(vp.Object, None) def getDisplayModes(self,obj): return ["2D","3D"] def getDefaultDisplayMode(self): - return "2D" + if hasattr(self,"defaultmode"): + return self.defaultmode + else: + return "2D" + + def setDisplayMode(self,mode): + return mode def getIcon(self): if self.Object.Base: @@ -2034,10 +2046,12 @@ class _ViewProviderDimension: """ def __getstate__(self): - return None + return self.Object.ViewObject.DisplayMode def __setstate__(self,state): - return None + if state: + self.defaultmode = state + self.setDisplayMode(state) class _AngularDimension: "The AngularDimension object" diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 559e75e268..a5f52576c9 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -1653,7 +1653,7 @@ class Dimension(Creator): if self.dir: point = self.node[0].add(DraftVecUtils.project(point.sub(self.node[0]),self.dir)) self.node.append(point) - print "node",self.node + #print "node",self.node self.dimtrack.update(self.node) if (len(self.node) == 2): self.point2 = self.node[1] From 4f59b00aac03b36b48efc344c878c217c50e5d78 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 5 Jun 2012 18:53:48 -0300 Subject: [PATCH 57/69] Draft: Fixes in working plane --- src/Mod/Draft/DraftTools.py | 11 +++++++++++ src/Mod/Draft/DraftTrackers.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index a5f52576c9..06cffa4720 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -231,6 +231,17 @@ class SelectPlane: self.call = None self.doc = FreeCAD.ActiveDocument if self.doc: + sel = FreeCADGui.Selection.getSelectionEx() + if len(sel) == 1: + sel = sel[0] + if sel.HasSubObjects: + if len(sel.SubElementNames) == 1: + if "Face" in sel.SubElementNames[0]: + self.ui = FreeCADGui.draftToolBar + plane.alignToFace(sel.SubObjects[0], self.offset) + self.display(plane.axis) + self.finish() + return FreeCAD.activeDraftCommand = self self.view = Draft.get3DView() self.ui = FreeCADGui.draftToolBar diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index 5223ebf667..ab556e095c 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -617,7 +617,9 @@ class gridTracker(Tracker): def set(self): Q = FreeCAD.DraftWorkingPlane.getRotation().Rotation.Q + P = FreeCAD.DraftWorkingPlane.position self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]]) + self.trans.translation.setValue([P.x,P.y,P.z]) self.on() def getClosestNode(self,point): From 5cc67f3de9cf0bde2ecea3e6d7497eb1ce42f22c Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 5 Jun 2012 20:11:47 -0300 Subject: [PATCH 58/69] Draft: Fixed concrete hatch pattern --- src/Mod/Draft/Draft_rc.py | 365 +++++++++++------- src/Mod/Draft/Resources/patterns/concrete.svg | 112 +----- 2 files changed, 234 insertions(+), 243 deletions(-) diff --git a/src/Mod/Draft/Draft_rc.py b/src/Mod/Draft/Draft_rc.py index e1f587ddd4..e74a2fc93b 100644 --- a/src/Mod/Draft/Draft_rc.py +++ b/src/Mod/Draft/Draft_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Tue May 22 16:46:43 2012 +# Created: Tue Jun 5 20:09:57 2012 # by: The Resource Compiler for PyQt (Qt v4.8.1) # # WARNING! All changes made in this file will be lost! @@ -117,79 +117,150 @@ qt_resource_data = "\ \x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\ \x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ \ -\x00\x00\x04\x69\ -\x00\ -\x00\x15\x07\x78\x9c\xbd\x58\xdb\x6e\x1b\x37\x10\x7d\xef\x57\x2c\ -\x36\x2f\x2d\xb0\xa1\x78\xbf\xb8\x52\x7e\xa1\x0f\x45\x3e\x40\xb0\ -\xd7\xb2\x10\x47\x0e\x24\xb5\x49\xfa\xf5\x1d\xde\x96\xb4\xe1\x59\ -\xcf\x4b\x2c\x01\xc2\x90\x7b\x20\x9e\x1d\xce\x99\x19\x72\x7b\xf9\ -\xf7\xf0\xe9\xb7\x61\xd8\xde\xcd\xf7\x97\x68\x80\xf9\x6d\x7f\xbd\ -\xce\xe7\x53\x1a\xc0\xe7\x78\xb7\x1b\x6f\x9f\x4e\xb7\xe7\xf9\x3a\ -\x8f\x75\xb2\x60\x3e\x9f\x8e\xd7\xcb\x6e\xfc\xe7\x32\x9f\xff\xfe\ -\xb6\xbf\x9d\xff\x3a\x7d\xbe\x34\xd0\x8f\xdd\xc8\x97\xc1\xcf\x7e\ -\xf0\xfd\x78\x77\x7d\xd8\x8d\xcc\x2c\x33\x0f\xf3\xf1\xf0\x70\x4d\ -\x53\x9f\xca\xdc\xf6\x50\x1f\x0e\xc3\xe5\xfa\xf3\x71\xde\x8d\xf7\ -\xc7\xc7\xc7\x9b\xd3\xd3\x69\xfe\x13\x66\xce\x4f\x5f\xe6\x9b\x0f\ -\x3c\x7d\xea\xf8\x63\xfa\xe7\x9b\xee\x8f\x87\xe1\x7a\xde\x9f\x2e\ -\xf7\x4f\xe7\xaf\xbb\xf1\x72\xbb\x7f\x9c\x7f\x67\x5c\xfc\xd1\x3d\ -\x8f\x2f\x78\x50\x9a\x8b\x65\xe5\xec\x84\x87\x06\x19\x06\xc0\x7c\ -\x1d\x0c\x53\xc6\x09\x2d\xfd\xa4\x98\x91\xd2\x07\x6d\x87\xfd\x20\ -\x98\x52\x41\x7a\xe3\xa6\xc5\x1a\xf8\x20\xe0\xfb\x51\x32\xeb\x60\ -\x28\xcc\xc4\x57\x60\x3d\xea\xbf\xb1\x5f\x35\x52\x8b\x4c\xa4\xd7\ -\x7c\x1c\x36\x6f\xf1\x13\x9a\x09\x0e\x04\xd5\x64\x99\x97\x56\x5b\ -\x2b\x80\x9f\x62\x32\x72\x36\x76\x5a\xac\xca\xcf\xb2\x38\x74\x42\ -\xc0\xca\x38\xac\x47\xa1\xfc\x24\x8d\x9f\x97\xd9\x7f\x9c\x05\xa5\ -\x2c\x0f\xc0\x8f\xb3\xe4\x53\xe3\xd4\xd4\xcc\xca\x50\x94\xb5\x61\ -\xe9\x35\x5c\x07\x43\x19\x6a\x02\x43\xe9\x59\xd9\x61\x61\x99\xe4\ -\x02\x5c\x08\x0c\x25\x93\x2a\xf2\x0e\xd3\x62\x55\x7e\x9a\x69\x0b\ -\x70\x0b\x0b\xe3\xa8\x0e\x84\xb2\xb3\x04\x76\x40\xc9\xc4\x68\x97\ -\x93\xe4\x8c\x7b\xa3\x82\x4e\xfe\xd3\xa1\x6c\x7b\x33\x2b\x3f\xf0\ -\xb3\xcf\xa4\xf8\x2a\xb0\xc7\xa1\x1c\x3d\x81\xa3\xd2\xcc\xc5\x97\ -\xd7\x93\x74\x20\x12\xe7\xa4\x49\x1c\x65\x8e\xf1\xb4\xb4\x8a\x6a\ -\x0b\xae\x71\x34\x49\x57\x89\x22\x8e\xeb\x60\x18\x43\x43\x51\x89\ -\xe6\x4c\xc4\x35\xe4\x14\x98\x82\xa8\xe6\x5e\x24\x86\xa2\xea\x13\ -\x48\xe4\xfd\xf3\x8d\x61\xa6\x95\x18\xe2\xb8\x0e\x86\x32\xa4\xe8\ -\x44\x97\xdd\xd0\x93\xf2\xcc\x69\x2b\x8a\x4e\xea\x72\xaf\x32\xd4\ -\x29\xc6\xb2\x0f\x51\x5c\x07\x43\x19\x52\x74\xa2\x04\x13\x96\xc7\ -\x94\x05\x5c\x8d\xb3\x59\x27\x8a\x25\xc7\xc0\xec\x62\x55\x7e\x8e\ -\xc5\x61\x8c\xbc\x15\x54\x07\x42\xd9\x51\x74\x22\x8b\x00\x55\xf4\ -\x9f\x34\x86\x2b\x93\xf2\x74\x64\x9c\xb4\x5d\xad\xca\xae\x64\x3c\ -\x9b\xd2\x34\x86\xea\x40\x28\x3b\x8a\x42\xb4\x60\x21\xee\x01\x14\ -\x02\x58\x42\x3b\x1f\xb2\x42\x7c\x76\x46\x8c\xfc\x6a\xb6\x2c\x98\ -\x44\x91\x15\x82\xe3\x3a\x18\xc6\xd0\x52\x14\xa2\x2c\xcb\x69\x42\ -\x1a\xe6\xa2\xfb\x5c\x22\x58\x8a\x5f\x0c\xfc\x6a\xb6\xf0\x73\xd5\ -\x37\x6b\xb8\x0e\x86\x12\xa4\x08\x24\x26\xc2\x49\x4b\x50\x89\x13\ -\x2e\x6b\xc3\xc2\x4c\xfa\x6d\x3e\x8b\x33\xaf\x3c\x29\x0f\x50\x02\ -\x94\xf8\x17\x8e\x15\x89\x89\xc0\x82\x29\x75\x82\xd0\x09\x90\x1a\ -\x81\x35\x76\x94\xf8\x17\x25\xc2\xec\x24\x3c\xe8\xd4\x38\x23\x73\ -\x9d\x48\xda\x4f\x99\xab\x9a\x6d\x03\x7d\x49\x6a\xab\xb8\x0e\x86\ -\x32\xa4\x68\x00\xaa\xab\xcd\x21\x06\x9a\x0f\x20\x01\x9d\x77\x31\ -\x67\x78\x19\x57\xae\x66\xdb\xcf\xa4\xc6\x5c\xc8\x70\x5c\x07\xc3\ -\x18\x3a\x92\x06\x96\x72\x23\x14\xb3\x42\x8a\x52\x25\xde\x66\xe8\ -\x69\x0c\xd7\x72\xb0\xa3\x88\x00\x2a\x6d\x52\xbf\x8d\x4a\x70\x22\ -\x28\x29\x89\xdd\x94\xa4\x75\x53\x72\x8d\x21\x45\x25\x7a\xf1\x61\ -\xec\xab\x52\xc3\x9c\x54\x62\x4a\xfd\x5d\xac\x96\x87\xb9\x5a\xda\ -\x65\x0c\xd5\x81\x50\x76\x14\x95\xa8\x90\xbb\x51\x37\x41\xbd\xe0\ -\xbc\xa4\xb9\x9a\x43\xd5\xb4\x58\x8d\x9d\xa9\x1b\x87\xa3\x3a\x10\ -\xca\x8e\xa2\x10\x59\x8a\xe1\x73\xdf\x41\xca\xcb\x82\x9c\x16\xab\ -\xb2\x33\x4d\x9a\x38\xca\xbc\xad\x5f\x4f\x51\x47\xd7\x89\x7a\xc8\ -\x35\x10\x7b\x79\x67\x75\xa9\xec\x8b\xd5\xf2\x5f\x7a\x9b\xa4\x4b\ -\x1c\xd6\xa3\x50\x7e\x24\x6d\xe4\x18\x89\x1d\x80\x62\xf0\x4a\x5c\ -\xe5\xfc\xe2\x79\x69\x21\xab\xd5\x94\x61\x3b\xf1\x62\xb0\x1e\x85\ -\xf2\x23\x29\x23\x9f\x33\x54\x4c\xd0\x9d\xfb\x7e\x79\x83\xe2\x49\ -\xc2\x50\x85\x1c\xb4\x01\x29\xf0\xb2\xef\xf2\x01\x21\x95\xf5\x6a\ -\x36\xef\x85\x7a\xc0\x59\xc3\x75\x30\x94\x20\x49\x1b\xa6\x64\xac\ -\x98\x1b\x8c\xaf\xc7\xc8\x77\x29\x6f\x81\x22\x0f\xb9\x64\x01\xcd\ -\x78\x23\xf8\x2e\x1e\x0c\x14\x7d\x80\x6a\x4b\x12\x85\xfe\xc5\xf0\ -\xf7\x6d\xf1\x02\xe9\x84\x01\x39\x59\x7b\x13\xe0\x94\x60\xfa\xbb\ -\x02\xe3\x9c\x07\xc1\xbc\x42\x10\x8e\x24\x06\x6a\x58\x29\xbf\x18\ -\xac\x43\xa1\xfc\x28\x22\xd1\x70\xba\x9d\xb4\x01\x92\x3c\xb7\x57\ -\xa9\xad\x8c\x3f\x2d\xe5\xe5\x3a\xf6\x7c\x3a\xcf\xa2\x4b\x93\x0e\ -\x10\xe5\x1a\x25\xc0\x01\xca\x05\x58\x5e\xbc\xdf\x35\x4a\xe0\x94\ -\xf8\x8f\xa7\xd6\x00\x1f\x68\x4d\x80\x62\xbd\x46\x79\x76\xb8\xaf\ -\xe6\xcb\x4b\x00\xf5\xf2\x16\xe0\x05\xb0\xc7\xa1\x1c\x7b\x09\x6c\ -\x37\x87\x72\xb1\xb9\x29\xb7\x96\xe9\xc2\x73\x93\x6f\x3c\xb7\x9b\ -\x78\x03\xfa\x3f\xe0\xde\x25\x35\ +\x00\x00\x08\xd7\ +\x3c\ +\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x64\x3d\x22\x63\x6f\x6e\x63\x72\x65\x74\x65\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\x55\x6e\ +\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\ +\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x30\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x2e\x35\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ +\x2e\x35\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x67\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ +\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\x65\x3a\ +\x23\x30\x30\x30\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\x65\x2d\ +\x77\x69\x64\x74\x68\x3a\x2e\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x73\x63\ +\x61\x6c\x65\x28\x2e\x30\x31\x29\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x67\x33\x34\x30\x31\x22\x3e\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\ +\x6d\x20\x35\x2e\x33\x35\x37\x31\x34\x32\x38\x2c\x33\x2e\x35\x32\ +\x32\x38\x39\x34\x36\x20\x61\x20\x31\x2e\x33\x33\x39\x32\x38\x35\ +\x37\x2c\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x20\x30\x20\x31\x20\ +\x31\x20\x2d\x32\x2e\x36\x37\x38\x35\x37\x31\x35\x2c\x30\x20\x31\ +\x2e\x33\x33\x39\x32\x38\x35\x37\x2c\x31\x2e\x33\x33\x39\x32\x38\ +\x35\x37\x20\x30\x20\x31\x20\x31\x20\x32\x2e\x36\x37\x38\x35\x37\ +\x31\x35\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x34\x2e\ +\x31\x30\x37\x31\x34\x33\x2c\x36\x2e\x38\x32\x36\x34\x36\x36\x31\ +\x20\x61\x20\x33\x2e\x32\x31\x34\x32\x38\x35\x36\x2c\x33\x2e\x32\ +\x31\x34\x32\x38\x35\x36\x20\x30\x20\x31\x20\x31\x20\x2d\x36\x2e\ +\x34\x32\x38\x35\x37\x31\x31\x2c\x30\x20\x33\x2e\x32\x31\x34\x32\ +\x38\x35\x36\x2c\x33\x2e\x32\x31\x34\x32\x38\x35\x36\x20\x30\x20\ +\x31\x20\x31\x20\x36\x2e\x34\x32\x38\x35\x37\x31\x31\x2c\x30\x20\ +\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\ +\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x34\x2e\x38\x32\x31\x34\x32\ +\x38\x2c\x33\x30\x2e\x39\x33\x33\x36\x30\x39\x20\x61\x20\x30\x2e\ +\x37\x31\x34\x32\x38\x35\x37\x33\x2c\x30\x2e\x37\x31\x34\x32\x38\ +\x35\x37\x33\x20\x30\x20\x31\x20\x31\x20\x2d\x31\x2e\x34\x32\x38\ +\x35\x37\x31\x2c\x30\x20\x30\x2e\x37\x31\x34\x32\x38\x35\x37\x33\ +\x2c\x30\x2e\x37\x31\x34\x32\x38\x35\x37\x33\x20\x30\x20\x31\x20\ +\x31\x20\x31\x2e\x34\x32\x38\x35\x37\x31\x2c\x30\x20\x7a\x22\x2f\ +\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\ +\x64\x3d\x22\x6d\x20\x32\x38\x2e\x35\x37\x31\x34\x32\x38\x2c\x31\ +\x36\x2e\x32\x30\x31\x34\x36\x36\x20\x61\x20\x32\x2e\x32\x33\x32\ +\x31\x34\x32\x39\x2c\x32\x2e\x32\x33\x32\x31\x34\x32\x39\x20\x30\ +\x20\x31\x20\x31\x20\x2d\x34\x2e\x34\x36\x34\x32\x38\x36\x2c\x30\ +\x20\x32\x2e\x32\x33\x32\x31\x34\x32\x39\x2c\x32\x2e\x32\x33\x32\ +\x31\x34\x32\x39\x20\x30\x20\x31\x20\x31\x20\x34\x2e\x34\x36\x34\ +\x32\x38\x36\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x36\x2e\ +\x32\x35\x30\x30\x30\x30\x32\x2c\x32\x30\x2e\x30\x38\x35\x33\x39\ +\x34\x20\x61\x20\x30\x2e\x34\x39\x31\x30\x37\x31\x34\x33\x2c\x30\ +\x2e\x34\x39\x31\x30\x37\x31\x34\x33\x20\x30\x20\x31\x20\x31\x20\ +\x2d\x30\x2e\x39\x38\x32\x31\x34\x32\x39\x2c\x30\x20\x30\x2e\x34\ +\x39\x31\x30\x37\x31\x34\x33\x2c\x30\x2e\x34\x39\x31\x30\x37\x31\ +\x34\x33\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x39\x38\x32\x31\x34\ +\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x33\x34\x2e\ +\x37\x33\x32\x31\x34\x34\x2c\x32\x37\x2e\x35\x32\x37\x37\x32\x35\ +\x20\x61\x20\x30\x2e\x32\x36\x37\x38\x35\x37\x31\x33\x2c\x30\x2e\ +\x34\x33\x33\x34\x30\x31\x39\x37\x20\x30\x20\x31\x20\x31\x20\x2d\ +\x30\x2e\x35\x33\x35\x37\x31\x34\x2c\x30\x20\x30\x2e\x32\x36\x37\ +\x38\x35\x37\x31\x33\x2c\x30\x2e\x34\x33\x33\x34\x30\x31\x39\x37\ +\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x35\x33\x35\x37\x31\x34\x2c\ +\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\ +\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x34\x30\x2e\x31\x37\x38\ +\x35\x37\x32\x2c\x39\x2e\x33\x37\x31\x31\x30\x38\x31\x20\x61\x20\ +\x30\x2e\x31\x33\x33\x39\x32\x38\x35\x37\x2c\x30\x2e\x32\x32\x33\ +\x32\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x32\ +\x36\x37\x38\x35\x37\x2c\x30\x20\x30\x2e\x31\x33\x33\x39\x32\x38\ +\x35\x37\x2c\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x20\x30\x20\ +\x31\x20\x31\x20\x30\x2e\x32\x36\x37\x38\x35\x37\x2c\x30\x20\x7a\ +\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\ +\x68\x20\x64\x3d\x22\x6d\x20\x34\x30\x2e\x39\x38\x32\x31\x34\x34\ +\x2c\x33\x38\x2e\x37\x34\x36\x31\x30\x39\x20\x61\x20\x30\x2e\x32\ +\x32\x33\x32\x31\x34\x32\x38\x2c\x30\x2e\x32\x32\x33\x32\x31\x34\ +\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x34\x34\x36\x34\ +\x32\x38\x2c\x30\x20\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x2c\ +\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\ +\x20\x30\x2e\x34\x34\x36\x34\x32\x38\x2c\x30\x20\x7a\x22\x2f\x3e\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\ +\x3d\x22\x6d\x20\x33\x31\x2e\x31\x36\x30\x37\x31\x35\x2c\x34\x30\ +\x2e\x35\x37\x36\x34\x36\x36\x20\x61\x20\x33\x2e\x39\x32\x38\x35\ +\x37\x31\x35\x2c\x33\x2e\x39\x32\x38\x35\x37\x31\x35\x20\x30\x20\ +\x31\x20\x31\x20\x2d\x37\x2e\x38\x35\x37\x31\x34\x33\x2c\x30\x20\ +\x33\x2e\x39\x32\x38\x35\x37\x31\x35\x2c\x33\x2e\x39\x32\x38\x35\ +\x37\x31\x35\x20\x30\x20\x31\x20\x31\x20\x37\x2e\x38\x35\x37\x31\ +\x34\x33\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x32\x32\x2e\ +\x32\x33\x32\x31\x34\x33\x2c\x33\x38\x2e\x32\x35\x35\x30\x33\x35\ +\x20\x61\x20\x31\x2e\x36\x30\x37\x31\x34\x32\x38\x2c\x31\x2e\x36\ +\x30\x37\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x33\x2e\ +\x32\x31\x34\x32\x38\x36\x2c\x30\x20\x31\x2e\x36\x30\x37\x31\x34\ +\x32\x38\x2c\x31\x2e\x36\x30\x37\x31\x34\x32\x38\x20\x30\x20\x31\ +\x20\x31\x20\x33\x2e\x32\x31\x34\x32\x38\x36\x2c\x30\x20\x7a\x22\ +\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\ +\x20\x64\x3d\x22\x6d\x20\x34\x31\x2e\x39\x36\x34\x32\x38\x37\x2c\ +\x31\x31\x2e\x36\x34\x37\x38\x39\x35\x20\x61\x20\x30\x2e\x38\x39\ +\x32\x38\x35\x37\x31\x33\x2c\x30\x2e\x38\x39\x32\x38\x35\x37\x31\ +\x33\x20\x30\x20\x31\x20\x31\x20\x2d\x31\x2e\x37\x38\x35\x37\x31\ +\x34\x2c\x30\x20\x30\x2e\x38\x39\x32\x38\x35\x37\x31\x33\x2c\x30\ +\x2e\x38\x39\x32\x38\x35\x37\x31\x33\x20\x30\x20\x31\x20\x31\x20\ +\x31\x2e\x37\x38\x35\x37\x31\x34\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\ +\x22\x6d\x20\x33\x36\x2e\x30\x37\x31\x34\x33\x2c\x32\x35\x2e\x37\ +\x35\x35\x30\x33\x37\x20\x61\x20\x30\x2e\x33\x35\x37\x31\x34\x32\ +\x38\x37\x2c\x30\x2e\x33\x35\x37\x31\x34\x32\x38\x37\x20\x30\x20\ +\x31\x20\x31\x20\x2d\x30\x2e\x37\x31\x34\x32\x38\x36\x2c\x30\x20\ +\x30\x2e\x33\x35\x37\x31\x34\x32\x38\x37\x2c\x30\x2e\x33\x35\x37\ +\x31\x34\x32\x38\x37\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x37\x31\ +\x34\x32\x38\x36\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x36\ +\x2e\x32\x35\x2c\x34\x32\x2e\x39\x38\x37\x31\x37\x39\x20\x61\x20\ +\x30\x2e\x36\x32\x35\x2c\x30\x2e\x36\x32\x35\x20\x30\x20\x31\x20\ +\x31\x20\x2d\x31\x2e\x32\x35\x2c\x30\x20\x30\x2e\x36\x32\x35\x2c\ +\x30\x2e\x36\x32\x35\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x32\x35\ +\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x37\x2e\x33\x32\ +\x31\x34\x32\x38\x2c\x31\x39\x2e\x39\x35\x31\x34\x36\x36\x20\x61\ +\x20\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x2c\x31\x2e\x33\x33\x39\ +\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\x20\x2d\x32\x2e\x36\x37\ +\x38\x35\x37\x31\x2c\x30\x20\x31\x2e\x33\x33\x39\x32\x38\x35\x37\ +\x2c\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\ +\x20\x32\x2e\x36\x37\x38\x35\x37\x31\x2c\x30\x20\x7a\x22\x2f\x3e\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\ +\x3d\x22\x6d\x20\x31\x31\x2e\x39\x36\x34\x32\x38\x36\x2c\x31\x38\ +\x2e\x31\x36\x35\x37\x35\x32\x20\x61\x20\x30\x2e\x34\x34\x36\x34\ +\x32\x38\x35\x37\x2c\x30\x2e\x34\x34\x36\x34\x32\x38\x35\x37\x20\ +\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x38\x39\x32\x38\x35\x37\x2c\ +\x30\x20\x30\x2e\x34\x34\x36\x34\x32\x38\x35\x37\x2c\x30\x2e\x34\ +\x34\x36\x34\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\x20\x30\x2e\ +\x38\x39\x32\x38\x35\x37\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\ +\x20\x31\x36\x2e\x36\x30\x37\x31\x34\x33\x2c\x37\x2e\x38\x39\x37\ +\x38\x39\x34\x39\x20\x61\x20\x30\x2e\x35\x33\x35\x37\x31\x34\x32\ +\x37\x2c\x30\x2e\x35\x33\x35\x37\x31\x34\x32\x37\x20\x30\x20\x31\ +\x20\x31\x20\x2d\x31\x2e\x30\x37\x31\x34\x32\x39\x2c\x30\x20\x30\ +\x2e\x35\x33\x35\x37\x31\x34\x32\x37\x2c\x30\x2e\x35\x33\x35\x37\ +\x31\x34\x32\x37\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x30\x37\x31\ +\x34\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x0a\x20\x20\x20\x20\ +\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\ +\x74\x65\x72\x6e\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\ +\x3c\x2f\x73\x76\x67\x3e\ \x00\x00\x74\xe7\ \x3c\ \xb8\x64\x18\xca\xef\x9c\x95\xcd\x21\x1c\xbf\x60\xa1\xbd\xdd\x42\ @@ -37775,83 +37846,83 @@ qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x35\x00\x00\x00\x1b\ \x00\x00\x00\x38\x00\x02\x00\x00\x00\x05\x00\x00\x00\x16\ \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x11\x00\x00\x00\x05\ -\x00\x00\x02\x8e\x00\x01\x00\x00\x00\x01\x00\x06\x43\xc1\ -\x00\x00\x02\x02\x00\x01\x00\x00\x00\x01\x00\x05\x40\x4a\ -\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x03\x79\xe8\ -\x00\x00\x02\x56\x00\x01\x00\x00\x00\x01\x00\x05\xdd\x0d\ -\x00\x00\x02\x3a\x00\x01\x00\x00\x00\x01\x00\x05\xa8\x51\ -\x00\x00\x01\x3e\x00\x01\x00\x00\x00\x01\x00\x02\x06\xcc\ -\x00\x00\x02\x1e\x00\x01\x00\x00\x00\x01\x00\x05\x73\x1f\ -\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x96\ -\x00\x00\x02\x72\x00\x01\x00\x00\x00\x01\x00\x06\x10\xb8\ -\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x39\x0a\ -\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x01\x5e\x55\ -\x00\x00\x01\xae\x00\x01\x00\x00\x00\x01\x00\x04\x2a\x0f\ -\x00\x00\x01\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe4\x37\ -\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\x2b\x46\ -\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x7f\x81\ -\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x04\x5d\x63\ -\x00\x00\x01\xe6\x00\x01\x00\x00\x00\x01\x00\x05\x0e\x32\ +\x00\x00\x02\x8e\x00\x01\x00\x00\x00\x01\x00\x06\x48\x2f\ +\x00\x00\x02\x02\x00\x01\x00\x00\x00\x01\x00\x05\x44\xb8\ +\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x03\x7e\x56\ +\x00\x00\x02\x56\x00\x01\x00\x00\x00\x01\x00\x05\xe1\x7b\ +\x00\x00\x02\x3a\x00\x01\x00\x00\x00\x01\x00\x05\xac\xbf\ +\x00\x00\x01\x3e\x00\x01\x00\x00\x00\x01\x00\x02\x0b\x3a\ +\x00\x00\x02\x1e\x00\x01\x00\x00\x00\x01\x00\x05\x77\x8d\ +\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x04\ +\x00\x00\x02\x72\x00\x01\x00\x00\x00\x01\x00\x06\x15\x26\ +\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x3d\x78\ +\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x01\x62\xc3\ +\x00\x00\x01\xae\x00\x01\x00\x00\x00\x01\x00\x04\x2e\x7d\ +\x00\x00\x01\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe8\xa5\ +\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\x2f\xb4\ +\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x83\xef\ +\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x04\x61\xd1\ +\x00\x00\x01\xe6\x00\x01\x00\x00\x00\x01\x00\x05\x12\xa0\ \x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x00\xb0\x00\x01\x00\x00\x00\x01\x00\x00\x06\x29\ +\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x06\x29\ \x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x01\x64\ \x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x04\xc4\ \x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x03\x12\ -\x00\x00\x06\x02\x00\x01\x00\x00\x00\x01\x00\x07\x6c\x16\ -\x00\x00\x03\xb0\x00\x00\x00\x00\x00\x01\x00\x06\xbd\xc3\ -\x00\x00\x08\x38\x00\x01\x00\x00\x00\x01\x00\x08\x1f\xe0\ -\x00\x00\x0a\x7e\x00\x01\x00\x00\x00\x01\x00\x08\xde\xc8\ -\x00\x00\x04\xba\x00\x01\x00\x00\x00\x01\x00\x07\x08\xaa\ -\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x07\x89\x60\ -\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x07\xe1\xfe\ -\x00\x00\x06\xbe\x00\x00\x00\x00\x00\x01\x00\x07\xb3\x2f\ -\x00\x00\x08\xd2\x00\x01\x00\x00\x00\x01\x00\x08\x59\x6f\ -\x00\x00\x0a\xce\x00\x01\x00\x00\x00\x01\x00\x08\xfa\xd8\ -\x00\x00\x03\xf6\x00\x01\x00\x00\x00\x01\x00\x06\xd6\xe5\ -\x00\x00\x07\x84\x00\x01\x00\x00\x00\x01\x00\x07\xe7\xc2\ -\x00\x00\x06\x24\x00\x00\x00\x00\x00\x01\x00\x07\x76\xca\ -\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x06\xdc\x64\ -\x00\x00\x06\x92\x00\x01\x00\x00\x00\x01\x00\x07\xa2\x25\ -\x00\x00\x03\xd2\x00\x01\x00\x00\x00\x01\x00\x06\xcc\x79\ -\x00\x00\x0a\x08\x00\x00\x00\x00\x00\x01\x00\x08\xc1\x0e\ -\x00\x00\x03\x30\x00\x01\x00\x00\x00\x01\x00\x06\x98\x0d\ -\x00\x00\x05\x0a\x00\x01\x00\x00\x00\x01\x00\x07\x23\xc9\ -\x00\x00\x09\xc0\x00\x01\x00\x00\x00\x01\x00\x08\xad\x18\ -\x00\x00\x09\xe2\x00\x01\x00\x00\x00\x01\x00\x08\xb7\x7a\ -\x00\x00\x04\xe8\x00\x00\x00\x00\x00\x01\x00\x07\x11\xb0\ -\x00\x00\x02\xfe\x00\x01\x00\x00\x00\x01\x00\x06\x90\x54\ -\x00\x00\x07\xee\x00\x01\x00\x00\x00\x01\x00\x08\x08\xb6\ -\x00\x00\x09\x2c\x00\x00\x00\x00\x00\x01\x00\x08\x69\xb7\ -\x00\x00\x05\x5e\x00\x01\x00\x00\x00\x01\x00\x07\x35\x39\ -\x00\x00\x09\x50\x00\x00\x00\x00\x00\x01\x00\x08\x80\x6a\ -\x00\x00\x07\x18\x00\x00\x00\x00\x00\x01\x00\x07\xc9\xd1\ -\x00\x00\x04\x6c\x00\x01\x00\x00\x00\x01\x00\x06\xf7\xcc\ -\x00\x00\x0a\x9e\x00\x00\x00\x00\x00\x01\x00\x08\xe9\x62\ -\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x07\x4c\x78\ -\x00\x00\x03\x5c\x00\x00\x00\x00\x00\x01\x00\x06\xa0\x14\ -\x00\x00\x0a\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x06\xb1\ -\x00\x00\x09\x98\x00\x00\x00\x00\x00\x01\x00\x08\x9d\xf1\ -\x00\x00\x03\x80\x00\x01\x00\x00\x00\x01\x00\x06\xb5\x34\ -\x00\x00\x08\x5a\x00\x01\x00\x00\x00\x01\x00\x08\x28\x89\ -\x00\x00\x0a\x2e\x00\x00\x00\x00\x00\x01\x00\x08\xc9\x9b\ -\x00\x00\x05\xe0\x00\x01\x00\x00\x00\x01\x00\x07\x5e\xb7\ -\x00\x00\x06\xf0\x00\x01\x00\x00\x00\x01\x00\x07\xc0\x20\ -\x00\x00\x08\xb0\x00\x00\x00\x00\x00\x01\x00\x08\x44\xde\ -\x00\x00\x04\x90\x00\x01\x00\x00\x00\x01\x00\x06\xfe\x69\ -\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x07\xdb\x89\ -\x00\x00\x05\x3e\x00\x01\x00\x00\x00\x01\x00\x07\x2f\xba\ -\x00\x00\x08\xfc\x00\x01\x00\x00\x00\x01\x00\x08\x5f\xe4\ -\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x07\xfd\x44\ -\x00\x00\x08\x10\x00\x01\x00\x00\x00\x01\x00\x08\x10\x0a\ -\x00\x00\x0a\x54\x00\x01\x00\x00\x00\x01\x00\x08\xd4\x46\ -\x00\x00\x09\x74\x00\x01\x00\x00\x00\x01\x00\x08\x93\x41\ -\x00\x00\x04\x3c\x00\x01\x00\x00\x00\x01\x00\x06\xef\x83\ -\x00\x00\x07\xa6\x00\x00\x00\x00\x00\x01\x00\x07\xed\x72\ -\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x07\x3d\x1e\ -\x00\x00\x06\x72\x00\x01\x00\x00\x00\x01\x00\x07\x98\xb1\ -\x00\x00\x08\x7e\x00\x00\x00\x00\x00\x01\x00\x08\x2f\xda\ -\x00\x00\x02\xaa\x00\x01\x00\x00\x00\x01\x00\x06\x77\x44\ -\x00\x00\x02\xd6\x00\x01\x00\x00\x00\x01\x00\x06\x80\xb1\ +\x00\x00\x06\x02\x00\x01\x00\x00\x00\x01\x00\x07\x70\x84\ +\x00\x00\x03\xb0\x00\x00\x00\x00\x00\x01\x00\x06\xc2\x31\ +\x00\x00\x08\x38\x00\x01\x00\x00\x00\x01\x00\x08\x24\x4e\ +\x00\x00\x0a\x7e\x00\x01\x00\x00\x00\x01\x00\x08\xe3\x36\ +\x00\x00\x04\xba\x00\x01\x00\x00\x00\x01\x00\x07\x0d\x18\ +\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x07\x8d\xce\ +\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x07\xe6\x6c\ +\x00\x00\x06\xbe\x00\x00\x00\x00\x00\x01\x00\x07\xb7\x9d\ +\x00\x00\x08\xd2\x00\x01\x00\x00\x00\x01\x00\x08\x5d\xdd\ +\x00\x00\x0a\xce\x00\x01\x00\x00\x00\x01\x00\x08\xff\x46\ +\x00\x00\x03\xf6\x00\x01\x00\x00\x00\x01\x00\x06\xdb\x53\ +\x00\x00\x07\x84\x00\x01\x00\x00\x00\x01\x00\x07\xec\x30\ +\x00\x00\x06\x24\x00\x00\x00\x00\x00\x01\x00\x07\x7b\x38\ +\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x06\xe0\xd2\ +\x00\x00\x06\x92\x00\x01\x00\x00\x00\x01\x00\x07\xa6\x93\ +\x00\x00\x03\xd2\x00\x01\x00\x00\x00\x01\x00\x06\xd0\xe7\ +\x00\x00\x0a\x08\x00\x00\x00\x00\x00\x01\x00\x08\xc5\x7c\ +\x00\x00\x03\x30\x00\x01\x00\x00\x00\x01\x00\x06\x9c\x7b\ +\x00\x00\x05\x0a\x00\x01\x00\x00\x00\x01\x00\x07\x28\x37\ +\x00\x00\x09\xc0\x00\x01\x00\x00\x00\x01\x00\x08\xb1\x86\ +\x00\x00\x09\xe2\x00\x01\x00\x00\x00\x01\x00\x08\xbb\xe8\ +\x00\x00\x04\xe8\x00\x00\x00\x00\x00\x01\x00\x07\x16\x1e\ +\x00\x00\x02\xfe\x00\x01\x00\x00\x00\x01\x00\x06\x94\xc2\ +\x00\x00\x07\xee\x00\x01\x00\x00\x00\x01\x00\x08\x0d\x24\ +\x00\x00\x09\x2c\x00\x00\x00\x00\x00\x01\x00\x08\x6e\x25\ +\x00\x00\x05\x5e\x00\x01\x00\x00\x00\x01\x00\x07\x39\xa7\ +\x00\x00\x09\x50\x00\x00\x00\x00\x00\x01\x00\x08\x84\xd8\ +\x00\x00\x07\x18\x00\x00\x00\x00\x00\x01\x00\x07\xce\x3f\ +\x00\x00\x04\x6c\x00\x01\x00\x00\x00\x01\x00\x06\xfc\x3a\ +\x00\x00\x0a\x9e\x00\x00\x00\x00\x00\x01\x00\x08\xed\xd0\ +\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x07\x50\xe6\ +\x00\x00\x03\x5c\x00\x00\x00\x00\x00\x01\x00\x06\xa4\x82\ +\x00\x00\x0a\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x0b\x1f\ +\x00\x00\x09\x98\x00\x00\x00\x00\x00\x01\x00\x08\xa2\x5f\ +\x00\x00\x03\x80\x00\x01\x00\x00\x00\x01\x00\x06\xb9\xa2\ +\x00\x00\x08\x5a\x00\x01\x00\x00\x00\x01\x00\x08\x2c\xf7\ +\x00\x00\x0a\x2e\x00\x00\x00\x00\x00\x01\x00\x08\xce\x09\ +\x00\x00\x05\xe0\x00\x01\x00\x00\x00\x01\x00\x07\x63\x25\ +\x00\x00\x06\xf0\x00\x01\x00\x00\x00\x01\x00\x07\xc4\x8e\ +\x00\x00\x08\xb0\x00\x00\x00\x00\x00\x01\x00\x08\x49\x4c\ +\x00\x00\x04\x90\x00\x01\x00\x00\x00\x01\x00\x07\x02\xd7\ +\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x07\xdf\xf7\ +\x00\x00\x05\x3e\x00\x01\x00\x00\x00\x01\x00\x07\x34\x28\ +\x00\x00\x08\xfc\x00\x01\x00\x00\x00\x01\x00\x08\x64\x52\ +\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x08\x01\xb2\ +\x00\x00\x08\x10\x00\x01\x00\x00\x00\x01\x00\x08\x14\x78\ +\x00\x00\x0a\x54\x00\x01\x00\x00\x00\x01\x00\x08\xd8\xb4\ +\x00\x00\x09\x74\x00\x01\x00\x00\x00\x01\x00\x08\x97\xaf\ +\x00\x00\x04\x3c\x00\x01\x00\x00\x00\x01\x00\x06\xf3\xf1\ +\x00\x00\x07\xa6\x00\x00\x00\x00\x00\x01\x00\x07\xf1\xe0\ +\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x07\x41\x8c\ +\x00\x00\x06\x72\x00\x01\x00\x00\x00\x01\x00\x07\x9d\x1f\ +\x00\x00\x08\x7e\x00\x00\x00\x00\x00\x01\x00\x08\x34\x48\ +\x00\x00\x02\xaa\x00\x01\x00\x00\x00\x01\x00\x06\x7b\xb2\ +\x00\x00\x02\xd6\x00\x01\x00\x00\x00\x01\x00\x06\x85\x1f\ " def qInitResources(): diff --git a/src/Mod/Draft/Resources/patterns/concrete.svg b/src/Mod/Draft/Resources/patterns/concrete.svg index 8c2fd6c616..2aa5e9565e 100644 --- a/src/Mod/Draft/Resources/patterns/concrete.svg +++ b/src/Mod/Draft/Resources/patterns/concrete.svg @@ -11,102 +11,22 @@ style="fill:none; stroke:#000000; stroke-width:.5" transform="scale(.01)" id="g3401"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + From 141103c9660f0555f64d0f465015c7a80ad27bf7 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 6 Jun 2012 16:45:34 -0300 Subject: [PATCH 59/69] Arch: Bugfix in wall object --- src/Mod/Arch/ArchWall.py | 46 ++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 25fb891eee..1d6f8e5e07 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -382,28 +382,32 @@ class _Wall(ArchComponent.Component): else: FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object"))) - for app in obj.Additions: - if hasattr(app,"Shape"): - base = base.fuse(app.Shape) - app.ViewObject.hide() #to be removed - for hole in obj.Subtractions: - if Draft.getType(hole) == "Window": - # window - if hole.Base and obj.Width: - f = self.getSubVolume(hole.Base,width) - if f: - base = base.cut(f) - elif Draft.isClone(hole,"Window"): - if hole.Objects[0].Base and width: - f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement.Base) - if f: - base = base.cut(f) - elif hasattr(hole,"Shape"): - if not hole.Shape.isNull(): - base = base.cut(hole.Shape) - hole.ViewObject.hide() # to be removed - if base: + for app in obj.Additions: + if hasattr(app,"Shape"): + if app.Shape: + if not app.Shape.isNull(): + base = base.fuse(app.Shape) + app.ViewObject.hide() #to be removed + + for hole in obj.Subtractions: + if Draft.getType(hole) == "Window": + # window + if hole.Base and obj.Width: + f = self.getSubVolume(hole.Base,width) + if f: + base = base.cut(f) + elif Draft.isClone(hole,"Window"): + if hole.Objects[0].Base and width: + f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement.Base) + if f: + base = base.cut(f) + elif hasattr(hole,"Shape"): + if hole.Shape: + if not hole.Shape.isNull(): + base = base.cut(hole.Shape) + hole.ViewObject.hide() # to be removed + if not base.isNull(): try: base = base.removeSplitter() From 7c05d3aa84425f85b6836e8dc3cd501de321d3f2 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 6 Jun 2012 16:45:54 -0300 Subject: [PATCH 60/69] Draft: Fixes in trackers * Plane tracker can now be turned off in preferences * Grid no longer stays in front of new objects --- src/Mod/Draft/Draft.py | 2 +- src/Mod/Draft/DraftGui.py | 3 + src/Mod/Draft/DraftSnap.py | 4 + src/Mod/Draft/DraftTools.py | 193 +++--- src/Mod/Draft/DraftTrackers.py | 16 + src/Mod/Draft/Draft_rc.py | 663 ++++++++++--------- src/Mod/Draft/Resources/ui/userprefs-base.ui | 51 +- 7 files changed, 468 insertions(+), 464 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 49ff4ad04b..fc2596bebc 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -108,7 +108,7 @@ def getParamType(param): return "float" elif param in ["selectBaseObjects","alwaysSnap","grid","fillmode","saveonexit","maxSnap", "SvgLinesBlack","dxfStdSize","showSnapBar","hideSnapBar","alwaysShowGrid", - "renderPolylineWidth"]: + "renderPolylineWidth","showPlaneTracker"]: return "bool" elif param in ["color","constructioncolor","snapcolor"]: return "unsigned" diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 7e651a6986..663c57fe9d 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -87,6 +87,9 @@ class todo: except: wrn = "[Draft.todo.commit] Unexpected error:", sys.exc_info()[0], "in ", f, "(", arg, ")" FreeCAD.Console.PrintWarning (wrn) + # restack Draft screen widgets after creation + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.restack() todo.commitlist = [] @staticmethod diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 183f20a52d..3918be2b2f 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -631,6 +631,10 @@ class Snapper: v.setCursor(cur) self.cursorMode = mode + def restack(self): + if self.grid: + self.grid.lowerTracker() + def off(self): "finishes snapping" if self.tracker: diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 06cffa4720..1a55ee3cf4 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -327,62 +327,65 @@ class SelectPlane: # Geometry constructors #--------------------------------------------------------------------------- -class Creator: - "A generic Draft Creator Tool used by creation tools such as line or arc" +class DraftTool: + "The base class of all Draft Tools" def __init__(self): self.commitList = [] - + def Activated(self,name="None"): if FreeCAD.activeDraftCommand: FreeCAD.activeDraftCommand.finish() + global Part, DraftGeomUtils import Part, DraftGeomUtils + self.ui = None self.call = None - self.doc = None - self.support = None + self.support = None self.commitList = [] self.doc = FreeCAD.ActiveDocument - self.view = Draft.get3DView() - self.featureName = name if not self.doc: self.finish() - else: - FreeCAD.activeDraftCommand = self - self.ui = FreeCADGui.draftToolBar - self.ui.sourceCmd = self - self.ui.setTitle(name) - self.ui.show() - rot = self.view.getCameraNode().getField("orientation").getValue() - upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) - plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) - self.node = [] - self.pos = [] - self.constrain = None - self.obj = None - self.snap = snapTracker() - self.extsnap = lineTracker(dotted=True) - self.planetrack = PlaneTracker() - - def IsActive(self): - if FreeCADGui.ActiveDocument: - return True - else: - return False + return + FreeCAD.activeDraftCommand = self + self.view = Draft.get3DView() + self.ui = FreeCADGui.draftToolBar + self.ui.sourceCmd = self + self.ui.setTitle(name) + self.ui.show() + rot = self.view.getCameraNode().getField("orientation").getValue() + upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) + plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) + self.node = [] + self.pos = [] + self.constrain = None + self.obj = None + self.extendedCopy = False + self.ui.setTitle(name) + self.featureName = name + #self.snap = snapTracker() + #self.extsnap = lineTracker(dotted=True) + self.planetrack = None + if Draft.getParam("showPlaneTracker"): + self.planetrack = PlaneTracker() + def finish(self): - self.snap.finalize() - self.extsnap.finalize() - self.node=[] - self.planetrack.finalize() - if self.support: plane.restore() - FreeCADGui.Snapper.off() + self.node = [] + #self.snap.finalize() + #self.extsnap.finalize() FreeCAD.activeDraftCommand = None if self.ui: self.ui.offUi() - self.ui.sourceCmd = None + self.ui.sourceCmd=None + #self.ui.cross(False) msg("") + if self.planetrack: + self.planetrack.finalize() + if self.support: + plane.restore() + FreeCADGui.Snapper.off() if self.call: self.view.removeEventCallback("SoEvent",self.call) self.call = None @@ -390,6 +393,11 @@ class Creator: todo.delayCommit(self.commitList) self.commitList = [] + def commit(self,name,func): + "stores actions to be committed to the FreeCAD document" + # print "committing" + self.commitList.append((name,func)) + def getStrings(self): "returns a couple of useful strings fro building python commands" @@ -419,10 +427,19 @@ class Creator: fil = "True" return qr,sup,points,fil - - def commit(self,name,func): - "stores actions to be committed to the FreeCAD document" - self.commitList.append((name,func)) + + +class Creator(DraftTool): + "A generic Draft Creator Tool used by creation tools such as line or arc" + + def __init__(self): + DraftTool.__init__(self) + + def IsActive(self): + if FreeCADGui.ActiveDocument: + return True + else: + return False class Line(Creator): "The Line FreeCAD command definition" @@ -527,7 +544,8 @@ class Line(Creator): if (len(self.node) == 1): self.linetrack.on() msg(translate("draft", "Pick next point:\n")) - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) elif (len(self.node) == 2): last = self.node[len(self.node)-2] newseg = Part.Line(last,point).toShape() @@ -550,7 +568,8 @@ class Line(Creator): self.obj.ViewObject.Visibility = False self.node = [self.node[-1]] self.linetrack.p1(self.node[0]) - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) msg(translate("draft", "Pick next point:\n")) def numericInput(self,numx,numy,numz): @@ -643,7 +662,8 @@ class BSpline(Line): def drawUpdate(self,point): if (len(self.node) == 1): self.bsplinetrack.on() - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) msg(translate("draft", "Pick next point:\n")) else: spline = Part.BSplineCurve() @@ -821,7 +841,8 @@ class Rectangle(Creator): self.ui.setRelative() self.rect.setorigin(point) self.rect.on() - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) class Arc(Creator): @@ -1026,7 +1047,8 @@ class Arc(Creator): self.step = 1 self.linetrack.on() msg(translate("draft", "Pick radius:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) elif (self.step == 1): # choose radius if self.closedCircle: self.ui.cross(False) @@ -1294,7 +1316,8 @@ class Polygon(Creator): self.step = 1 self.linetrack.on() msg(translate("draft", "Pick radius:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) elif (self.step == 1): # choose radius self.ui.cross(False) self.drawPolygon() @@ -1670,7 +1693,8 @@ class Dimension(Creator): self.point2 = self.node[1] if (len(self.node) == 1): self.dimtrack.on() - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) elif (len(self.node) == 2) and self.cont: self.node.append(self.cont) self.createObject() @@ -1705,73 +1729,18 @@ class Dimension(Creator): # Modifier functions #--------------------------------------------------------------------------- -class Modifier: +class Modifier(DraftTool): "A generic Modifier Tool, used by modification tools such as move" - - def __init__(self): - self.commitList = [] + def __init__(self): + DraftTool.__init__(self) + def IsActive(self): if Draft.getSelection(): return True else: return False - def Activated(self,name="None"): - if FreeCAD.activeDraftCommand: - FreeCAD.activeDraftCommand.finish() - global Part, DraftGeomUtils - import Part, DraftGeomUtils - self.ui = None - self.call = None - self.commitList = [] - self.doc = FreeCAD.ActiveDocument - if not self.doc: - self.finish() - else: - FreeCAD.activeDraftCommand = self - self.view = Draft.get3DView() - self.ui = FreeCADGui.draftToolBar - FreeCADGui.draftToolBar.show() - rot = self.view.getCameraNode().getField("orientation").getValue() - upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) - plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) - self.node = [] - self.ui.sourceCmd = self - self.constrain = None - self.obj = None - self.extendedCopy = False - self.ui.setTitle(name) - self.featureName = name - #self.snap = snapTracker() - #self.extsnap = lineTracker(dotted=True) - self.planetrack = PlaneTracker() - - def finish(self): - self.node = [] - #self.snap.finalize() - #self.extsnap.finalize() - FreeCAD.activeDraftCommand = None - if self.ui: - self.ui.offUi() - self.ui.sourceCmd=None - self.ui.cross(False) - msg("") - self.planetrack.finalize() - FreeCADGui.Snapper.off() - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - self.call = None - if self.commitList: - todo.delayCommit(self.commitList) - self.commitList = [] - - def commit(self,name,func): - "stores actions to be committed to the FreeCAD document" - # print "committing" - self.commitList.append((name,func)) - - class Move(Modifier): "The Draft_Move FreeCAD command definition" @@ -1870,7 +1839,8 @@ class Move(Modifier): self.ghost.on() self.linetrack.p1(point) msg(translate("draft", "Pick end point:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) else: last = self.node[0] if self.ui.isCopy.isChecked() or hasMod(arg,MODALT): @@ -2085,7 +2055,8 @@ class Rotate(Modifier): self.linetrack.on() self.step = 1 msg(translate("draft", "Pick base angle:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) elif (self.step == 1): self.ui.labelRadius.setText("Rotation") self.rad = DraftVecUtils.dist(point,self.center) @@ -2201,7 +2172,8 @@ class Offset(Modifier): self.call = self.view.addEventCallback("SoEvent",self.action) msg(translate("draft", "Pick distance:\n")) self.ui.cross(True) - self.planetrack.set(self.shape.Vertexes[0].Point) + if self.planetrack: + self.planetrack.set(self.shape.Vertexes[0].Point) self.running = True def action(self,arg): @@ -3293,7 +3265,8 @@ class Edit(Modifier): plane.save() if "Shape" in self.obj.PropertiesList: plane.alignToFace(self.obj.Shape) - self.planetrack.set(self.editpoints[0]) + if self.planetrack: + self.planetrack.set(self.editpoints[0]) else: msg(translate("draft", "This object type is not editable\n"),'warning') self.finish() diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index ab556e095c..47e378a348 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -81,6 +81,22 @@ class Tracker: self.switch.whichChild = -1 self.Visible = False + def lowerTracker(self): + '''lowers the tracker to the bottom of the scenegraph, so + it doesn't obscure the other objects''' + if self.switch: + sg=Draft.get3DView().getSceneGraph() + sg.removeChild(self.switch) + sg.addChild(self.switch) + + def raiseTracker(self): + '''raises the tracker to the top of the scenegraph, so + it obscures the other objects''' + if self.switch: + sg=Draft.get3DView().getSceneGraph() + sg.removeChild(self.switch) + sg.insertChild(self.switch,0) + class snapTracker(Tracker): "A Snap Mark tracker, used by tools that support snapping" def __init__(self): diff --git a/src/Mod/Draft/Draft_rc.py b/src/Mod/Draft/Draft_rc.py index e74a2fc93b..4028d367e7 100644 --- a/src/Mod/Draft/Draft_rc.py +++ b/src/Mod/Draft/Draft_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Tue Jun 5 20:09:57 2012 +# Created: Wed Jun 6 16:21:23 2012 # by: The Resource Compiler for PyQt (Qt v4.8.1) # # WARNING! All changes made in this file will be lost! @@ -117,7 +117,7 @@ qt_resource_data = "\ \x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\ \x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ \ -\x00\x00\x08\xd7\ +\x00\x00\x08\xd6\ \x3c\ \x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ \x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ @@ -257,10 +257,10 @@ qt_resource_data = "\ \x20\x31\x20\x2d\x31\x2e\x30\x37\x31\x34\x32\x39\x2c\x30\x20\x30\ \x2e\x35\x33\x35\x37\x31\x34\x32\x37\x2c\x30\x2e\x35\x33\x35\x37\ \x31\x34\x32\x37\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x30\x37\x31\ -\x34\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x0a\x20\x20\x20\x20\ -\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\ -\x74\x65\x72\x6e\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\ -\x3c\x2f\x73\x76\x67\x3e\ +\x34\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\ +\x65\x72\x6e\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\ +\x2f\x73\x76\x67\x3e\ \x00\x00\x74\xe7\ \x3c\ \xb8\x64\x18\xca\xef\x9c\x95\xcd\x21\x1c\xbf\x60\xa1\xbd\xdd\x42\ @@ -26767,258 +26767,263 @@ qt_resource_data = "\ \x33\x2e\x72\x9f\xc2\xcc\xa8\x77\x7d\x66\xc6\x1b\xcc\xef\xf2\x48\ \xa6\x8c\x0e\x28\xa3\x2d\x9e\x88\x63\x66\x74\x00\x9f\x1b\xd5\x84\ \x37\x0f\xfe\x01\xbd\x89\x17\xfc\ -\x00\x00\x0f\x9f\ +\x00\x00\x0f\xe1\ \x00\ -\x00\xa2\x2c\x78\x9c\xed\x1d\x6b\x6f\xdb\x38\xf2\x7b\x7e\x05\x91\ -\x0f\x45\x0e\xc8\xc6\xb1\xf3\x6e\x1d\x2f\xda\xf4\x09\x74\x77\xbb\ -\x75\xda\xbd\xbd\x2f\x0b\x5a\xa2\x6d\x5e\x65\xd1\x4b\xd2\x49\xbc\ -\xb8\x1f\x7f\x33\xa4\x64\x4b\xb2\x2c\xc7\xa6\x65\x39\xad\x8b\x02\ -\xb1\x48\x8a\x1c\x0e\xe7\xc5\x99\x21\xd5\xfc\xf9\x61\x10\x90\x3b\ -\x26\x15\x17\xe1\xf5\x7e\xfd\xe8\x78\x9f\xb0\xd0\x13\x3e\x0f\x7b\ -\xd7\xfb\x5f\x6e\xdf\xfe\x74\xb9\xff\x73\x6b\xaf\x39\xe2\xd3\x46\ -\xa7\xd0\xa8\xb5\x47\x9a\x5e\x40\x95\x6a\xbd\x1b\xf1\xe7\xcf\x5f\ -\x73\x1a\x88\x1e\xfc\x0d\x7a\x6d\xa6\x35\xbc\xac\x5e\x4b\xda\xd5\ -\xcd\x9a\x6d\x04\xad\xef\xb9\xdf\x63\x9a\x98\xe7\xeb\xfd\xdf\xff\ -\x30\x8f\xfb\x24\xa4\x03\x76\xbd\x5f\xd8\x09\x0e\x46\x9a\x43\x29\ -\x86\x4c\xea\x71\xf4\x46\x8f\x89\x01\xd3\x72\x6c\x2a\x49\x53\x32\ -\x4f\x9b\x5f\xa4\xf9\xd0\x3a\x6e\xd6\x1e\xa2\x87\x31\x3e\x8c\xa3\ -\x07\x00\x41\xf7\x5b\x67\x57\x50\x64\x7f\xda\xe2\x3e\xe3\xbd\xbe\ -\x6e\x9d\x9f\x34\x9a\xb5\xe8\xb7\xe9\xb3\x16\x77\xda\xac\xc5\x83\ -\xe7\x41\x72\xcf\x43\x5f\xdc\xdf\x72\x1d\xb0\x08\x18\xa5\x25\x00\ -\xdf\x7a\xc7\x42\x26\x69\x40\x54\x34\x99\x66\x2d\xaa\x98\xed\x32\ -\xa0\x63\x31\x9a\x22\xe7\xeb\x2b\xf1\xf0\xd1\x14\x45\x3d\x66\x86\ -\x54\x43\xea\x41\x47\xfb\xd1\x04\xc2\xd1\xa0\xc3\x64\xeb\xbc\x59\ -\x8b\x7e\x59\xf0\x93\x23\xcc\x74\x31\xa0\xb2\xc7\xc3\x4c\x0f\x57\ -\x85\x3d\x70\xcd\x06\x53\x4c\x26\x17\xf3\x9d\x14\xa3\x21\xc0\x1c\ -\x2f\x67\x2f\x7e\xb6\xcd\x67\x06\xd7\x53\x64\xe5\xe0\xcb\x2c\x3a\ -\x69\xe7\x60\x6d\x16\xa8\x42\xdc\x45\xa3\x01\xe1\x6a\xee\xd1\xc0\ -\x96\xfe\xd5\x98\x0e\x3c\x9d\x51\x4e\x47\xef\x67\x3a\xea\x0b\xc9\ -\xff\x11\xa1\x9e\x74\x55\xbf\x9a\xf4\x95\xed\x6d\x16\x49\x1f\x69\ -\x87\x05\x71\x57\x01\x3e\xa4\xdf\xcf\x41\x13\x7b\xd0\xa9\x06\x13\ -\x54\x59\x14\xf1\x50\x33\xd9\xa5\x1e\x23\x03\xe1\xb3\x0c\xa2\xf2\ -\xb1\x65\x0b\x2d\x64\x09\xd0\x6b\x69\xd8\x17\x4c\xc5\x70\xeb\x27\ -\xc9\xba\x37\x62\xd0\x11\xc9\x75\xc7\x8a\x21\x54\x78\x58\xd1\x11\ -\x0f\x7f\x9d\x16\x4f\x50\x88\xe0\x96\x0f\xf3\xe7\x78\xdb\xe7\x8a\ -\xc0\x7f\xdd\x67\xe4\xcb\x07\x33\x45\x98\x31\xb9\xef\x73\xaf\x6f\ -\x0a\x2d\x12\xa0\x7c\x14\x30\x72\xcf\x83\x80\xdc\x0b\xf9\xed\x39\ -\xb9\x85\x5e\x3b\x54\xda\x37\x4c\xf9\x30\x40\x24\xd1\x20\xa6\xad\ -\x98\x23\xb1\x3f\x0a\x4f\x43\x2a\xa9\x66\x44\xdb\x17\x0f\x71\x0c\ -\xe8\x52\x53\xf5\x2d\xdd\xcf\x48\x31\x33\xf2\x5b\xc9\xd8\xcd\xcb\ -\xd7\xe4\x16\x5a\xdc\x71\x76\x4f\xd4\x58\x01\xc6\x48\x57\x48\x33\ -\x0a\xd7\x0a\xdb\x4a\xbb\x42\xd4\xd3\x20\x37\x1f\xbd\x3c\x33\x58\ -\x42\x84\xbe\x09\x51\xd6\x11\xa5\x7d\x80\xfd\x7a\xff\x38\x83\x32\ -\x2f\xea\xfb\x0b\xff\xc5\x50\x82\xe7\x32\xd6\x27\xaa\xfb\x8b\x87\ -\x82\x81\x6a\xb1\x8c\x7f\xf4\x68\x19\xba\x7a\x0c\xc9\x4f\xe9\xc1\ -\xae\x4e\x1e\x1e\xe7\x0d\x57\xcb\x8e\xe7\x06\x40\xb4\xda\x2e\x10\ -\x2c\xe4\xbd\x66\xcd\x8a\xa1\x89\x8c\x4a\x55\x3b\x4b\xac\x86\x9b\ -\xc0\x6a\xac\x2a\xaf\x58\x97\x8e\x02\xe8\x5a\x04\x22\x77\x05\x4b\ -\x17\x54\x30\xee\xab\x91\xd6\x22\xcc\x91\x55\x50\xd7\xb1\x75\x2b\ -\x0b\x2b\x94\x0a\x7e\x72\x92\x46\x16\x84\x20\x1a\x44\xe7\xbf\x60\ -\x46\x64\xd5\x58\x11\xcd\x64\xc6\x35\xdd\x65\x99\x10\xcb\x32\x54\ -\x2a\x99\x8f\xc6\x0e\xfe\x49\x57\xf4\x40\x5a\x85\x58\x65\x7f\xa4\ -\x2b\x3b\xc1\x88\x61\x9d\xf9\x9b\x26\xe8\x99\x41\xd6\x2e\xae\x22\ -\x72\xd8\x4e\x69\xe5\x4c\x7d\x79\x0c\xb4\x40\x1f\xce\x65\xa0\x76\ -\x48\x87\xdb\xce\x3d\x8b\xa4\xc3\xf2\xfc\xa3\x70\xd6\x6a\x0c\xb6\ -\x44\xb0\x63\xa0\x14\xf8\x11\x2e\x10\x41\x3f\x1e\x13\xad\x6a\x35\ -\xdf\x88\x10\x7e\x8d\x8c\x39\xb6\xf5\xcc\x74\xb2\x1e\xcb\x79\x96\ -\xa9\x22\x85\x44\x3a\x0c\xda\x12\x5f\xd2\xfb\x30\x32\x78\x39\x62\ -\x25\x81\x22\x34\x7c\x8f\x36\xc3\x77\xa7\xa7\xf3\x19\xaf\xde\x38\ -\x2b\x60\xbd\xc6\xd9\x59\x65\xda\x6b\x8a\xab\x1f\x8f\x09\x17\xd0\ -\xe7\x42\x53\x30\xe0\x60\x1d\xa1\x17\xa6\x02\x1e\x6c\x0f\x79\x98\ -\xb7\x6d\x55\x50\xde\x99\xba\x2d\xf2\x67\xf6\x58\x35\x36\x99\xe0\ -\xfa\x4c\xc1\x3b\x0a\x74\x9e\x19\x39\x72\xd8\x34\x52\xae\x9b\xa5\ -\x7a\x5d\x82\xe4\x13\x8b\xf6\x44\x49\xbd\xe4\xfd\xd5\x89\xe3\xfe\ -\xea\xd8\x49\xb5\x51\x6e\x84\xf6\x56\x7a\x82\xce\x5c\xf4\x19\x23\ -\x93\x09\xa2\xd2\x82\x39\xf2\x2e\x67\x92\x7c\x63\xe3\x8d\xf8\x55\ -\x60\x40\x2f\x06\x60\x5b\x49\xdf\xcd\xb9\xa1\xfa\x1c\x07\xab\xca\ -\xb7\xe2\x69\x19\x54\x37\x3a\x0d\x9c\xa6\xbe\x02\x1b\xa1\x07\x1f\ -\xe8\x37\x2b\x45\xda\xa6\x78\x01\xaf\x40\x6b\x06\x8d\xd1\xec\xc8\ -\x50\x0f\x03\x15\xd0\xfa\x5d\x3f\x7f\xfe\x7e\xd2\x63\xb3\x66\x0a\ -\x97\x26\x55\xc5\xff\x61\xef\x79\xa8\xe7\x93\x2a\xb6\xc8\xa0\xd3\ -\x46\x55\x4e\xd3\xf1\x95\x49\x6d\x14\x5b\x69\x1c\xa7\xc2\x2c\x53\ -\xb0\xb2\x1d\xce\x11\x59\x16\x75\x6b\xb5\x65\x1a\x75\x97\x6d\xf9\ -\xb6\x4a\xdc\x73\x37\x89\xab\xa2\xb9\xad\x41\xd2\x7a\x23\x29\x81\ -\x64\x3f\x84\x3e\x7b\xc8\x37\x5f\xea\x1b\x31\x5f\x60\x36\x38\xab\ -\x9d\x04\xb7\xa5\x3b\x09\x3e\x2d\x58\xb3\x04\x2f\xe6\xbd\x9d\x04\ -\x5f\xbb\x04\x5f\x35\x32\xf1\x32\xd0\x5b\x2b\xc0\x2f\xdc\x04\x38\ -\xb5\x53\xdb\x88\xfc\xde\xcc\xf6\x13\x66\x63\x18\x7d\x27\xbe\x77\ -\xe2\x7b\x9b\xa2\xaa\xf5\x24\x15\xad\x92\x07\xe2\xb6\xed\x8f\xdc\ -\xb5\x26\xeb\xc6\xbc\x52\x81\x34\xfb\xc8\x43\xf6\xc6\xe7\x7a\x46\ -\x9a\xa1\xcb\x88\x41\x85\x4b\x74\x28\xcf\xa1\x3d\x9d\xad\xf1\xaf\ -\xa5\x1c\xd7\x71\x72\xd8\xea\x12\xef\x71\x28\xdf\x88\xef\x21\x39\ -\x33\x33\x69\xbb\xc2\xdb\x29\x03\xab\xe6\xc4\xd3\xc7\x33\xe2\x54\ -\x11\xf7\x99\xf7\x2d\x57\x11\x63\x85\x93\x3f\xd8\xf4\x00\x64\x8b\ -\xe4\xdb\x25\x00\x21\xb9\xa7\xa1\x26\x5a\x4c\x52\x89\x4c\xe0\xa0\ -\x96\xf0\x15\x4b\x31\x30\x15\x51\x32\x12\xa1\x2a\x26\xf9\x32\xa8\ -\xb9\x4d\xef\x00\x06\xab\xde\xa3\x18\x11\x0d\xfd\x84\xef\x9a\x7a\ -\x52\x28\x45\x14\x53\x98\xfc\xe9\xe0\xbb\x5e\x26\xaa\x09\x40\x89\ -\x90\x3d\xf0\xad\x55\xf5\x55\x93\xf9\x45\x19\x64\xee\xe2\xa2\xfd\ -\xd0\x8d\xa8\x5c\x11\xd3\x1d\xf3\x0f\x81\x9c\x86\xe3\x44\xf2\x5c\ -\x87\x81\x11\x3a\xd4\x31\x45\x81\x95\x3b\x00\x52\x3b\x24\x02\xa8\ -\x5d\xde\x73\xc5\xe2\x22\x65\xdb\xd3\xe0\x9e\x8e\x81\xf2\x34\x95\ -\x98\xea\x48\x42\xf1\xd3\xa4\xc7\x32\x58\xe1\x5d\x20\x3a\x34\x20\ -\x6b\x18\x23\xc2\x40\x66\x98\x0e\xe0\xaf\x05\x82\x1c\x3a\x36\x3f\ -\xcb\xd5\x19\xc3\xf1\x60\x8b\x33\x01\xab\xe6\x9f\xfa\x65\x29\x0c\ -\xe4\x10\xb3\xff\x55\xc8\x01\x0d\x82\xf1\x21\x01\x44\x32\x69\xc8\ -\x10\x43\x1d\x51\xcc\xf0\x30\x52\x16\x43\xce\x14\x41\xe0\x14\x0b\ -\xa0\x9c\xf9\x47\x24\x66\x3d\x31\x34\xb6\x4f\x92\x03\xf1\x9d\x0e\ -\x05\xce\x8a\x63\xfe\x31\x23\xc6\x6f\x03\x5f\x29\xcd\xa8\xef\x10\ -\xe5\x2f\xd0\x2d\x66\x90\x34\x00\xa9\xc9\x6d\x46\x99\x18\x28\x5e\ -\x01\x10\xbf\xc5\xe1\xd7\x1d\x4f\xe4\xf1\xc4\x59\x29\x2c\x71\xb9\ -\x5e\x9d\x62\x4c\xa8\xa4\x76\x40\x27\x35\x98\x53\x60\x2c\x28\x9d\ -\xe0\x97\x28\xa3\x05\xb3\x5b\xa0\xd4\x30\x49\x28\x74\xe2\xfd\x8e\ -\x75\x70\x0f\xcd\x3b\x61\x30\x86\x17\x58\x48\x00\x74\xb0\x73\xa0\ -\xe8\xe6\xf6\xf3\xc7\x52\x98\xe2\x65\x1a\xee\x18\xdc\x03\x9f\x2b\ -\xda\x09\xa6\x5e\x77\x74\xd8\xfc\xeb\x29\xab\x20\xbb\x40\xed\x2d\ -\x76\xb7\xaf\xdd\xe9\x5b\xbc\xc3\xdd\x39\x7d\xd7\xed\xf4\xad\x3b\ -\x3a\x48\x25\xf5\xf9\x48\x4d\x32\x4d\x8d\x2c\x00\x9e\x54\x43\xe6\ -\x71\xb0\x04\x87\x02\x10\xa9\x8e\xf0\x24\x12\x16\x1f\xdb\x3c\x1e\ -\x41\x80\x55\x35\x0d\x3d\x46\x0e\x78\xd8\xe5\x21\xc0\xed\xc0\xa8\ -\x0b\x62\x8b\x92\x86\xbd\x2a\xdc\x39\x0b\x32\xa4\x16\x90\xba\x1a\ -\x75\xbb\x3c\xeb\x29\x8e\xe6\x30\x7c\xd8\x8c\xda\x07\xec\x7d\xb6\ -\xc8\x7b\xa2\xd2\xa7\xec\x93\x20\x4b\xf8\x2c\x97\xd0\xf7\x0e\x07\ -\xbe\xf2\xf4\xfd\x84\x2f\x8d\xd2\x06\x15\x4e\x84\xe7\x8d\x24\xa1\ -\x3d\x8a\xa6\x6b\xc2\xb0\xd5\x7d\x50\x9b\x12\x9d\x2a\x34\x34\x86\ -\x2f\x0f\x7d\xee\x51\x34\x72\x6d\x64\x82\x88\x2e\x61\x30\x2b\x07\ -\x1f\xc6\xa2\x44\x00\xfa\xc0\x07\xa3\x41\x29\x5a\xbb\x4b\x03\xb5\ -\x09\xb5\x0d\x73\xf8\x91\x74\xf6\x4e\x63\xcf\xc7\x74\x19\x61\xda\ -\xe2\x1d\x32\x0b\xd1\x06\x5e\x3f\xfd\x17\x8a\x9d\x5f\x2c\xd7\x66\ -\xa5\x04\xaa\xfc\x0e\x33\x71\x05\xee\x33\x09\x62\x24\x69\x27\x94\ -\x21\x43\xe6\x00\xb2\x85\xda\xbf\x38\xbb\xa1\xac\x55\x8c\xc4\x6b\ -\x7e\x00\xfa\xea\xea\x6a\xf5\x10\x74\x51\x5e\xf5\xe9\x66\x22\xdb\ -\x56\xec\xbe\xb1\x0b\xfe\x44\x65\x6f\xd9\x16\x4b\xbd\x14\x8b\xc5\ -\x61\x13\x01\x16\x4b\xca\xd7\xd6\xb6\xbb\x7a\x1b\xb8\x99\xb8\x19\ -\xfa\xc2\x9c\xb0\x61\x21\xbb\x03\xb6\x46\x0f\x04\x06\x7f\xca\x94\ -\x24\x6d\x18\x32\xbe\x46\x21\x01\xd2\x53\x76\x27\x20\x16\x71\x2e\ -\xaf\xe8\xf7\x7b\xcc\x66\x09\xa2\x5d\xf5\x08\xdc\x7b\xd0\x65\xf1\ -\x25\x08\x49\x62\xb5\x7e\xd9\x91\x72\x88\x7a\x2c\xb1\x98\x7d\x80\ -\xe2\xa9\x2f\x66\xc9\xc2\xae\x9c\x00\x85\x43\x4a\x70\xde\xee\x2c\ -\x15\x56\x00\x71\xc6\x6c\xbc\xba\x0b\x8f\x60\x2f\x4d\x23\xd7\x47\ -\xe4\xb7\x38\xc8\x67\x04\xe5\x38\xfb\xc6\x3d\x07\x28\xe5\x32\x99\ -\x2b\x4b\xd0\xfc\x5b\x1c\x6b\x72\xea\x71\xec\x1e\x4e\x5f\x82\xd2\ -\x11\x15\xbb\x40\xdc\x7c\x32\x77\xbd\x40\xc7\xf5\x42\x0a\xbc\x31\ -\x06\xdd\x0b\xc3\x80\x86\xdb\x79\x83\x4e\xf1\x0c\x97\x20\xc5\x88\ -\xec\xff\xf8\xb4\xad\xb4\xe8\x96\x42\xf8\xab\xc8\x5f\xc0\xcd\x24\ -\x30\xfe\xfb\x4f\x72\x70\x2b\x86\xb9\x8e\xe0\x0d\x41\xf0\x1f\x72\ -\xf0\x56\x02\x6f\x55\x08\xc3\x9f\x00\x43\x1b\x74\xbb\x13\x08\x55\ -\x8b\xa4\x7a\x39\xc9\x35\x0e\xe9\x8f\xc9\x6d\x06\x25\x3d\xc9\xfd\ -\x94\xfe\x34\x91\xcb\x28\xd2\x59\x86\xfe\xfc\x02\xbb\x15\x1c\xf4\ -\x49\xfb\x35\xed\x04\xb6\x53\xf0\xad\x3d\x10\x59\xbc\xa5\xdd\xb9\ -\x35\x5d\xdc\x9a\x4b\xb0\xfc\x82\xb4\xee\x4a\xbc\x9d\x59\x97\x85\ -\xdd\x0b\x26\x64\x8a\xcd\x4f\xe8\x30\x72\xc7\x15\xc7\x8c\x04\x23\ -\x5e\xa6\x4d\xd1\x62\xea\xb0\xd0\xeb\xe3\x4e\x00\xaf\xb6\xbb\x63\ -\x09\xf3\x3e\x91\x4b\x31\x32\x89\x14\x34\xce\xea\x2b\x33\x99\xa2\ -\x2f\x72\x2f\x65\x2b\x2b\xa7\x01\x86\x7b\xf7\x7d\x89\x93\x47\x58\ -\xda\xc5\xc9\xa1\xa5\xd1\xf2\xfc\x74\x4d\x24\xd9\xe8\x22\xd4\x0a\ -\x0c\xf7\xd7\x62\x04\x13\x9e\xe7\x28\xf7\x4d\x6d\xec\x2e\xdf\xc2\ -\xa0\x87\x39\xe0\x6b\x91\x07\xbc\xae\xef\x19\x30\x2c\xa3\xc0\xd4\ -\x46\x12\x60\xfa\xf7\xea\x0c\xe5\x33\x8f\x0f\x00\xf2\x7c\x87\xba\ -\x83\x3f\x3d\x3f\x00\x60\x71\x8d\x01\x80\xab\xa3\xab\xcb\xab\xc9\ -\xbf\x8b\xcb\x46\xb3\x16\x55\x2e\x3d\x54\x5e\x44\x20\xea\xab\x7e\ -\x74\x9c\xfe\xb7\xfa\x28\x4b\xda\x2f\xed\x98\xda\x7f\x24\xb9\x53\ -\x4d\xac\xa9\x28\x4a\xc7\x43\xc3\x20\x8a\xa0\x3f\xff\xf1\x67\x8c\ -\x36\x17\x9e\x2b\xce\xc1\xa8\x28\xc8\xca\x43\x8b\xb4\x38\x2c\x62\ -\x2f\x1e\xd3\x7d\x0e\xb6\x88\x3c\x22\x6d\x4c\xba\xea\x8e\x09\xd8\ -\x11\x8c\x60\xf8\x02\x6c\x86\x31\x51\x7f\x8f\xa8\x64\x6a\x22\xa2\ -\x06\x71\x37\x0e\xb9\x99\x05\xc1\xbe\xfa\xf1\x46\x82\x7d\xc8\xcb\ -\x6f\x2c\xed\x3c\x51\x4e\x2e\x7b\x0b\xee\x78\x8d\xd2\x82\xa4\xe3\ -\xb9\xbc\xfd\x01\xaf\x6b\x0e\x31\xf5\x4f\x02\x35\xe2\x81\x27\x12\ -\x00\x93\xe7\x1e\x8c\xad\x98\xc7\x8b\x6d\x8a\x48\x53\xb6\x61\x47\ -\xf4\xd8\x5d\xd7\x79\xf1\xae\xab\x7e\x7e\x71\x71\xd1\xa8\x9f\xb9\ -\xec\xbd\x96\x37\x51\xa6\xa9\x10\xb1\x51\x81\x27\x81\x78\xbc\x4c\ -\x9e\x10\xd2\xe7\x21\xd5\x0c\x8f\x3f\x30\x69\xf6\xb4\x8a\x1c\x60\ -\xa6\x06\x7b\x38\x22\x27\xe4\x9a\x1c\x83\xba\xae\x3b\x24\x68\x16\ -\x88\x8b\xf3\x8d\x48\x8b\x09\x2d\x3e\x59\x69\xb1\xac\xfb\xa2\x58\ -\x7f\xed\xdc\x17\x6b\xcf\xa3\x5e\x55\x5e\xbe\xe6\x03\x16\x9a\x73\ -\xa1\x4f\x40\x62\x16\x6f\x60\x0b\x93\x8b\x2e\xcb\x49\x2d\xda\x88\ -\xf8\xf0\xf9\xe0\xd3\x93\x97\x20\x65\xdb\x1b\x4b\x9c\x7d\xca\x63\ -\xa0\x05\x29\x44\x73\xf9\xe7\x56\x04\xa0\xb4\x42\xaf\xd4\xc8\x63\ -\xb1\xac\x2d\x66\xfd\x9d\xac\x5d\x8b\xab\x78\x09\x67\xd1\x0f\x60\ -\xd6\x4d\xef\xf2\x30\xc2\x11\xb3\x8e\x7c\xcc\xcb\xe8\x8e\x42\xcf\ -\x1a\x70\xba\x4f\xb5\x49\x92\xa3\x44\xc7\x2c\x72\xb4\xf7\x15\x5b\ -\x47\xa9\xfe\x3e\xef\x76\x61\xa7\x08\xe5\xb8\x39\x0c\x60\xbf\x68\ -\x92\x53\x6c\x87\xf1\x16\x53\x4b\x66\xf2\xff\xa9\x22\x0a\x00\x73\ -\xd8\x33\x16\x1c\x68\xa9\xad\x42\xf0\x61\x0f\xc8\x41\xb3\x2c\x8a\ -\x22\x3f\x12\x98\xac\xf5\x35\x79\x98\x0a\xfc\x58\x30\xca\xd9\xe6\ -\xfd\x58\x7a\x2a\xf2\x76\xba\x28\x4f\x17\x9d\xbb\xa9\xa2\x05\x19\ -\x5e\x0b\x13\x62\xb0\x05\xb1\xb2\xa0\x3a\xa5\x54\x9c\xdd\xb8\x53\ -\x4a\x9b\x56\x4a\x0b\x92\x17\xbe\x07\xb5\x14\xd3\xbf\x1d\xcf\x9c\ -\xf5\x40\x56\x50\xe6\x3a\x1c\x7f\xb2\xcf\x59\xaf\x1f\x61\x22\x89\ -\x1b\x15\x44\x14\x70\x7a\x31\xa3\xef\x44\x71\xee\xb6\xc0\xf1\x6b\ -\x59\x0b\xee\xd3\x79\x9c\x2c\xee\x02\x44\xd5\x49\xe2\x62\x37\xe3\ -\x4e\x12\xaf\x45\x12\x2f\xbc\x3b\x6f\xf5\xd4\xb1\xbc\x9b\xf3\x90\ -\xa2\xcc\xbb\x93\xef\x15\xda\xcc\x8e\x3c\x79\x77\xb4\xf7\x01\xe0\ -\xa5\x21\x9a\xd3\x34\xf1\xa6\x1a\x79\x7d\xb4\xab\x9f\xfd\x3d\x12\ -\xfa\xc5\x4b\xc9\x69\x60\x7f\x62\x86\x5a\x3c\x90\xd2\xe3\x20\xdb\ -\x54\xd1\x50\xc5\x2d\xa3\x12\x26\x79\xd7\xfe\xdc\x03\x78\xec\xaf\ -\x81\x08\x45\xdc\x0c\x81\x24\x5d\x3a\xe0\xc1\x38\x6f\xdc\xc3\xf7\ -\x2c\xb8\x63\xf8\x85\xcf\xc3\x69\xe7\xf6\x25\x03\xaa\xd9\x2f\x50\ -\x0b\xcc\x5e\xce\xfb\xcf\x5f\x89\xc0\xb7\xcf\xa5\xe4\xa8\xe0\x18\ -\xab\x77\x4c\x03\xde\x0b\x61\x35\x66\x7a\x07\x26\x47\xfe\x7a\x89\ -\xf5\x9f\x91\x86\xff\x37\x79\xbc\x95\x94\x03\xe9\xf4\xa6\x25\x5f\ -\x6f\x18\xfa\xcc\x01\x0c\xa6\x97\x87\x61\x49\xa5\x62\x25\xd6\x4e\ -\xa5\xe4\xaa\x94\x25\xce\xb0\xe5\xba\x6a\x57\xbd\xa9\x7e\xaa\x53\ -\x06\xc3\x00\x3f\x76\xaa\xfa\x8c\x55\xa8\x58\x16\x4c\x64\xa7\x59\ -\xd6\xa2\x59\xde\xf2\x80\xdd\xf4\x85\x00\x19\x3b\xa3\x5c\xba\x50\ -\xe7\xd9\xba\x45\xf6\x3d\x0f\x97\xb5\xef\x4f\x8e\x8b\x11\xe4\x84\ -\x9f\xe5\xe3\x88\x7e\x96\xfa\xa3\x1b\x38\x4d\xa2\xa2\x87\x6e\x22\ -\x9b\xab\x88\x5f\x69\x8a\x32\xa9\x97\x64\x10\x37\xa1\x69\xa1\xda\ -\x09\xcd\x39\x42\x73\x89\x5b\x5d\x73\x84\xa6\x7b\x70\xeb\x19\x1d\ -\x0c\x5f\x90\x8f\x8c\xfa\x20\xd0\xa8\x94\xe2\xde\x9a\x13\xdb\x78\ -\x60\x68\x6d\xc7\x85\xf8\xc0\x7e\x79\x73\x5b\xa9\xd2\xed\xa8\xca\ -\x6b\xa1\xc9\x59\x75\x27\x65\x70\xf8\x8b\x6a\x87\xbf\xaa\x6e\xf8\ -\x1b\x2e\x3d\xd8\x19\x54\x88\xff\x08\x82\x0a\x97\x20\x82\xa0\xc2\ -\x55\x68\x83\x68\xe9\x57\xb9\x08\x16\x80\x0a\xd7\xc0\x02\x50\xe1\ -\x12\xbc\xa2\xde\x37\x55\xf5\x32\x4c\x81\xa8\x70\x29\xa6\x40\x38\ -\x2d\x47\xe5\xa6\x8a\x63\xf8\xa6\x5e\x9c\xfb\x34\xdf\x5a\xf9\x0a\ -\xad\xb8\x47\x83\x84\xef\xc6\xfa\x0f\x13\x3b\xa9\x6d\x34\x57\xd6\ -\xf5\x9d\xe3\xc4\x34\x31\x5d\xd0\xf8\xbb\x62\x4c\x44\x9e\xad\xe8\ -\x20\x13\x6c\x78\x92\x48\xa2\x92\x91\xbb\x08\x79\x47\x24\xde\x26\ -\x43\xaf\x01\xeb\xea\x43\xbc\x47\xd6\x1e\x75\xc2\x1e\x3f\xb4\x7f\ -\xc3\xfb\xc8\x43\x9f\x4a\x97\x2b\x93\x97\xb3\xc2\x52\x0b\xf8\x3d\ -\x9a\x62\x1f\x01\xd1\xe4\x20\x89\xdb\x0a\x8f\x30\x7f\x9e\x17\xf9\ -\x7c\x22\x12\xc8\x51\xfc\xac\xfa\x55\xe7\x97\x81\x49\xc9\x85\x9d\ -\x75\xfb\xeb\x3b\x02\x84\x86\x8f\xc0\x43\xc2\x2b\x5d\xf2\x2c\xf0\ -\x33\x15\x9f\xc1\xdc\xf9\x99\x36\xea\x67\x5a\x90\xb5\xfe\xa4\xfd\ -\x4c\xef\xf1\xe4\x0a\xde\xee\x85\xe1\x12\x15\x1d\x67\xa1\xa0\x6a\ -\x24\xf3\xb4\x90\x63\xbc\x4a\x50\x47\x1f\x30\x46\x26\x41\xbc\xa8\ -\x64\xe1\xb3\x40\xbf\x18\x5a\xce\x79\xd6\xd3\x2f\xd0\x69\x85\xb7\ -\x0b\x4f\x33\xa2\xe2\x38\x8c\xef\x33\x1f\x3d\x58\xa8\x92\x62\x91\ -\x19\xc5\x70\xa0\x19\xa8\xab\xa8\x97\xcd\x7c\xad\x25\x1a\x0c\x49\ -\x60\x5b\xf5\x93\x9b\x4c\x4e\x57\xa6\xfa\x9a\xb6\x4b\x34\x4a\xb4\ -\x88\xe4\xf7\x24\x20\x66\x0f\xf0\x5d\xef\x9f\xef\x93\x01\x95\x3d\ -\x1e\x5e\xef\xd7\xeb\xfb\x98\x42\xd6\x1c\xf2\x87\x01\x1d\xc6\x39\ -\x70\xad\xbf\x3f\x99\xe7\xb7\x52\x0c\x7e\x01\x63\xa5\x2d\x46\x12\ -\x73\xa6\x32\xad\xe0\x3d\x6f\xa4\xb4\x18\xd8\x11\x95\x81\x24\x59\ -\x62\xa1\x34\xec\xda\x32\xdc\x9a\xe0\x54\xc0\x9f\x29\x37\x4d\x40\ -\xb2\xb3\xd0\x57\xad\xdf\xff\x30\xef\x81\xa8\x8b\x0a\xf6\x2c\x37\ -\xa1\xeb\x0b\x7b\xa8\x61\x07\xaf\x39\x0d\x44\xef\xa8\x8f\xcc\x65\ -\x2a\x0c\x02\xb2\xe3\x16\x03\x72\x83\x1f\x1b\x7a\x35\xd2\xda\x98\ -\x35\x39\x80\x7c\x1a\xa9\x7e\x5c\x3f\x0f\x18\x0b\xac\x72\x83\x24\ -\x23\xc0\x72\xa1\x99\xc5\xdd\x3c\x90\xb0\xb7\xb5\x81\x15\x65\xe7\ -\xe4\x23\x68\x52\xb9\x11\x50\x16\xad\xd7\xec\xa2\x6e\x06\xac\xe8\ -\x16\x86\x7c\x14\x4d\x6b\x37\x84\x23\xbb\xdb\x99\x03\xcc\xa4\x76\ -\x23\xc0\xc4\x59\x05\xf9\xc0\x4c\x6b\x37\x02\x4c\x2a\xd9\x2c\x1f\ -\xa2\x4c\x13\x77\xb0\xd2\x05\xd8\x53\x53\x32\x65\x04\xa9\x32\x22\ -\x17\x74\x6f\xc8\x6c\xca\xb1\x9d\xc5\xe4\xd9\x8e\xaa\x00\x00\xe8\ -\x3b\xe7\x2e\x1f\x0c\xa4\x87\xd1\xb8\x68\x9d\xf4\x42\x1a\xb4\xbc\ -\x00\xcf\xb2\xfa\x07\x78\x7c\x16\x77\x30\xb6\xd4\xb4\x00\x13\x80\ -\xf1\xbb\x64\x5f\x99\x1b\x02\x9a\xb5\x49\x13\xdb\x65\x20\x74\x0b\ -\xd4\xe0\x1b\x7b\x58\x77\xd2\x27\x16\x5b\x8c\xe0\xb7\x09\x22\x8d\ -\x84\xbf\x89\x1e\x0f\xd1\xd4\x34\xd3\x4b\x85\x3d\x9a\x0f\xad\x2b\ -\x00\xf8\x21\x7e\x1c\xb7\x4e\x2e\xae\x9a\xb5\x71\xac\xce\xf0\xed\ -\xd9\x8e\x7c\x86\xdf\x53\x31\x86\x71\xb6\xb7\xd3\xfa\x45\xba\xbb\ -\xcb\x7a\x5e\x77\xf6\xa7\x8a\x96\x26\x85\xda\x8d\x62\x7a\x72\x2a\ -\xba\x5c\x1c\x23\x4e\x53\x38\x3e\x71\xc1\xf1\xd9\x45\x3d\xd3\xdd\ -\x69\x69\x38\x3e\x5d\x1f\x8e\xcf\xcb\xc5\xf1\x79\x9a\xf0\x1a\x17\ -\xc7\xeb\xc4\x71\xe3\xa2\xb1\x85\x38\x8e\x2f\x4e\x2f\x19\xb3\x8d\ -\xb5\x62\xb6\x7e\xbe\x31\xcc\xae\x2e\x21\xe2\x8b\x6d\xca\xc5\xec\ -\xd9\x65\x9a\x91\xcf\xcf\x5c\x30\x7b\x72\x76\xfa\x04\x64\x6f\x7c\ -\x75\x47\xb9\x98\x3d\x39\xc9\x60\xf6\x62\x9d\x34\x5b\x26\x66\x2f\ -\xdc\x25\xee\xf4\x26\xe8\x72\x91\x5c\xcf\xd0\x6f\xe3\xca\x49\x32\ -\x9c\xa2\xbf\x26\xd5\x5d\xae\x9a\xdc\x16\xdb\x21\x71\x95\x5b\xc9\ -\xd6\x43\xd6\x42\x73\x92\x12\x8d\xc6\xf9\x63\x8c\x91\xf9\x58\x4e\ -\x3e\x42\x7d\xb3\x36\xe2\xad\xbd\xff\x03\x0c\xba\x22\x38\ +\x00\xa3\x4a\x78\x9c\xed\x1d\x6b\x6f\xdb\x38\xf2\x7b\x7e\x05\x91\ +\x0f\x45\x0f\xc8\xc6\xb1\xf3\x6e\x1d\x2f\xda\xf4\x09\x74\x77\xbb\ +\x75\xda\xde\xde\x97\x05\x2d\xd1\x36\xaf\xb2\xe8\x8a\x74\x12\x2f\ +\xee\xc7\xdf\x0c\x49\x59\x8f\xc8\x72\x64\x59\x96\xdd\xba\x28\x10\ +\x89\xa4\xc8\xe1\x70\x5e\x9c\x19\xd2\xed\x5f\xef\x47\x1e\xb9\x65\ +\x81\xe4\xc2\xbf\xda\x6f\x1e\x1e\xed\x13\xe6\x3b\xc2\xe5\xfe\xe0\ +\x6a\xff\xf3\xcd\x9b\x5f\x2e\xf6\x7f\xed\xec\xb5\x27\x3c\x6a\x74\ +\x02\x8d\x3a\x7b\xa4\xed\x78\x54\xca\xce\xdb\x09\x7f\xf6\xec\x15\ +\xa7\x9e\x18\xc0\x5f\x6f\xd0\x65\x4a\xc1\xc7\xf2\x55\x40\xfb\xaa\ +\xdd\x30\x8d\xa0\xf5\x1d\x77\x07\x4c\x11\xfd\x7e\xb5\xff\xe7\x57\ +\xfd\xba\x4f\x7c\x3a\x62\x57\xfb\xb9\x9d\xe0\x60\xa4\x3d\x0e\xc4\ +\x98\x05\x6a\x6a\xbf\x18\x30\x31\x62\x2a\x98\xea\x4a\xd2\x0e\x98\ +\xa3\xf4\x13\x69\xdf\x77\x8e\xda\x8d\x7b\xfb\x32\xc5\x97\xa9\x7d\ +\x01\x10\xd4\xb0\x73\x7a\x09\x45\xe6\xd1\x14\x0f\x19\x1f\x0c\x55\ +\xe7\xec\xb8\xd5\x6e\xd8\x67\xdd\x67\x23\xec\xb4\xdd\x08\x07\xcf\ +\x82\xe4\x8e\xfb\xae\xb8\xbb\xe1\xca\x63\x16\x18\xa9\x02\x00\xbe\ +\xf3\x96\xf9\x2c\xa0\x1e\x91\x76\x32\xed\x86\xad\x78\xd8\xa5\x47\ +\xa7\x62\x12\x21\xe7\xcb\x4b\x71\xff\x41\x17\xd9\x1e\x53\x43\xca\ +\x31\x75\xa0\xa3\x7d\x3b\x01\x7f\x32\xea\xb1\xa0\x73\xd6\x6e\xd8\ +\x27\x03\x7e\x7c\x84\x07\x5d\x8c\x68\x30\xe0\x7e\xaa\x87\xcb\xdc\ +\x1e\xb8\x62\xa3\x08\x93\xf1\xc5\x7c\x1b\x88\xc9\x18\x60\x0e\x97\ +\x73\x10\xbe\x9b\xe6\x0f\x06\x57\x11\xb2\x32\xf0\xa5\x17\x9d\x74\ +\x33\xb0\xf6\x10\xa8\x5c\xdc\xd9\xd1\x80\x70\x15\x77\xa8\x67\x4a\ +\xff\x6e\x45\x03\x47\x33\xca\xe8\xe8\xdd\x83\x8e\x86\x22\xe0\xff\ +\x08\x5f\xcd\xba\x6a\x5e\xce\xfa\x4a\xf7\xf6\x10\x49\x1f\x68\x8f\ +\x79\x61\x57\x1e\xbe\x24\xbf\xcf\x40\x13\xbb\x57\x89\x06\x33\x54\ +\x19\x14\x71\x5f\xb1\xa0\x4f\x1d\x46\x46\xc2\x65\x29\x44\x65\x63\ +\xcb\x14\x1a\xc8\x62\xa0\x37\x92\xb0\x2f\x98\x8a\xe6\xd6\x8f\x01\ +\xeb\x5f\x8b\x51\x4f\xc4\xd7\x1d\x2b\xc6\x50\xe1\x60\x45\x4f\xdc\ +\xff\x7d\x92\x3f\x41\x21\xbc\x1b\x3e\xce\x9e\xe3\xcd\x90\x4b\x02\ +\xff\xd5\x90\x91\xcf\xef\xf5\x14\x61\xc6\xe4\x6e\xc8\x9d\xa1\x2e\ +\x34\x48\x80\xf2\x89\xc7\xc8\x1d\xf7\x3c\x72\x27\x82\x6f\xcf\xc8\ +\x0d\xf4\xda\xa3\x81\xf9\x42\x97\x8f\x3d\x44\x12\xf5\x42\xda\x0a\ +\x39\x12\xfb\xa3\xf0\x36\xa6\x01\x55\x8c\x28\xf3\xe1\x01\x8e\x01\ +\x5d\x2a\x2a\xbf\x25\xfb\x99\x48\xa6\x47\x7e\x13\x30\x76\xfd\xe2\ +\x15\xb9\x81\x16\xb7\x9c\xdd\x11\x39\x95\x80\x31\xd2\x17\x81\x1e\ +\x85\x2b\x89\x6d\x03\xb3\x42\xd4\x51\x20\x37\x1f\xbd\x3c\x0f\xb0\ +\x84\x08\x7d\xed\xa3\xac\x23\x52\xb9\x00\xfb\xd5\xfe\x51\x0a\x65\ +\x8e\xed\xfb\x33\xff\x4d\x53\x82\x53\x66\xac\x8f\x54\x0d\x17\x0f\ +\x05\x03\x35\x42\x19\xff\xe8\xd1\x52\x74\xf5\x18\x92\x8f\xe8\xc1\ +\xac\x4e\x16\x1e\xe7\x0d\xd7\x48\x8f\x57\x0e\x00\xbb\xda\x65\x20\ +\x58\xc8\x7b\xed\x86\x11\x43\x33\x19\x95\xa8\x2e\x2d\xb1\x5a\xe5\ +\x04\x56\x6b\x59\x79\xc5\xfa\x74\xe2\x41\xd7\xc2\x13\x99\x2b\x58\ +\xb9\xa0\x82\x71\x5f\x4e\x94\x12\x7e\x86\xac\x82\xba\x9e\xa9\x5b\ +\x5a\x58\xa1\x54\x70\xe3\x93\xd4\xb2\xc0\x07\xd1\x20\x7a\xff\x05\ +\x33\x22\xad\xc6\xf2\x68\x26\x35\xae\xee\x2e\xcd\x84\x58\x96\xa2\ +\xd2\x80\xb9\x68\xec\xe0\x9f\x64\xc5\x00\xa4\x95\x8f\x55\xe6\x21\ +\x59\xd9\xf3\x26\x0c\xeb\xf4\xdf\x24\x41\x3f\x18\x64\xe5\xe2\xca\ +\x92\xc3\x66\x4a\xab\xd2\xd4\x97\xc5\x40\x0b\xf4\xe1\x5c\x06\xea\ +\xfa\x74\xbc\xe9\xdc\xb3\x48\x3a\x14\xe7\x1f\x89\xb3\x96\x53\xb0\ +\x25\xbc\x1d\x03\x25\xc0\xb7\xb8\x40\x04\xfd\x7c\x4c\xb4\xac\xd5\ +\x7c\x2d\x7c\x78\x9a\x68\x73\x6c\xe3\x99\xe9\x78\x35\x96\xf3\x43\ +\xa6\xb2\x0a\x89\xf4\x18\xb4\x25\x6e\x40\xef\x7c\x6b\xf0\x72\xc4\ +\x4a\x0c\x45\x68\xf8\x1e\xae\x87\xef\x4e\x4e\xe6\x33\x5e\xb3\x75\ +\x9a\xc3\x7a\xad\xd3\xd3\xda\xb4\x57\x84\xab\x9f\x8f\x09\x17\xd0\ +\xe7\x42\x53\xd0\xe3\x60\x1d\xa1\x17\xa6\x06\x1e\xec\x8e\xb9\x9f\ +\xb5\x6d\x95\x50\xde\x8b\xdc\x16\xd9\x33\x7b\xac\x1a\x9b\x4d\x70\ +\x75\xa6\xe0\x2d\x05\x3a\x4f\x8d\x6c\x1d\x36\xad\x84\xeb\xa6\x50\ +\xaf\x05\x48\x3e\xb6\x68\x5b\x4a\xea\x15\xef\xaf\x8e\x4b\xee\xaf\ +\x8e\x4a\xa9\x36\xca\xb5\xd0\xde\x48\x4f\xd0\x69\x19\x7d\xc6\xc8\ +\x6c\x82\xa8\xb4\x60\x8e\xbc\xcf\x59\x40\xbe\xb1\xe9\x5a\xfc\x2a\ +\x30\xa0\x13\x02\xb0\xa9\xa4\x5f\xce\xb9\x21\x87\x1c\x07\xab\xcb\ +\xb7\xe2\xa8\xc0\xab\x6f\x74\xea\x95\x9a\xfa\x12\x6c\x84\x1e\x7c\ +\xa0\xdf\xb4\x14\xe9\xea\xe2\x05\xbc\x02\xad\x19\x34\x46\xb3\x23\ +\x45\x3d\x0c\x54\x40\xe7\x4f\xf5\xec\xd9\xbb\x59\x8f\xed\x86\x2e\ +\x2c\x4c\xaa\x92\xff\xc3\xde\x71\x5f\xcd\x27\x55\x6c\x91\x42\xa7\ +\x89\xaa\x9c\x24\xe3\x2b\xb3\x5a\x1b\x5b\x69\x1d\x25\xc2\x2c\x11\ +\x58\xe9\x0e\xe7\x88\x2c\x83\xba\x95\xda\x32\xad\x66\x99\x6d\xf9\ +\xa6\x4a\xdc\xb3\x72\x12\x57\xda\xb9\xad\x40\xd2\x3a\x93\x20\x00\ +\x92\x7d\xef\xbb\xec\x3e\xdb\x7c\x69\xae\xc5\x7c\x81\xd9\xe0\xac\ +\x76\x12\xdc\x94\xee\x24\x78\x54\xb0\x62\x09\x9e\xcf\x7b\x3b\x09\ +\xbe\x72\x09\xbe\x6c\x64\xe2\x85\xa7\x36\x56\x80\x9f\x97\x13\xe0\ +\xd4\x4c\x6d\x2d\xf2\x7b\x3d\xdb\x4f\x98\x8d\x66\xf4\x9d\xf8\xde\ +\x89\xef\x4d\x8a\xaa\x36\xe3\x54\xb4\x4c\x1e\x48\xb9\x6d\xbf\x75\ +\xd7\xea\xac\x1b\xfd\x49\x0d\xd2\xec\x03\xf7\xd9\x6b\x97\xab\x07\ +\xd2\x0c\x5d\x46\x0c\x2a\xca\x44\x87\xb2\x1c\xda\xd1\x6c\xb5\x7f\ +\x2d\xe1\xb8\x0e\x93\xc3\x96\x97\x78\x8f\x43\xf9\x5a\x7c\x0f\xf1\ +\x99\xe9\x49\x9b\x15\xde\x4c\x19\x58\x37\x27\x9e\x3c\x9e\x11\x23\ +\x45\x3c\x64\xce\xb7\x4c\x45\x8c\x15\xa5\xfc\xc1\xba\x07\x20\x5b\ +\x24\xdf\x3e\x01\x08\xc9\x1d\xf5\x15\x51\x62\x96\x4a\xa4\x03\x07\ +\x8d\x98\xaf\x38\x10\x23\x5d\x61\x93\x91\x08\x95\x21\xc9\x57\x41\ +\xcd\x5d\x7a\x0b\x30\x18\xf5\x6e\x63\x44\xd4\x77\x63\xbe\x6b\xea\ +\x04\x42\x4a\x22\x99\xc4\xe4\xcf\x12\xbe\xeb\x22\x51\x4d\x00\x4a\ +\xf8\xec\x9e\x6f\xac\xaa\xaf\x9b\xcc\xcf\xab\x20\xf3\x32\x2e\xda\ +\xf7\x7d\x4b\xe5\x92\xe8\xee\x98\x7b\x00\xe4\x34\x9e\xc6\x92\xe7\ +\x7a\x0c\x8c\xd0\xb1\x0a\x29\x0a\xac\xdc\x11\x90\xda\x01\x11\x40\ +\xed\xc1\x1d\x97\x2c\x2c\x92\xa6\x3d\xf5\xee\xe8\x14\x28\x4f\xd1\ +\x00\x53\x1d\x89\x2f\x7e\x99\xf5\x58\x05\x2b\xbc\xf5\x44\x8f\x7a\ +\x64\x05\x63\x58\x0c\xa4\x86\xe9\x01\xfe\x3a\x20\xc8\xa1\x63\xfd\ +\x58\xad\xce\x18\x4f\x47\x1b\x9c\x09\x58\x37\xff\x34\x2f\x2a\x61\ +\xa0\x12\x31\xfb\xdf\x45\x30\xa2\x9e\x37\x3d\x20\x80\x48\x16\x68\ +\x32\xc4\x50\x87\x8d\x19\x1e\x58\x65\x31\xe6\x4c\x12\x04\x4e\x32\ +\x0f\xca\x99\x7b\x48\x42\xd6\x13\x63\x6d\xfb\xc4\x39\x10\xbf\xe9\ +\x51\xe0\xac\x30\xe6\x1f\x32\x62\xf8\x35\xf0\x95\x54\x8c\xba\x25\ +\xa2\xfc\x39\xba\x45\x0f\x92\x04\x20\x31\xb9\xf5\x28\x13\x0d\xc5\ +\x4b\x00\xe2\x8f\x30\xfc\xba\xe3\x89\x2c\x9e\x38\xad\x84\x25\x2e\ +\x56\xab\x53\xb4\x09\x15\xd7\x0e\xe8\xa4\x06\x73\x0a\x8c\x05\xa9\ +\x62\xfc\x62\x33\x5a\x30\xbb\x05\x4a\x35\x93\xf8\x42\xc5\xbe\xef\ +\x19\x07\xf7\x58\x7f\xe3\x7b\x53\xf8\x80\xf9\x04\x40\x07\x3b\x07\ +\x8a\xae\x6f\x3e\x7d\xa8\x84\x29\x5e\x24\xe1\x0e\xc1\x7d\xea\x72\ +\x49\x7b\x5e\xe4\x75\x47\x87\xcd\xbf\xb6\x59\x05\x99\x05\xea\x6e\ +\xb0\xbb\x7d\xe5\x4e\xdf\xfc\x1d\xee\xce\xe9\xbb\x6a\xa7\x6f\xb3\ +\xa4\x83\x34\xa0\x2e\x9f\xc8\x59\xa6\xa9\x96\x05\xc0\x93\x72\xcc\ +\x1c\x0e\x96\xe0\x58\x00\x22\xe5\x21\x9e\x44\xc2\xe2\x23\x93\xc7\ +\x23\x08\xb0\xaa\xa2\xbe\xc3\xc8\x53\xee\xf7\xb9\x0f\x70\x97\x60\ +\xd4\x05\xb1\xc5\x80\xfa\x83\x3a\xdc\x39\x0b\x32\xa4\x16\x90\xba\ +\x9c\xf4\xfb\x3c\xed\x29\xb6\x73\x18\xdf\xaf\x47\xed\x03\xf6\x3e\ +\x19\xe4\x6d\xa9\xf4\xa9\xfa\x24\x48\x01\x9f\x65\x01\x7d\x5f\xe2\ +\xc0\x57\x96\xbe\x9f\xf1\xa5\x56\xda\xa0\xc2\x89\x70\x9c\x49\x40\ +\xe8\x80\xa2\xe9\x1a\x33\x6c\xd5\x10\xd4\x66\x80\x4e\x15\xea\x6b\ +\xc3\x97\xfb\x2e\x77\x28\x1a\xb9\x26\x32\x41\x44\x9f\x30\x98\x55\ +\x09\x1f\xc6\xa2\x44\x00\x7a\xcf\x47\x93\x51\x25\x5a\xbb\x4f\x3d\ +\xb9\x0e\xb5\x0d\x73\xf8\x99\x74\xf6\x4e\x63\xcf\xc7\x74\x15\x61\ +\xda\xfc\x1d\x32\xf3\xd1\x06\x5e\x3d\xfd\xe7\x8a\x9d\xdf\x0c\xd7\ +\xa6\xa5\x04\xaa\xfc\x1e\xd3\x71\x05\xee\xb2\x00\xc4\x48\xdc\x4e\ +\xa8\x42\x86\xcc\x01\x64\x03\xb5\x7f\x7e\x76\x43\x55\xab\x68\xc5\ +\x6b\x76\x00\xfa\xf2\xf2\x72\xf9\x10\x74\x5e\x5e\xf5\xc9\x7a\x22\ +\xdb\x46\xec\xbe\x36\x0b\xbe\xa5\xb2\xb7\x6a\x8b\xa5\x59\x89\xc5\ +\x52\x62\x13\x01\x16\x4b\xc2\xd7\xd6\x35\xbb\x7a\x13\xb8\x99\xb9\ +\x19\x86\x42\x9f\xb0\x61\x3e\xbb\x05\xb6\x46\x0f\x04\x06\x7f\xaa\ +\x94\x24\x5d\x18\x32\xbc\x46\x21\x06\xd2\x36\xbb\x13\x10\x8b\x38\ +\x97\x97\xf4\xc7\x3d\x66\x53\x80\x68\x97\x3d\x02\xf7\x0e\x74\x59\ +\x78\x09\x42\x9c\x58\x8d\x5f\x76\x22\x4b\x44\x3d\x0a\x2c\xe6\x10\ +\xa0\xd8\xf6\xc5\xac\x3a\x42\x51\xe0\xa4\x7e\x01\xba\x69\x2e\xc8\ +\x74\xce\x93\x76\x3c\x26\xed\x28\xb1\x20\xd8\x5d\x96\x76\x9d\x0c\ +\xa3\x50\x32\xde\xc4\x81\x65\x63\x8f\xfa\x8c\xc4\x0c\x69\x02\x52\ +\x8f\xd1\x40\x12\x77\x82\x9d\x86\x1e\x5a\x82\x60\xe8\x06\xd5\x6c\ +\xcf\x50\x20\x7e\xb5\x30\x7d\xd4\x30\xa9\x80\xc2\x54\x4a\xc8\xc4\ +\x82\xc2\x4b\x8f\x7a\x13\x0e\xba\x23\xfa\x2c\xa2\xaf\x26\x2a\x57\ +\x22\x0f\x3e\xcb\x25\x91\x88\xa5\x19\x6a\xc6\x24\x8d\x3e\xbc\xc2\ +\x26\x21\x4a\xd7\x38\x24\x7f\x84\x91\x6d\x6d\x1d\x4c\xd3\x5f\xdc\ +\x71\x80\x32\x28\x92\xae\x55\x80\xe2\xdf\xe0\x58\xb3\xa3\xbe\xd3\ +\xf2\x39\x24\x05\xc8\x1d\x51\xb1\x8b\x3e\xcf\x27\xf3\xb2\xb7\x46\ +\x95\xbd\x85\x25\x21\x9c\x37\x32\xf3\x39\x7f\x86\x05\x48\xd1\x92\ +\xfd\xd7\x8f\x9b\x4a\x8b\xe5\xf2\x66\x7f\x17\xd9\x0b\xb8\x9e\xac\ +\xdd\x7f\xff\x45\x9e\xde\x88\x71\x66\xf4\x63\x4d\x10\xfc\x87\x3c\ +\x7d\x13\x00\x6f\xd5\x08\xc3\x5f\x00\x43\x17\x0c\xda\x52\x20\xd4\ +\x2d\x92\x9a\xd5\x64\x94\x95\xc8\xf9\x7d\x9f\xb0\x36\x07\x01\x77\ +\x13\xfa\x53\x87\xeb\xad\xf1\x58\x85\xfe\xfc\x0c\x5b\x74\x1c\x74\ +\xab\x9d\xf9\x66\x02\x9b\x29\xf8\x56\x1e\x7d\xcf\xf7\xe3\xec\x7c\ +\xf9\x65\x7c\xf9\x45\x76\x98\xf9\x67\x19\x6a\x71\xf1\xa7\xfd\x74\ +\xc6\x01\x12\x93\x29\x26\x29\xa7\xc7\xc8\x2d\x97\x1c\xd3\x70\xb4\ +\x78\x89\x9a\xa2\xc5\xd4\x63\xbe\x33\xc4\x9d\x00\xde\xe7\x78\xcb\ +\x62\xe6\x7d\x2c\x81\x68\xa2\xb3\x87\x68\x98\xca\x5a\x65\x06\x11\ +\xec\x29\xd7\x62\xcd\xdb\x44\x1e\x18\xee\xed\x8f\x25\x4e\x1e\x61\ +\x69\xe7\x67\x44\x57\x46\xcb\xf3\x73\x94\x91\x64\xed\xed\xbf\x35\ +\x18\xee\xaf\xc4\x04\x26\x3c\x2f\x3a\xe4\xea\xda\x30\x46\xb4\x81\ +\x91\x3e\x7d\xaa\xdd\x20\x0f\x78\x5d\xdd\x31\x60\x58\x46\x81\xa9\ +\xb5\x24\xc0\x33\x0f\xcb\x33\x94\xcb\x1c\x3e\x02\xc8\xb3\xa3\x48\ +\x25\x82\x48\xd9\x51\x2f\x83\x6b\x8c\x7a\x5d\x1e\x5e\x5e\x5c\xce\ +\xfe\x9d\x5f\xb4\xda\x0d\x5b\x59\x78\xa8\xac\x30\x98\xed\xab\x79\ +\x78\x94\xfc\xb7\xfc\x28\x05\xed\x97\x6e\x48\xed\x3f\x93\xdc\xa9\ +\x27\xc0\x9a\x17\x9a\xe6\xbe\x66\x10\x49\x30\x88\xf5\xf8\x83\x75\ +\xeb\x8b\x49\xe7\x27\x1e\xd5\x94\x59\xc0\x7d\x83\xb4\x30\x16\x68\ +\x6e\xdb\x53\x43\x8e\x5e\xe0\x43\xd2\xc5\x4c\xc3\xfe\x94\x80\x1d\ +\xc1\x08\xba\xa8\xc1\x66\x98\x12\xf9\x7d\x42\x03\x26\x67\x22\x6a\ +\x14\x76\x53\x22\x21\x39\x27\xc2\xdd\x3c\x5a\x4b\x84\x1b\x79\xf9\ +\xb5\xa1\x9d\x2d\xe5\xe4\xaa\xb7\xe0\x25\xef\x0e\x5b\x90\x69\x3f\ +\x97\xb7\xdf\xe3\x1d\xe5\x3e\xe6\xbb\x06\x40\x8d\x78\xca\x8f\x78\ +\xc0\xe4\x99\xa7\xc1\x6b\xe6\xf1\x7c\x9b\xc2\x6a\xca\x2e\xec\x88\ +\x1e\xbb\xeb\x3a\xcb\xdf\x75\x35\xcf\xce\xcf\xcf\x5b\xcd\xd3\x32\ +\x7b\xaf\xe2\x26\x4a\x94\xff\x13\x1a\x15\x78\xfc\x8d\x87\xcb\xe4\ +\x08\x11\xb8\xdc\xa7\x8a\xc9\x58\xd4\x8c\x3c\xc5\xf4\x24\x76\x7f\ +\x48\x8e\xc9\x15\x39\x02\x75\xdd\x2c\x91\x95\x9c\x23\x2e\xce\xd6\ +\x22\x2d\x66\xb4\xb8\xb5\xd2\xa2\xa8\xfb\x22\x5f\x7f\xed\xdc\x17\ +\x2b\x3f\x3c\xb0\xac\xbc\x7c\xc5\x47\xcc\xd7\x87\xa1\xb7\x40\x62\ +\xe6\x6f\x60\x73\x33\xea\x2e\xaa\xc9\xa7\x5b\x8b\xf8\x70\xf9\xe8\ +\xe3\xd6\x4b\x90\xaa\xed\x8d\x02\x07\xfe\xb2\x18\x68\x41\xde\xdc\ +\x5c\xfe\xb9\x11\x1e\x28\x2d\xdf\xa9\x34\xf2\x98\x2f\x6b\xf3\x59\ +\x7f\x27\x6b\x57\xe2\x2a\x2e\xe0\x2c\xfa\x09\xcc\xba\xe8\x02\x1b\ +\x2d\x1c\x31\xd5\xce\xc5\xbc\x8c\xfe\xc4\x77\x8c\x01\xa7\x86\x54\ +\xe9\xcc\x50\x4a\x54\xc8\x22\x87\x7b\x5f\xb0\xb5\x3d\xdf\xe2\xf2\ +\x7e\x1f\x76\x8a\x50\x8e\x9b\x43\x0f\xf6\x8b\x3a\x39\xc5\x74\x18\ +\x6e\x31\x55\xc0\xf4\xa1\x17\x2a\x89\x04\xc0\x4a\xec\x19\x73\x4e\ +\x71\x35\x96\x21\x78\x7f\x00\xe4\xa0\x58\x1a\x45\xd6\x8f\x04\x26\ +\x6b\x73\x45\x1e\xa6\x1c\x3f\x16\x8c\x72\xba\x7e\x3f\x96\x8a\x44\ +\xde\x4e\x17\x65\xe9\xa2\xb3\x72\xaa\x68\x41\x86\xd7\xc2\x84\x18\ +\x6c\x41\x8c\x2c\xd8\x70\xaf\x7a\x7e\x40\xfd\x47\x10\x95\xe1\x9a\ +\x98\xf1\xf4\xa1\x1b\x5c\x1e\xa9\xef\x25\x72\x67\xb6\xf7\x6a\xf7\ +\xb6\x33\xe9\xd0\xaa\xc1\xcb\x8d\xd3\x0b\x89\x6f\x4b\xc5\x43\x51\ +\x03\x2c\xdf\x91\xb3\x33\xc0\x56\xbd\xd9\x5d\x70\xb3\xd3\xe3\x04\ +\x64\x1f\xf0\xbd\x89\x37\x0b\x2e\x9f\x63\x94\x75\xaf\x20\xce\x52\ +\x7f\x3b\xfb\x35\x47\x93\x02\x90\x25\x84\x0e\xf7\xde\x03\xbc\xd4\ +\x47\xbb\x8b\xc6\xbe\x94\x13\x67\x88\x06\xd8\x93\xef\x13\xa1\x9e\ +\xbf\x08\x38\xf5\xcc\x23\xa6\x32\x85\x03\x49\x35\xf5\xd2\x4d\x25\ +\xf5\x65\xd8\xd2\x96\xb0\x80\xf7\xcd\xe3\x1e\xc0\x63\x9e\x46\xc2\ +\x17\x61\x33\x04\x92\xf4\xe9\x88\x7b\xd3\xac\x71\x0f\xde\x31\xef\ +\x96\xe1\xef\x9f\x1e\x44\x9d\x9b\x8f\x34\xa8\xda\xb0\xa4\x06\x98\ +\xbd\x8c\xef\x9f\xbd\x14\x9e\x6b\xde\x2b\x49\x66\xc0\x31\x96\xef\ +\x98\x7a\x7c\xe0\xc3\x6a\x3c\xe8\x1d\x08\x0f\xc5\xc4\x0b\xac\xff\ +\x84\xac\xf8\xbf\xd9\xeb\x4d\x40\x39\x90\xce\x20\x2a\xf9\x72\xcd\ +\xd0\xb9\x0a\x60\x30\x55\x1c\x86\x82\x92\xde\x70\xd1\x96\xca\xf9\ +\xaa\x5d\x12\x05\x4e\xf8\x65\xfa\xf4\x96\xbd\xc7\x3f\x92\x73\xa3\ +\xb1\x87\x3f\x05\x2b\x87\x8c\x55\x2a\xec\xf2\xf5\xe3\x82\x89\xec\ +\x14\xe4\x4a\x3c\x14\x6f\xb8\xc7\xae\x87\x42\x80\x8c\x7d\xa0\x5c\ +\xfa\x50\xe7\x98\xba\x45\x46\x37\xf7\x8b\x1a\xdd\xc7\x47\xf9\x08\ +\x2a\x85\x9f\xe2\x01\x27\x37\x4d\xfd\xf6\x7e\x52\x9d\xd1\xe6\xa0\ +\x3f\xc1\x24\xb5\xe1\x6f\x58\x85\xe7\xb5\x8a\x31\x48\x39\xa1\x69\ +\xa0\xda\x09\xcd\x39\x42\xb3\xc0\x9d\xb7\x19\x42\xb3\x7c\x14\xe4\ +\x09\x1d\x8d\x9f\x93\x0f\x8c\xba\x20\xd0\x68\x10\x88\x3b\x63\x4e\ +\x6c\xe2\xc9\x92\x95\x9d\x2b\xe1\x23\xf3\xbb\xa4\x9b\x4a\x95\xe5\ +\xce\x34\xbc\x12\x8a\x9c\xd6\x77\xa4\x02\x87\x3f\xaf\x77\xf8\xcb\ +\xfa\x86\xbf\xe6\x81\x03\x3b\x83\x1a\xf1\x6f\x21\xa8\x71\x09\x2c\ +\x04\x35\xae\x42\x17\x44\xcb\xb0\xce\x45\x30\x00\xd4\xb8\x06\x06\ +\x80\x1a\x97\xe0\x25\x75\xbe\xc9\xba\x97\x21\x02\xa2\xc6\xa5\x88\ +\x80\x28\xb5\x1c\xb5\x9b\x2a\x25\xfd\xfc\xcd\xfc\x24\x99\xf9\xd6\ +\xca\x17\x68\xc5\x1d\xea\xc5\x7c\x37\xc6\xa7\x15\xdb\x49\x6d\xa2\ +\xb9\xb2\xaa\x5f\x81\x8e\x5f\xc0\x20\xfa\xc6\xdf\x15\x62\xc2\x7a\ +\xb6\xec\x89\x17\xd8\xf0\xc4\x91\x44\x03\x46\x6e\x2d\xf2\x0e\x49\ +\xb8\x4d\x86\x5e\x3d\xd6\x57\x07\x78\xcb\xae\x39\x13\x83\x3d\xbe\ +\xef\xfe\x81\xb7\xb5\xfb\x2e\x0d\xca\x5c\x28\x5d\xcc\x0a\x4b\x2c\ +\xe0\x8f\x68\x8a\x7d\x00\x44\x93\xa7\x71\xdc\xd6\x78\xd6\xf5\xd3\ +\xbc\x10\xd9\x96\x48\xa0\x92\xe2\x67\xd9\xdf\xbc\x7e\xe1\xe9\xdc\ +\x4d\xd8\x59\x77\xbf\xbc\x25\x40\x68\xf8\x0a\x3c\x24\x9c\xca\x25\ +\xcf\x02\x3f\x53\xfe\x61\xbd\x9d\x9f\x69\xad\x7e\xa6\x05\xe9\xcd\ +\x5b\xed\x67\x7a\x87\x47\x1c\xf0\xee\x33\x0c\x97\x48\x7b\xee\x81\ +\x82\xaa\x09\x98\xa3\x44\x30\xc5\x8b\x16\x95\xfd\x79\x67\x64\x12\ +\xc4\x8b\x8c\x17\x3e\xf1\xd4\xf3\xb1\xe1\x9c\x27\x03\xf5\x1c\x9d\ +\x56\x78\xf7\x72\x94\x3a\x13\xc6\x61\x5c\x97\xb9\xe8\xc1\x42\x95\ +\x14\x8a\x4c\x1b\xc3\x81\x66\xa0\xae\x6c\x2f\xeb\xf9\x2d\x1b\x3b\ +\x18\x92\xc0\xa6\xea\xa7\x72\x32\x39\x59\x99\xe8\x2b\x6a\x17\x6b\ +\x14\x6b\x61\xe5\xf7\x2c\x20\x66\x4e\x7a\x5d\xed\x9f\xed\x93\x11\ +\x0d\x06\xdc\xbf\xda\x6f\x36\xf7\x31\xd7\xa8\x3d\xe6\xf7\x23\x3a\ +\x0e\x93\xa5\x3a\xdf\x3f\xea\xf7\x37\x81\x18\xfd\x06\xc6\x4a\x57\ +\x4c\x02\x4c\xae\x49\xb5\x82\xef\x9c\x89\x54\x62\x64\x46\x94\x1a\ +\x92\x78\x89\x81\x52\xb3\x6b\x47\x73\x6b\x8c\x53\x01\x7f\xba\x5c\ +\x37\x01\xc9\xce\x7c\x57\x76\xfe\xfc\xaa\xbf\x03\x51\x67\x0b\xf6\ +\x0c\x37\xa1\xeb\x0b\x7b\x68\x60\x07\xaf\x38\xf5\xc4\xe0\x70\x88\ +\xcc\xa5\x2b\x34\x02\xd2\xe3\xe6\x03\x72\x8d\x3f\xc5\xf4\x72\xa2\ +\x94\x36\x6b\x32\x00\xf9\x38\x91\xc3\xb0\x7e\x1e\x30\x06\x58\x59\ +\x0e\x92\x94\x00\xcb\x84\xe6\x21\xee\xe6\x81\x84\xbd\xad\x0c\x2c\ +\x9b\x32\x93\x8d\xa0\x59\xe5\x5a\x40\x59\xb4\x5e\x0f\x17\x75\x3d\ +\x60\xd9\xe3\xfa\xd9\x28\x8a\x6a\xd7\x84\x23\xb3\xdb\x99\x03\xcc\ +\xac\x76\x2d\xc0\x84\x59\x05\xd9\xc0\x44\xb5\x6b\x01\x26\x91\x01\ +\x96\x0d\x51\xaa\x49\x79\xb0\x92\x05\xd8\x53\x3b\x60\x52\x0b\x52\ +\xa9\x45\x2e\xe8\x5e\x9f\x99\xdc\x54\x33\x8b\xd9\xbb\x19\x55\x02\ +\x00\xd0\x77\xc6\xa5\x2f\x18\x48\xf7\xed\xb8\x68\x9d\x0c\x7c\xea\ +\x75\x1c\x0f\x0f\x3d\xba\x4f\xf1\x9c\x25\xee\x60\x4c\xa9\x6e\x01\ +\x26\x00\xe3\xb7\xf1\xbe\x52\x47\xc9\xdb\x8d\x59\x13\xd3\xa5\x27\ +\x54\x07\xd4\xe0\x6b\x73\xaa\x73\xd6\x27\x16\x1b\x8c\xe0\x2f\x37\ +\x58\x8d\x84\xcf\x44\x4d\xc7\x68\x6a\xea\xe9\x25\xc2\x1e\xed\xfb\ +\xce\x25\x00\x7c\x1f\xbe\x4e\x3b\xc7\xe7\x97\xed\xc6\x34\x54\x67\ +\xf8\xf5\xc3\x8e\x5c\x86\xbf\x36\xa3\x0d\xe3\x74\x6f\x27\xcd\xf3\ +\x64\x77\x17\xcd\xac\xee\xcc\xa3\xb4\x4b\x93\x40\xed\x5a\x31\x3d\ +\x3b\x3e\x5b\x2d\x8e\x11\xa7\x09\x1c\x1f\x97\xc1\xf1\xe9\x79\x33\ +\xd5\xdd\x49\x65\x38\x3e\x59\x1d\x8e\xcf\xaa\xc5\xf1\x59\x92\xf0\ +\x5a\xe7\x47\xab\xc4\x71\xeb\xbc\xb5\x81\x38\x0e\xaf\x95\xaf\x18\ +\xb3\xad\x95\x62\xb6\x79\xb6\x36\xcc\x2e\x2f\x21\xc2\x1b\x50\xaa\ +\xc5\xec\xe9\x45\x92\x91\xcf\x4e\xcb\x60\xf6\xf8\xf4\x64\x0b\x64\ +\x6f\x78\xc7\x43\xb5\x98\x3d\x3e\x4e\x61\xf6\x7c\x95\x34\x5b\x25\ +\x66\xcf\xcb\x4b\xdc\xe8\x9e\xec\x6a\x91\xdc\x4c\xd1\x6f\xeb\xb2\ +\x94\x64\x38\x41\x7f\x4d\xa2\xbb\x4c\x35\xb9\x29\xb6\x43\xec\xce\ +\xaf\x8a\xad\x87\xb4\x85\x56\x4a\x4a\xb4\x5a\x67\x8f\x31\x46\xe6\ +\x63\x39\xfe\x0a\xf5\xed\xc6\x84\x77\xf6\xfe\x0f\x9b\x41\x85\x54\ +\ \x00\x00\x07\xb5\ \x00\ \x00\x1a\x6e\x78\x9c\xed\x58\x5d\x6f\xe3\xb8\x15\x7d\xcf\xaf\x50\ @@ -37846,83 +37851,83 @@ qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x35\x00\x00\x00\x1b\ \x00\x00\x00\x38\x00\x02\x00\x00\x00\x05\x00\x00\x00\x16\ \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x11\x00\x00\x00\x05\ -\x00\x00\x02\x8e\x00\x01\x00\x00\x00\x01\x00\x06\x48\x2f\ -\x00\x00\x02\x02\x00\x01\x00\x00\x00\x01\x00\x05\x44\xb8\ -\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x03\x7e\x56\ -\x00\x00\x02\x56\x00\x01\x00\x00\x00\x01\x00\x05\xe1\x7b\ -\x00\x00\x02\x3a\x00\x01\x00\x00\x00\x01\x00\x05\xac\xbf\ -\x00\x00\x01\x3e\x00\x01\x00\x00\x00\x01\x00\x02\x0b\x3a\ -\x00\x00\x02\x1e\x00\x01\x00\x00\x00\x01\x00\x05\x77\x8d\ -\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x04\ -\x00\x00\x02\x72\x00\x01\x00\x00\x00\x01\x00\x06\x15\x26\ -\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x3d\x78\ -\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x01\x62\xc3\ -\x00\x00\x01\xae\x00\x01\x00\x00\x00\x01\x00\x04\x2e\x7d\ -\x00\x00\x01\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe8\xa5\ -\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\x2f\xb4\ -\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x83\xef\ -\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x04\x61\xd1\ -\x00\x00\x01\xe6\x00\x01\x00\x00\x00\x01\x00\x05\x12\xa0\ +\x00\x00\x02\x8e\x00\x01\x00\x00\x00\x01\x00\x06\x48\x2e\ +\x00\x00\x02\x02\x00\x01\x00\x00\x00\x01\x00\x05\x44\xb7\ +\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x03\x7e\x55\ +\x00\x00\x02\x56\x00\x01\x00\x00\x00\x01\x00\x05\xe1\x7a\ +\x00\x00\x02\x3a\x00\x01\x00\x00\x00\x01\x00\x05\xac\xbe\ +\x00\x00\x01\x3e\x00\x01\x00\x00\x00\x01\x00\x02\x0b\x39\ +\x00\x00\x02\x1e\x00\x01\x00\x00\x00\x01\x00\x05\x77\x8c\ +\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x03\ +\x00\x00\x02\x72\x00\x01\x00\x00\x00\x01\x00\x06\x15\x25\ +\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x3d\x77\ +\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x01\x62\xc2\ +\x00\x00\x01\xae\x00\x01\x00\x00\x00\x01\x00\x04\x2e\x7c\ +\x00\x00\x01\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe8\xa4\ +\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\x2f\xb3\ +\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x83\xee\ +\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x04\x61\xd0\ +\x00\x00\x01\xe6\x00\x01\x00\x00\x00\x01\x00\x05\x12\x9f\ \x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x06\x29\ \x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x01\x64\ \x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x04\xc4\ \x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x03\x12\ -\x00\x00\x06\x02\x00\x01\x00\x00\x00\x01\x00\x07\x70\x84\ -\x00\x00\x03\xb0\x00\x00\x00\x00\x00\x01\x00\x06\xc2\x31\ -\x00\x00\x08\x38\x00\x01\x00\x00\x00\x01\x00\x08\x24\x4e\ -\x00\x00\x0a\x7e\x00\x01\x00\x00\x00\x01\x00\x08\xe3\x36\ -\x00\x00\x04\xba\x00\x01\x00\x00\x00\x01\x00\x07\x0d\x18\ -\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x07\x8d\xce\ -\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x07\xe6\x6c\ -\x00\x00\x06\xbe\x00\x00\x00\x00\x00\x01\x00\x07\xb7\x9d\ -\x00\x00\x08\xd2\x00\x01\x00\x00\x00\x01\x00\x08\x5d\xdd\ -\x00\x00\x0a\xce\x00\x01\x00\x00\x00\x01\x00\x08\xff\x46\ -\x00\x00\x03\xf6\x00\x01\x00\x00\x00\x01\x00\x06\xdb\x53\ -\x00\x00\x07\x84\x00\x01\x00\x00\x00\x01\x00\x07\xec\x30\ -\x00\x00\x06\x24\x00\x00\x00\x00\x00\x01\x00\x07\x7b\x38\ -\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x06\xe0\xd2\ -\x00\x00\x06\x92\x00\x01\x00\x00\x00\x01\x00\x07\xa6\x93\ -\x00\x00\x03\xd2\x00\x01\x00\x00\x00\x01\x00\x06\xd0\xe7\ -\x00\x00\x0a\x08\x00\x00\x00\x00\x00\x01\x00\x08\xc5\x7c\ -\x00\x00\x03\x30\x00\x01\x00\x00\x00\x01\x00\x06\x9c\x7b\ -\x00\x00\x05\x0a\x00\x01\x00\x00\x00\x01\x00\x07\x28\x37\ -\x00\x00\x09\xc0\x00\x01\x00\x00\x00\x01\x00\x08\xb1\x86\ -\x00\x00\x09\xe2\x00\x01\x00\x00\x00\x01\x00\x08\xbb\xe8\ -\x00\x00\x04\xe8\x00\x00\x00\x00\x00\x01\x00\x07\x16\x1e\ -\x00\x00\x02\xfe\x00\x01\x00\x00\x00\x01\x00\x06\x94\xc2\ -\x00\x00\x07\xee\x00\x01\x00\x00\x00\x01\x00\x08\x0d\x24\ -\x00\x00\x09\x2c\x00\x00\x00\x00\x00\x01\x00\x08\x6e\x25\ -\x00\x00\x05\x5e\x00\x01\x00\x00\x00\x01\x00\x07\x39\xa7\ -\x00\x00\x09\x50\x00\x00\x00\x00\x00\x01\x00\x08\x84\xd8\ -\x00\x00\x07\x18\x00\x00\x00\x00\x00\x01\x00\x07\xce\x3f\ -\x00\x00\x04\x6c\x00\x01\x00\x00\x00\x01\x00\x06\xfc\x3a\ -\x00\x00\x0a\x9e\x00\x00\x00\x00\x00\x01\x00\x08\xed\xd0\ -\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x07\x50\xe6\ -\x00\x00\x03\x5c\x00\x00\x00\x00\x00\x01\x00\x06\xa4\x82\ -\x00\x00\x0a\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x0b\x1f\ -\x00\x00\x09\x98\x00\x00\x00\x00\x00\x01\x00\x08\xa2\x5f\ -\x00\x00\x03\x80\x00\x01\x00\x00\x00\x01\x00\x06\xb9\xa2\ -\x00\x00\x08\x5a\x00\x01\x00\x00\x00\x01\x00\x08\x2c\xf7\ -\x00\x00\x0a\x2e\x00\x00\x00\x00\x00\x01\x00\x08\xce\x09\ -\x00\x00\x05\xe0\x00\x01\x00\x00\x00\x01\x00\x07\x63\x25\ -\x00\x00\x06\xf0\x00\x01\x00\x00\x00\x01\x00\x07\xc4\x8e\ -\x00\x00\x08\xb0\x00\x00\x00\x00\x00\x01\x00\x08\x49\x4c\ -\x00\x00\x04\x90\x00\x01\x00\x00\x00\x01\x00\x07\x02\xd7\ -\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x07\xdf\xf7\ -\x00\x00\x05\x3e\x00\x01\x00\x00\x00\x01\x00\x07\x34\x28\ -\x00\x00\x08\xfc\x00\x01\x00\x00\x00\x01\x00\x08\x64\x52\ -\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x08\x01\xb2\ -\x00\x00\x08\x10\x00\x01\x00\x00\x00\x01\x00\x08\x14\x78\ -\x00\x00\x0a\x54\x00\x01\x00\x00\x00\x01\x00\x08\xd8\xb4\ -\x00\x00\x09\x74\x00\x01\x00\x00\x00\x01\x00\x08\x97\xaf\ -\x00\x00\x04\x3c\x00\x01\x00\x00\x00\x01\x00\x06\xf3\xf1\ -\x00\x00\x07\xa6\x00\x00\x00\x00\x00\x01\x00\x07\xf1\xe0\ -\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x07\x41\x8c\ -\x00\x00\x06\x72\x00\x01\x00\x00\x00\x01\x00\x07\x9d\x1f\ -\x00\x00\x08\x7e\x00\x00\x00\x00\x00\x01\x00\x08\x34\x48\ -\x00\x00\x02\xaa\x00\x01\x00\x00\x00\x01\x00\x06\x7b\xb2\ -\x00\x00\x02\xd6\x00\x01\x00\x00\x00\x01\x00\x06\x85\x1f\ +\x00\x00\x06\x02\x00\x01\x00\x00\x00\x01\x00\x07\x70\xc5\ +\x00\x00\x03\xb0\x00\x00\x00\x00\x00\x01\x00\x06\xc2\x72\ +\x00\x00\x08\x38\x00\x01\x00\x00\x00\x01\x00\x08\x24\x8f\ +\x00\x00\x0a\x7e\x00\x01\x00\x00\x00\x01\x00\x08\xe3\x77\ +\x00\x00\x04\xba\x00\x01\x00\x00\x00\x01\x00\x07\x0d\x59\ +\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x07\x8e\x0f\ +\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x07\xe6\xad\ +\x00\x00\x06\xbe\x00\x00\x00\x00\x00\x01\x00\x07\xb7\xde\ +\x00\x00\x08\xd2\x00\x01\x00\x00\x00\x01\x00\x08\x5e\x1e\ +\x00\x00\x0a\xce\x00\x01\x00\x00\x00\x01\x00\x08\xff\x87\ +\x00\x00\x03\xf6\x00\x01\x00\x00\x00\x01\x00\x06\xdb\x94\ +\x00\x00\x07\x84\x00\x01\x00\x00\x00\x01\x00\x07\xec\x71\ +\x00\x00\x06\x24\x00\x00\x00\x00\x00\x01\x00\x07\x7b\x79\ +\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x06\xe1\x13\ +\x00\x00\x06\x92\x00\x01\x00\x00\x00\x01\x00\x07\xa6\xd4\ +\x00\x00\x03\xd2\x00\x01\x00\x00\x00\x01\x00\x06\xd1\x28\ +\x00\x00\x0a\x08\x00\x00\x00\x00\x00\x01\x00\x08\xc5\xbd\ +\x00\x00\x03\x30\x00\x01\x00\x00\x00\x01\x00\x06\x9c\xbc\ +\x00\x00\x05\x0a\x00\x01\x00\x00\x00\x01\x00\x07\x28\x78\ +\x00\x00\x09\xc0\x00\x01\x00\x00\x00\x01\x00\x08\xb1\xc7\ +\x00\x00\x09\xe2\x00\x01\x00\x00\x00\x01\x00\x08\xbc\x29\ +\x00\x00\x04\xe8\x00\x00\x00\x00\x00\x01\x00\x07\x16\x5f\ +\x00\x00\x02\xfe\x00\x01\x00\x00\x00\x01\x00\x06\x95\x03\ +\x00\x00\x07\xee\x00\x01\x00\x00\x00\x01\x00\x08\x0d\x65\ +\x00\x00\x09\x2c\x00\x00\x00\x00\x00\x01\x00\x08\x6e\x66\ +\x00\x00\x05\x5e\x00\x01\x00\x00\x00\x01\x00\x07\x39\xe8\ +\x00\x00\x09\x50\x00\x00\x00\x00\x00\x01\x00\x08\x85\x19\ +\x00\x00\x07\x18\x00\x00\x00\x00\x00\x01\x00\x07\xce\x80\ +\x00\x00\x04\x6c\x00\x01\x00\x00\x00\x01\x00\x06\xfc\x7b\ +\x00\x00\x0a\x9e\x00\x00\x00\x00\x00\x01\x00\x08\xee\x11\ +\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x07\x51\x27\ +\x00\x00\x03\x5c\x00\x00\x00\x00\x00\x01\x00\x06\xa4\xc3\ +\x00\x00\x0a\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x0b\x60\ +\x00\x00\x09\x98\x00\x00\x00\x00\x00\x01\x00\x08\xa2\xa0\ +\x00\x00\x03\x80\x00\x01\x00\x00\x00\x01\x00\x06\xb9\xe3\ +\x00\x00\x08\x5a\x00\x01\x00\x00\x00\x01\x00\x08\x2d\x38\ +\x00\x00\x0a\x2e\x00\x00\x00\x00\x00\x01\x00\x08\xce\x4a\ +\x00\x00\x05\xe0\x00\x01\x00\x00\x00\x01\x00\x07\x63\x66\ +\x00\x00\x06\xf0\x00\x01\x00\x00\x00\x01\x00\x07\xc4\xcf\ +\x00\x00\x08\xb0\x00\x00\x00\x00\x00\x01\x00\x08\x49\x8d\ +\x00\x00\x04\x90\x00\x01\x00\x00\x00\x01\x00\x07\x03\x18\ +\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x07\xe0\x38\ +\x00\x00\x05\x3e\x00\x01\x00\x00\x00\x01\x00\x07\x34\x69\ +\x00\x00\x08\xfc\x00\x01\x00\x00\x00\x01\x00\x08\x64\x93\ +\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x08\x01\xf3\ +\x00\x00\x08\x10\x00\x01\x00\x00\x00\x01\x00\x08\x14\xb9\ +\x00\x00\x0a\x54\x00\x01\x00\x00\x00\x01\x00\x08\xd8\xf5\ +\x00\x00\x09\x74\x00\x01\x00\x00\x00\x01\x00\x08\x97\xf0\ +\x00\x00\x04\x3c\x00\x01\x00\x00\x00\x01\x00\x06\xf4\x32\ +\x00\x00\x07\xa6\x00\x00\x00\x00\x00\x01\x00\x07\xf2\x21\ +\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x07\x41\xcd\ +\x00\x00\x06\x72\x00\x01\x00\x00\x00\x01\x00\x07\x9d\x60\ +\x00\x00\x08\x7e\x00\x00\x00\x00\x00\x01\x00\x08\x34\x89\ +\x00\x00\x02\xaa\x00\x01\x00\x00\x00\x01\x00\x06\x7b\xb1\ +\x00\x00\x02\xd6\x00\x01\x00\x00\x00\x01\x00\x06\x85\x1e\ " def qInitResources(): diff --git a/src/Mod/Draft/Resources/ui/userprefs-base.ui b/src/Mod/Draft/Resources/ui/userprefs-base.ui index 1928878206..ebf2bdbd97 100755 --- a/src/Mod/Draft/Resources/ui/userprefs-base.ui +++ b/src/Mod/Draft/Resources/ui/userprefs-base.ui @@ -562,6 +562,26 @@ + + + + + + if checked, a widget indicating the current working plane orientation appears during drawing operations + + + Show Working Plane tracker + + + showPlaneTracker + + + Mod/Draft + + + + + @@ -873,19 +893,6 @@ Values with differences below this value will be treated as same. - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -908,17 +915,6 @@ Values with differences below this value will be treated as same. - - - - - - - - Default text font - - - @@ -932,6 +928,13 @@ Values with differences below this value will be treated as same. + + + + Default text font + + + From 80b9029fc93df691d2ea2860a0bedfa47fe862eb Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 6 Jun 2012 17:04:24 -0300 Subject: [PATCH 61/69] Draft: snap toolbar now appears on WB activation --- src/Mod/Arch/InitGui.py | 8 ++++++-- src/Mod/Draft/DraftSnap.py | 4 ++-- src/Mod/Draft/InitGui.py | 12 +++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 6267d9077e..2701e5616c 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -85,11 +85,15 @@ class ArchWorkbench(Workbench): Log ('Loading Arch module... done\n') def Activated(self): - FreeCADGui.draftToolBar.Activated() + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Activated() + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.show() Msg("Arch workbench activated\n") def Deactivated(self): - FreeCADGui.draftToolBar.Deactivated() + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Deactivated() Msg("Arch workbench deactivated\n") def ContextMenu(self, recipient): diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 3918be2b2f..746bb7bc94 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -167,7 +167,7 @@ class Snapper: self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) # set the grid - if self.grid and Draft.getParam("grid") and (not self.forceGridOff): + if self.grid and (not self.forceGridOff): self.grid.set() # activate snap @@ -894,7 +894,7 @@ class Snapper: def setGrid(self): "sets the grid, if visible" - if self.grid: + if self.grid and (not self.forceGridOff): if self.grid.Visible: self.grid.set() diff --git a/src/Mod/Draft/InitGui.py b/src/Mod/Draft/InitGui.py index 3e2bd5696d..b4b83ee359 100644 --- a/src/Mod/Draft/InitGui.py +++ b/src/Mod/Draft/InitGui.py @@ -201,10 +201,16 @@ class DraftWorkbench (Workbench): self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Wire tools"))],self.lineList) def Activated(self): - FreeCADGui.draftToolBar.Activated() - + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Activated() + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.show() + Msg("Draft workbench activated\n") + def Deactivated(self): - FreeCADGui.draftToolBar.Deactivated() + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Deactivated() + Msg("Draft workbench deactivated\n") def ContextMenu(self, recipient): if (recipient == "View"): From 4ad11a9bba65622c40098060626af41e71962b6c Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 7 Jun 2012 10:42:55 -0300 Subject: [PATCH 62/69] Arch: fixed import error --- src/Mod/Arch/InitGui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 2701e5616c..7f4c510795 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -109,7 +109,8 @@ FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ") try: import collada except: - FreeCAD.Console.PrintMessage(str(DraftTools.translate("arch","pycollada not found, collada support will be disabled.\n"))) + from DraftTools import translate + FreeCAD.Console.PrintMessage(str(translate("arch","pycollada not found, collada support will be disabled.\n"))) else: FreeCAD.addImportType("Collada (*.dae)","importDAE") FreeCAD.addExportType("Collada (*.dae)","importDAE") From 1ff3d74efccc7a6939389fbf989103bc0cac0357 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 7 Jun 2012 10:43:32 -0300 Subject: [PATCH 63/69] Draft: Made the grid appear on module activation --- src/Mod/Draft/DraftSnap.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 746bb7bc94..846cb522c1 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -883,7 +883,7 @@ class Snapper: return False def show(self): - "shows the toolbar" + "shows the toolbar and the grid" if not hasattr(self,"toolbar"): self.makeSnapToolBar() mw = getMainWindow() @@ -891,6 +891,11 @@ class Snapper: if not bt: mw.addToolBar(self.toolbar) self.toolbar.show() + if FreeCADGui.ActiveDocument: + if not self.forceGridOff: + if not self.grid: + self.grid = DraftTrackers.gridTracker() + self.grid.set() def setGrid(self): "sets the grid, if visible" From c09bf76d8415b463ce94e53092d4b4f0fa2115ea Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 7 Jun 2012 20:17:38 +0200 Subject: [PATCH 64/69] 0000738: Compress stuff in Tree view --- src/Gui/CombiView.cpp | 2 ++ src/Gui/Tree.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Gui/CombiView.cpp b/src/Gui/CombiView.cpp index 1632c4a749..b7cdb1f7f9 100644 --- a/src/Gui/CombiView.cpp +++ b/src/Gui/CombiView.cpp @@ -64,6 +64,8 @@ CombiView::CombiView(Gui::Document* pcDocument, QWidget *parent) // tree widget tree = new TreeWidget(this); //tree->setRootIsDecorated(false); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + tree->setIndentation(hGrp->GetInt("Indentation", tree->indentation())); splitter->addWidget(tree); // property view diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index c453662a59..48d27f3256 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -726,6 +726,8 @@ TreeDockWidget::TreeDockWidget(Gui::Document* pcDocument,QWidget *parent) setWindowTitle(tr("Tree view")); this->treeWidget = new TreeWidget(this); this->treeWidget->setRootIsDecorated(false); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + this->treeWidget->setIndentation(hGrp->GetInt("Indentation", this->treeWidget->indentation())); QGridLayout* pLayout = new QGridLayout(this); pLayout->setSpacing(0); From a31f9e6a645cde515564f5cd6d305acdef8cfdcd Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 7 Jun 2012 22:14:58 +0200 Subject: [PATCH 65/69] 0000739: Chamfer/Fillet(PartDesign) on whole body produces infinite loop --- src/Mod/PartDesign/Gui/Command.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index c5fa4d2ad1..37e02dc6a9 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -428,11 +428,15 @@ void CmdPartDesignFillet::activated(int iMsg) SubNames.erase(SubNames.begin()+i); } + // empty name or any other sub-element + else { + SubNames.erase(SubNames.begin()+i); + } } - if(SubNames.size() == 0){ + if (SubNames.size() == 0) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("No Fillet possilbe on seleced faces/edges")); + QObject::tr("No fillet possible on selected faces/edges")); return; } @@ -571,11 +575,15 @@ void CmdPartDesignChamfer::activated(int iMsg) SubNames.erase(SubNames.begin()+i); } + // empty name or any other sub-element + else { + SubNames.erase(SubNames.begin()+i); + } } - if(SubNames.size() == 0){ + if (SubNames.size() == 0) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("No Fillet possilbe on seleced faces/edges")); + QObject::tr("No chamfer possible on selected faces/edges")); return; } From 95d07584ddd9e287b214dbea4f4e40380c3a356d Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 7 Jun 2012 23:19:00 +0200 Subject: [PATCH 66/69] Add missing export macro for Box class --- src/Mod/Part/App/FeaturePartBox.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Part/App/FeaturePartBox.h b/src/Mod/Part/App/FeaturePartBox.h index 0349bb2111..086fa40cd4 100644 --- a/src/Mod/Part/App/FeaturePartBox.h +++ b/src/Mod/Part/App/FeaturePartBox.h @@ -32,7 +32,7 @@ namespace Part { -class Box :public Part::Primitive +class PartExport Box :public Part::Primitive { PROPERTY_HEADER(Part::Box); From e78f832760b1764b96254ca274ed0ed96c9bf787 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 7 Jun 2012 18:20:42 -0300 Subject: [PATCH 67/69] Draft: Huge bugfixing in snap and grid behaviour --- src/Mod/Draft/DraftGui.py | 9 + src/Mod/Draft/DraftSnap.py | 42 ++++- src/Mod/Draft/DraftTools.py | 303 ++++++++++++++++----------------- src/Mod/Draft/DraftTrackers.py | 50 ++++-- src/Mod/Draft/WorkingPlane.py | 57 ++++++- 5 files changed, 275 insertions(+), 186 deletions(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 663c57fe9d..d68a981a74 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -959,6 +959,10 @@ class DraftToolBar: self.wipeLine() elif txt.endsWith("s"): self.togglesnap() + elif txt.endsWith("["): + self.toggleradius(1) + elif txt.endsWith("]"): + self.toggleradius(-1) elif txt.endsWith("c"): if self.closeButton.isVisible(): self.closeLine() @@ -1158,6 +1162,11 @@ class DraftToolBar: if hasattr(FreeCADGui,"Snapper"): FreeCADGui.Snapper.toggle() + def toggleradius(self,val): + if hasattr(FreeCADGui,"Snapper"): + par = Draft.getParam("snapRange") + Draft.setParam("snapRange",par+val) + FreeCADGui.Snapper.showradius() #--------------------------------------------------------------------------- # TaskView operations diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 846cb522c1..3e1673460e 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -70,11 +70,12 @@ class Snapper: self.grid = None self.constrainLine = None self.trackLine = None + self.radiusTracker = None self.snapInfo = None self.lastSnappedObject = None self.active = True self.forceGridOff = False - self.trackers = [[],[],[],[]] # view, grid, snap, extline + self.trackers = [[],[],[],[],[]] # view, grid, snap, extline, radius self.polarAngles = [90,45] @@ -126,10 +127,13 @@ class Snapper: def cstr(point): "constrains if needed" if constrain: - return self.constrain(point,lastpoint) + fpt = self.constrain(point,lastpoint) else: self.unconstrain() - return point + fpt = point + if self.radiusTracker: + self.radiusTracker.update(fpt) + return fpt snaps = [] self.snapInfo = None @@ -150,6 +154,7 @@ class Snapper: self.grid = self.trackers[1][i] self.tracker = self.trackers[2][i] self.extLine = self.trackers[3][i] + self.radiusTracker = self.trackers[4][i] else: if Draft.getParam("grid"): self.grid = DraftTrackers.gridTracker() @@ -157,19 +162,23 @@ class Snapper: self.grid = None self.tracker = DraftTrackers.snapTracker() self.extLine = DraftTrackers.lineTracker(dotted=True) + self.radiusTracker = DraftTrackers.radiusTracker() self.trackers[0].append(v) self.trackers[1].append(self.grid) self.trackers[2].append(self.tracker) self.trackers[3].append(self.extLine) + self.trackers[4].append(self.radiusTracker) # getting current snap Radius - if not self.radius: - self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) + self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) + if self.radiusTracker: + self.radiusTracker.update(self.radius) + self.radiusTracker.off() # set the grid if self.grid and (not self.forceGridOff): self.grid.set() - + # activate snap oldActive = False if Draft.getParam("alwaysSnap"): @@ -195,7 +204,7 @@ class Snapper: eline = None point,eline = self.snapToPolar(point,lastpoint) point,eline = self.snapToExtensions(point,lastpoint,constrain,eline) - + if not self.snapInfo: # nothing has been snapped, check fro grid snap @@ -223,6 +232,9 @@ class Snapper: snaps = [self.snapToVertex(self.snapInfo)] else: + + # first stick to the snapped object + point = self.snapToVertex(self.snapInfo)[0] # active snapping comp = self.snapInfo['Component'] @@ -324,7 +336,12 @@ class Snapper: view = Draft.get3DView() pt = view.getPoint(x,y) if hasattr(FreeCAD,"DraftWorkingPlane"): - dv = view.getViewDirection() + if view.getCameraType() == "Perspective": + camera = view.getCameraNode() + p = camera.getField("position").getValue() + vd = pt.sub(Vector(p[0],p[1],p[2])) + else: + dv = view.getViewDirection() return FreeCAD.DraftWorkingPlane.projectPoint(pt,dv) else: return pt @@ -641,6 +658,8 @@ class Snapper: self.tracker.off() if self.extLine: self.extLine.off() + if self.radiusTracker: + self.radiusTracker.off() if self.grid: if not Draft.getParam("alwaysShowGrid"): self.grid.off() @@ -875,6 +894,13 @@ class Snapper: self.toolbarButtons[i].setEnabled(False) self.saveSnapModes() + def showradius(self): + "shows the snap radius indicator" + self.radius = self.getScreenDist(Draft.getParam("snapRange"),(400,300)) + if self.radiusTracker: + self.radiusTracker.update(self.radius) + self.radiusTracker.on() + def isEnabled(self,but): "returns true if the given button is turned on" for b in self.toolbarButtons: diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 1a55ee3cf4..9f31ff1e81 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -143,22 +143,6 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True): cmod = hasMod(args,MODCONSTRAIN) point = FreeCADGui.Snapper.snap(args["Position"],lastpoint=last,active=amod,constrain=cmod) info = FreeCADGui.Snapper.snapInfo - # project onto working plane if needed - if (not plane.weak) and workingplane: - # working plane was explicitely selected - project onto it - viewDirection = view.getViewDirection() - if view.getCameraType() == "Perspective": - camera = view.getCameraNode() - p = camera.getField("position").getValue() - # view is from camera to point: - viewDirection = point.sub(Vector(p[0],p[1],p[2])) - # if we are not snapping to anything, project along view axis, - # otherwise perpendicularly - if view.getObjectInfo((args["Position"][0],args["Position"][1])): - pass - # point = plane.projectPoint(point) - else: - point = plane.projectPoint(point, viewDirection) ctrlPoint = Vector(point) mask = FreeCADGui.Snapper.affinity if target.node: @@ -169,8 +153,19 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True): else: ui.displayPoint(point, plane=plane, mask=mask) return point,ctrlPoint,info -def getSupport(args): +def getSupport(args=None): "returns the supporting object and sets the working plane" + if not args: + sel = FreeCADGui.Selection.getSelectionEx() + if len(sel) == 1: + sel = sel[0] + if sel.HasSubObjects: + if len(sel.SubElementNames) == 1: + if "Face" in sel.SubElementNames[0]: + plane.alignToFace(sel.SubObjects[0]) + return sel.Object + return None + snapped = Draft.get3DView().getObjectInfo((args["Position"][0],args["Position"][1])) if not snapped: return None obj = None @@ -204,11 +199,125 @@ def setMod(args,mod,state): elif mod == "alt": args["AltDown"] = state + + + +#--------------------------------------------------------------------------- +# Base Class +#--------------------------------------------------------------------------- + +class DraftTool: + "The base class of all Draft Tools" + + def __init__(self): + self.commitList = [] + + def IsActive(self): + if FreeCADGui.ActiveDocument: + return True + else: + return False + + def Activated(self,name="None"): + if FreeCAD.activeDraftCommand: + FreeCAD.activeDraftCommand.finish() + + global Part, DraftGeomUtils + import Part, DraftGeomUtils + + self.ui = None + self.call = None + self.support = None + self.commitList = [] + self.doc = FreeCAD.ActiveDocument + if not self.doc: + self.finish() + return + + FreeCAD.activeDraftCommand = self + self.view = Draft.get3DView() + self.ui = FreeCADGui.draftToolBar + self.ui.sourceCmd = self + self.ui.setTitle(name) + self.ui.show() + rot = self.view.getCameraNode().getField("orientation").getValue() + upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) + plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) + self.node = [] + self.pos = [] + self.constrain = None + self.obj = None + self.extendedCopy = False + self.ui.setTitle(name) + self.featureName = name + #self.snap = snapTracker() + #self.extsnap = lineTracker(dotted=True) + self.planetrack = None + if Draft.getParam("showPlaneTracker"): + self.planetrack = PlaneTracker() + + def finish(self): + self.node = [] + #self.snap.finalize() + #self.extsnap.finalize() + FreeCAD.activeDraftCommand = None + if self.ui: + self.ui.offUi() + self.ui.sourceCmd = None + #self.ui.cross(False) + msg("") + if self.planetrack: + self.planetrack.finalize() + if self.support: + plane.restore() + FreeCADGui.Snapper.off() + if self.call: + self.view.removeEventCallback("SoEvent",self.call) + self.call = None + if self.commitList: + todo.delayCommit(self.commitList) + self.commitList = [] + + def commit(self,name,func): + "stores actions to be committed to the FreeCAD document" + # print "committing" + self.commitList.append((name,func)) + + def getStrings(self): + "returns a couple of useful strings fro building python commands" + + # current plane rotation + p = plane.getRotation() + qr = p.Rotation.Q + qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')' + + # support object + if self.support: + sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' + else: + sup = 'None' + + # contents of self.node + points='[' + for n in self.node: + if len(points) > 1: + points += ',' + points += DraftVecUtils.toString(n) + points += ']' + + # fill mode + if self.ui: + fil = str(bool(self.ui.fillmode)) + else: + fil = "True" + + return qr,sup,points,fil + #--------------------------------------------------------------------------- # Helper tools -#--------------------------------------------------------------------------- - -class SelectPlane: +#--------------------------------------------------------------------------- + +class SelectPlane(DraftTool): "The Draft_SelectPlane FreeCAD command definition" def GetResources(self): @@ -216,20 +325,10 @@ class SelectPlane: 'Accel' : "W, P", 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "SelectPlane"), 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "Select a working plane for geometry creation")} - - def IsActive(self): - if FreeCADGui.ActiveDocument: - return True - else: - return False def Activated(self): - if FreeCAD.activeDraftCommand: - FreeCAD.activeDraftCommand.finish() + DraftTool.Activated(self) self.offset = 0 - self.ui = None - self.call = None - self.doc = FreeCAD.ActiveDocument if self.doc: sel = FreeCADGui.Selection.getSelectionEx() if len(sel) == 1: @@ -242,12 +341,8 @@ class SelectPlane: self.display(plane.axis) self.finish() return - FreeCAD.activeDraftCommand = self - self.view = Draft.get3DView() - self.ui = FreeCADGui.draftToolBar self.ui.selectPlaneUi() msg(translate("draft", "Pick a face to define the drawing plane\n")) - self.ui.sourceCmd = self if plane.alignToSelection(self.offset): FreeCADGui.Selection.clearSelection() self.display(plane.axis) @@ -316,135 +411,25 @@ class SelectPlane: self.ui.wplabel.setText(plv+suffix) FreeCADGui.Snapper.setGrid() - def finish(self): - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - FreeCAD.activeDraftCommand = None - if self.ui: - self.ui.offUi() - #--------------------------------------------------------------------------- # Geometry constructors #--------------------------------------------------------------------------- - -class DraftTool: - "The base class of all Draft Tools" - - def __init__(self): - self.commitList = [] - - def Activated(self,name="None"): - if FreeCAD.activeDraftCommand: - FreeCAD.activeDraftCommand.finish() - - global Part, DraftGeomUtils - import Part, DraftGeomUtils - - self.ui = None - self.call = None - self.support = None - self.commitList = [] - self.doc = FreeCAD.ActiveDocument - if not self.doc: - self.finish() - return - - FreeCAD.activeDraftCommand = self - self.view = Draft.get3DView() - self.ui = FreeCADGui.draftToolBar - self.ui.sourceCmd = self - self.ui.setTitle(name) - self.ui.show() - rot = self.view.getCameraNode().getField("orientation").getValue() - upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) - plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) - self.node = [] - self.pos = [] - self.constrain = None - self.obj = None - self.extendedCopy = False - self.ui.setTitle(name) - self.featureName = name - #self.snap = snapTracker() - #self.extsnap = lineTracker(dotted=True) - self.planetrack = None - if Draft.getParam("showPlaneTracker"): - self.planetrack = PlaneTracker() - - def finish(self): - self.node = [] - #self.snap.finalize() - #self.extsnap.finalize() - FreeCAD.activeDraftCommand = None - if self.ui: - self.ui.offUi() - self.ui.sourceCmd=None - #self.ui.cross(False) - msg("") - if self.planetrack: - self.planetrack.finalize() - if self.support: - plane.restore() - FreeCADGui.Snapper.off() - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - self.call = None - if self.commitList: - todo.delayCommit(self.commitList) - self.commitList = [] - - def commit(self,name,func): - "stores actions to be committed to the FreeCAD document" - # print "committing" - self.commitList.append((name,func)) - - def getStrings(self): - "returns a couple of useful strings fro building python commands" - - # current plane rotation - p = plane.getRotation() - qr = p.Rotation.Q - qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')' - - # support object - if self.support: - sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' - else: - sup = 'None' - - # contents of self.node - points='[' - for n in self.node: - if len(points) > 1: - points += ',' - points += DraftVecUtils.toString(n) - points += ']' - - # fill mode - if self.ui: - fil = str(bool(self.ui.fillmode)) - else: - fil = "True" - - return qr,sup,points,fil - class Creator(DraftTool): "A generic Draft Creator Tool used by creation tools such as line or arc" def __init__(self): DraftTool.__init__(self) - - def IsActive(self): - if FreeCADGui.ActiveDocument: - return True - else: - return False + def Activated(self,name="None"): + DraftTool.Activated(self) + self.support = getSupport() + class Line(Creator): "The Line FreeCAD command definition" def __init__(self, wiremode=False): + Creator.__init__(self) self.isWire = wiremode def GetResources(self): @@ -508,7 +493,8 @@ class Line(Creator): if (arg["Position"] == self.pos): self.finish(False,cont=True) else: - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) point,ctrlPoint,info = getPoint(self,arg) self.pos = arg["Position"] self.node.append(point) @@ -632,7 +618,8 @@ class BSpline(Line): if (arg["Position"] == self.pos): self.finish(False,cont=True) else: - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) point,ctrlPoint,info = getPoint(self,arg) self.pos = arg["Position"] self.node.append(point) @@ -822,7 +809,8 @@ class Rectangle(Creator): if (arg["Position"] == self.pos): self.finish() else: - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) point,ctrlPoint,info = getPoint(self,arg) self.appendPoint(point) @@ -1019,7 +1007,8 @@ class Arc(Creator): if not DraftVecUtils.isNull(viewdelta): point = point.add(DraftVecUtils.neg(viewdelta)) if (self.step == 0): # choose center - self.support = getSupport(arg) + if not self.support: + self.support = getSupport(arg) if hasMod(arg,MODALT): snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1])) if snapped: @@ -1288,7 +1277,8 @@ class Polygon(Creator): if not DraftVecUtils.isNull(viewdelta): point = point.add(DraftVecUtils.neg(viewdelta)) if (self.step == 0): # choose center - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) if hasMod(arg,MODALT): snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1])) if snapped: @@ -1635,7 +1625,8 @@ class Dimension(Creator): elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): point,ctrlPoint,info = getPoint(self,arg) - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) if hasMod(arg,MODALT) and (len(self.node)<3): print "snapped: ",info if info: diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index 47e378a348..1d9ba2b6cf 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -640,24 +640,16 @@ class gridTracker(Tracker): def getClosestNode(self,point): "returns the closest node from the given point" + print "in:",point # get the 2D coords. - point = FreeCAD.DraftWorkingPlane.projectPoint(point) - u = DraftVecUtils.project(point,FreeCAD.DraftWorkingPlane.u) - lu = u.Length - if u.getAngle(FreeCAD.DraftWorkingPlane.u) > 1.5: - lu = -lu - v = DraftVecUtils.project(point,FreeCAD.DraftWorkingPlane.v) - lv = v.Length - if v.getAngle(FreeCAD.DraftWorkingPlane.v) > 1.5: - lv = -lv - # print "u = ",u," v = ",v - # find nearest grid node - pu = (round(lu/self.space,0))*self.space - pv = (round(lv/self.space,0))*self.space - rot = FreeCAD.Rotation() - rot.Q = self.trans.rotation.getValue().getValue() - return rot.multVec(Vector(pu,pv,0)) - + # point = FreeCAD.DraftWorkingPlane.projectPoint(point) + pt = FreeCAD.DraftWorkingPlane.getLocalCoords(point) + pu = (round(pt.x/self.space,0))*self.space + pv = (round(pt.y/self.space,0))*self.space + pt = FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(pu,pv,0)) + print "out:",pt + return pt + class boxTracker(Tracker): "A box tracker, can be based on a line object" def __init__(self,line=None,width=0.1,height=1): @@ -719,3 +711,27 @@ class boxTracker(Tracker): self.update() else: return self.cube.depth.getValue() + +class radiusTracker(Tracker): + "A tracker that displays a transparent sphere to inicate a radius" + def __init__(self,position=FreeCAD.Vector(0,0,0),radius=1): + self.trans = coin.SoTransform() + self.trans.translation.setValue([position.x,position.y,position.z]) + m = coin.SoMaterial() + m.transparency.setValue(0.9) + m.diffuseColor.setValue([0,1,0]) + self.sphere = coin.SoSphere() + self.sphere.radius.setValue(radius) + self.baseline = None + Tracker.__init__(self,children=[self.trans,m,self.sphere]) + + def update(self,arg1,arg2=None): + if isinstance(arg1,FreeCAD.Vector): + self.trans.translation.setValue([arg1.x,arg1.y,arg1.z]) + else: + self.sphere.radius.setValue(arg1) + if arg2 != None: + if isinstance(arg2,FreeCAD.Vector): + self.trans.translation.setValue([arg2.x,arg2.y,arg2.z]) + else: + self.sphere.radius.setValue(arg2) diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index e41eaabf76..6c1d6066b9 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -86,8 +86,29 @@ class plane: def projectPoint(self, p, direction=None): '''project point onto plane, default direction is orthogonal''' - if not direction: direction = self.axis + if not direction: + direction = self.axis + lp = self.getLocalCoords(p) + gp = self.getGlobalCoords(Vector(lp.x,lp.y,0)) + a = direction.getAngle(gp.sub(p)) + if a > math.pi/2: + direction = DraftVecUtils.neg(direction) + a = math.pi - a + ld = self.getLocalRot(direction) + gd = self.getGlobalRot(Vector(ld.x,ld.y,0)) + hyp = abs(math.tan(a) * lp.z) + return gp.add(DraftVecUtils.scaleTo(gd,hyp)) + + def projectPointOld(self, p, direction=None): + '''project point onto plane, default direction is orthogonal. Obsolete''' + if not direction: + direction = self.axis t = Vector(direction) + #t.normalize() + a = round(t.getAngle(self.axis),DraftVecUtils.precision()) + pp = round((math.pi)/2,DraftVecUtils.precision()) + if a == pp: + return p t.multiply(self.offsetToPoint(p, direction)) return p.add(t) @@ -196,6 +217,31 @@ class plane: def getLocalCoords(self,point): "returns the coordinates of a given point on the working plane" + pt = point.sub(self.position) + xv = DraftVecUtils.project(pt,self.u) + x = xv.Length + if xv.getAngle(self.u) > 1: + x = -x + yv = DraftVecUtils.project(pt,self.v) + y = yv.Length + if yv.getAngle(self.v) > 1: + y = -y + zv = DraftVecUtils.project(pt,self.axis) + z = zv.Length + if zv.getAngle(self.axis) > 1: + z = -z + return Vector(x,y,z) + + def getGlobalCoords(self,point): + "returns the global coordinates of the given point, taken relatively to this working plane" + vx = DraftVecUtils.scale(self.u,point.x) + vy = DraftVecUtils.scale(self.v,point.y) + vz = DraftVecUtils.scale(self.axis,point.z) + pt = (vx.add(vy)).add(vz) + return pt.add(self.position) + + def getLocalRot(self,point): + "Same as getLocalCoords, but discards the WP position" xv = DraftVecUtils.project(point,self.u) x = xv.Length if xv.getAngle(self.u) > 1: @@ -210,13 +256,14 @@ class plane: z = -z return Vector(x,y,z) - def getGlobalCoords(self,point): - "returns the global coordinates of the given point, taken relatively to this working plane" + def getGlobalRot(self,point): + "Same as getGlobalCoords, but discards the WP position" vx = DraftVecUtils.scale(self.u,point.x) vy = DraftVecUtils.scale(self.v,point.y) vz = DraftVecUtils.scale(self.axis,point.z) - return (vx.add(vy)).add(vz) - + pt = (vx.add(vy)).add(vz) + return pt + def getClosestAxis(self,point): "returns which of the workingplane axes is closest from the given vector" ax = point.getAngle(self.u) From fc46ef0f1e981eca3ea488072ae35c96a06d06dc Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 7 Jun 2012 18:23:50 -0300 Subject: [PATCH 68/69] Draft: Removed debug message --- src/Mod/Draft/DraftTrackers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index 1d9ba2b6cf..c36d031522 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -640,14 +640,12 @@ class gridTracker(Tracker): def getClosestNode(self,point): "returns the closest node from the given point" - print "in:",point # get the 2D coords. # point = FreeCAD.DraftWorkingPlane.projectPoint(point) pt = FreeCAD.DraftWorkingPlane.getLocalCoords(point) pu = (round(pt.x/self.space,0))*self.space pv = (round(pt.y/self.space,0))*self.space pt = FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(pu,pv,0)) - print "out:",pt return pt class boxTracker(Tracker): From 68ac7f303f4e3eaf8a90dce2c304f23c69f03f5f Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 7 Jun 2012 19:58:56 -0300 Subject: [PATCH 69/69] Draft: remaining fixes --- src/Mod/Draft/DraftSnap.py | 22 ++++++++++++---------- src/Mod/Draft/DraftTools.py | 2 ++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 3e1673460e..ee2e2aab97 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -339,7 +339,7 @@ class Snapper: if view.getCameraType() == "Perspective": camera = view.getCameraNode() p = camera.getField("position").getValue() - vd = pt.sub(Vector(p[0],p[1],p[2])) + dv = pt.sub(Vector(p[0],p[1],p[2])) else: dv = view.getViewDirection() return FreeCAD.DraftWorkingPlane.projectPoint(pt,dv) @@ -390,15 +390,17 @@ class Snapper: else: if self.isEnabled('parallel'): if last: - de = Part.Line(last,last.add(DraftGeomUtils.vec(e))).toShape() - np = self.getPerpendicular(de,point) - if (np.sub(point)).Length < self.radius: - if self.tracker: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['parallel']) - self.tracker.on() - self.setCursor('parallel') - return np,de + ve = DraftGeomUtils.vec(e) + if not DraftVecUtils.isNull(ve): + de = Part.Line(last,last.add(ve)).toShape() + np = self.getPerpendicular(de,point) + if (np.sub(point)).Length < self.radius: + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['parallel']) + self.tracker.on() + self.setCursor('parallel') + return np,de return point,eline def snapToPolar(self,point,last): diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 9f31ff1e81..0407369277 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -3823,6 +3823,8 @@ class ToggleGrid(): else: FreeCADGui.Snapper.grid.on() FreeCADGui.Snapper.forceGridOff=False + else: + FreeCADGui.Snapper.show() #--------------------------------------------------------------------------- # Adds the icons & commands to the FreeCAD command manager, and sets defaults