#!/usr/bin/python # -*- coding: utf-8 -*- #**************************************************************************** #* * #* Kicad STEPUP (TM) (3D kicad board and models to STEP) for FreeCAD * #* 3D exporter for FreeCAD * #* Kicad STEPUP TOOLS (TM) (3D kicad board and models to STEP) for FreeCAD * #* Copyright (c) 2015 * #* Maurice easyw@katamail.com * #* * #* Kicad STEPUP (TM) is a TradeMark and cannot be freely usable * #* * #* code partially based on: * #* Printed Circuit Board Workbench for FreeCAD FreeCAD-PCB * #* Copyright (c) 2013, 2014, 2015 * #* marmni * #* * #* and IDF import for FreeCAD * #* (c) Milos Koutny (milos.koutny@gmail.com) 2012 * #* and (c) hyOzd ecad-3d-model-generator * #* * #* this macro rotates, translates and scales one object * #* scale for VRML export and open footprint for easy alignment * #* this sw is a part of kicad StepUp code * #* all credits and licence details in kicad StepUp code * #* Macro_Move_Rotate_Scale * #* ver in ___ver___ * #* Copyright (c) 2015, 2016, 2017 * #* Maurice easyw@katamail.com * #* * #* Collisions routines from Highlight Common parts Macro * #* author JMG, galou and other contributors * #* from FreeCAD OpenSCAD Workbench - 2D helper functions * #* __author__ = "Sebastian Hoogen" * #* * #* semantic parser from __author__ = "Zheng, Lei" fcad_pcb * #* * #* IDF_ImporterVersion="3.9.2" #* ignoring step search associations (too old models) #* displaying Flat Mode models #* checking version 3 for both Geometry and Part Number #* supporting Z position #* skipping PROP in emp file #* adding color to shapes opt IDF_colorize #* adding emp library/single model load support #* aligning IDF shape to both Geom and PartNBR for exactly match #* to do: .ROUTE_OUTLINE ECAD, .PLACE_OUTLINE MCAD, .ROUTE_KEPOUT ECAD, .PLACE_KEEPOUT ECAD #**************************************************************************** #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Affero General Public License * #* as published by the Free Software Foundation to ensure cooperation * #* with the community in the case of network server software; * #* for detail see the LICENCE text file. * #* http://www.gnu.org/licenses/agpl-3.0.en.html * #* Moreover you have to include the original author copyright * #* kicad StepUP made by Maurice easyw@katamail.com * #* * #* 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., * #* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * #* * #**************************************************************************** ##With kicad StepUp you’ll get an exact representation of your physical board in Native 3D PCB ## kicad StepUp tools ##done # upgrade kicadstepup version # resized font size # add bbox and volume images on the starter guide # add ksu config more detailed description # complete volume_minimum config in doc # improved OSX QtGui File Open # better arc and line import # remove test button # enable confirm on exit # replace FreeCAD.Console.Message -> say ##todo list # collision and proximity as microelly ## kicad StepUp # added messages on missing emn files # added messages on missing models # added path to adapt your KISYS3DMOD # added blacklist for unwanted modules # added messages on blacklisted modules # added pcb color attribute # added bounding box option # added bounding box white list to leave real model on connector or peripheral models # added auxorigin, base origin, base point placement option # added vrml models z-rotation angle # added virtual models option # added fusion export option # added saving in native format, export to STEP # added arcs and circles for calculate board position # added idf_to_origin flag for version >6091 # added reset properties for FC 016 bug # added ${KIPRJMOD} support # added v3,v4 pcb version support # added multi 3D vrml model support # added compatibility to kicad version >=3 # added auto color assigning in bboxes # added minimum volume per model # added minimum height per model # updated findPcbCenter method # added support for .stp extension beside .step # added support for .igs extension beside .step # added support for .iges extension beside .step # because of hole sovrapposition prob... # cutting hole by hole instead of hole compound # added holes_solid var # to have holes as solid to guarantee cutting # handled single circle # used OpenSCAD2Dgeom instead of wire + face (best option) # http://www.freecadweb.org/wiki/index.php?title=Macro_Creating_faces_from_a_DXF_file # fixed unicode text parsing # double option .kicad_pcb .emn # in case of non coincidences .emn is more tolerant # try to build wires on closed shaped for make the cutting faster # try to optimize cutting changing creation/type of holes # manage bklist and volume # accept with or without /\ at the end of 3Dpath # search models in KIPRJMOD and in KISYS3DMOD # removed unicode chars in .kicad_pcb # exported wrl, step from python # reload & display ini cfg file # display/edit ini file with syntax highlight # msg first ksu config # added warning for import step multi part fixed v3035 # commented warning in load footprint and in placing step mod if x,y are different from 0 0 0 # added message if scale are different from 1 1 1 # non stopping warning for footprint # added command line args to load board(/emn) # avoid argv in memory in case of opened from command line # used multi cut also for footprint too # enabled loadB, loadI, loadF with filename=None to align Mod and Macro # enabled Macro & Mod # added ico tools info # added checkbox export_2_step # added export2STEP var in ini file # subst print to say # fixed cursor wait # angle on pads for footprint pads angle i.e. DB25M_V ok # improved multipart load checking # added virtual checkbox # added VRML with material properties exporter # added metal grey material # added multipart VRML option # improved export resolution from %.3f, %.5f to %g # added config for material props # pad & holes as circles when possible # improved multipart load checking # removed illegal characters in filenames when exporting VRML and STEP # message of missing models at the end # implemented caching for 3D models # optimized for fusion w/ colors # added support for alias i.e. :Kicad3D: as for environment variables # added crease angle for wrl export # added support for ${KISYS3DMOD}/ in new 3d viewer path resolver # accepting .wrl .step .stp .iges .igs as 3d models directly in kicad_pcb # added fixedPosition for aligning part to footprint in assembly2 # message if scale values are not assigned to 1 1 1 # added support for wrl offset in position & rotation when loadboard # added bbox creation based on scaled values (1mm as base unit) # added height and volume blacklist for scaled shapes # added error message in case of scale factor not 1:1 for non scaled box # fixed base point for lower case settings # added single instance # added open file type 'rb' 'ab' and write file 'wb' type binary for utf8 full support # added fix to rect pads in footprint loader # non blocking warning only for scale <> 1 # fixed minor issues in FC015 loading fp # added models3Dprefix as default saving location # added $HOME support for unix systems (it doesn't resolve on win) # added OCC >=7 FC 0.17 compatibility for Footprint pads, lines, arcs # added check if the models3Dprefix is writable, otherwise will write on $HOME # added 2nd path to resolver prefix3d_2 # added wrl materials dialog theme compatibility # reduced export file size # accept the new Part.LineSegment using yorik help function FC 0.17 >= 9123 # EdgeCuts for footprint will generate a path on FP loader # EdgeCuts in footprint will generate Cuts in Board # accepted utf8 for model in kysys3mod or alias # checked spaces in name # added edge tolerance on vertex coincidence # added /n to say() and removed extra /n in each say msg # removed print in all doc ()9255 # message when adjusting edges # animation when assembled # moved config read-write to my own method for full utf-8 support # accept utf8 on 3D prefix path # accept utf8 on saving loading last path # tidy up a bit the utf-8 code # regenerate ksu-config if too short # enabled upper case on config # added message on edge not coincident # added turntable section # added 'no layers' for pads # allowed STEP multipart (creating compound) # added exception on import step model # added better theme integration # improved tolerance on edges <0.005 #edge coincidence tolerance (5000nm = 1/2 centesimo) base is mm # updated import IDF mod # added font size in config # added improved way to load step files as compound from vejmarie # accepted lower and upper case for extension (linux is case sensitive os) # added align_vrml_step_colors when saving with material properties # assigned shininess and specular color for single color Shapes # fixed position for rotated fp-modules with offset # fixed position for all rotation and offset # fixed edge cuts for bottom modules # fixed circle in circle edges # added tolerance warning > 1e-6 #(1nm) base is mm # substituted (== None) -> (is None) # disabling VBO during loading modules to avoid crashing (new bug from 0.17 after 10101) # disabling Part-o-Magic observer if exists (not possible without calling the PoM function) # added full unicode support for names in compounds # removed activateWorkbench("PartWorkbench") if Assembly2 is active # added Glass materials to exporting # added virtual parts skipped message # added recursive App::Part single copy (thx nico!) to compatibility with FC > 10647 # implemented App::Part also for moving/rotating obj and exporting model # added support for opening footprints kicad_mod from command line # disabling VBO during loading footprints to avoid crashing (new bug from 0.17 after 10101) # removed IDF import # removed Move X,Y,Z buttons # resetP always enabled # collision cb, virtual cb, expstep cb, reset placement cb, setGeometry # textEdit -> textBrowser for html links to tutorials # link to local config file on disk # moved to icon based GUI # minor dock fixing # temporary_undock to enable dock left & right memory on edit/help # fusion & compound buttons (App::Part compatible) # handling bad fusion redirect to compound # added toggle on minimize button # fixed Qt5 OSX issues # removed App. changed to FreeCAD in remaining instances # fixed App::Part relative placement # started moving toward compatibility to py3 # small fix to avoid VRML selection when exporting # added pcb colors for exporting # fixed bug on multiple mixed models per footprint position/rotation # compatibility py3 # added Step_Virtual_Models folder for mechanical parts # added workaround for "bug for ImportGui *.iges" -> using "Part.insert" # cleaned License for App::Part containers (.License .LicenseURL) # added warning_nbr in case of too many missing models # added reset placement before exporting wrl & STEP model # added warning in case of drill and volume used in config # added ReadShapeCompoundMode parameter for step import shapes with colors on LoadBoard and (+)Import Step # added ReadShapeCompoundMode var also when (+) importing step models # added force_oldGroups to force old method if set to True # use_AppPart instead of Groups for FC0.17 > FC_export_min_version="11670" # managed Export2MCAD for exporting hierarchy and not if fused selected # added semantic parser to speed up .kicad_pcb parsing up to 70 times faster for pcb loading # added Top & Bottom containers for STEP models # added allow_compound mode 'simplify' to enable fast loading (losing colors) # removing empty Top/Bot containers after loading models (only on FC 0.17) # added relative 3d path '../' # added F.Fab F.CrtYd to loadFootprint function # added StepUp WorkBench # fixed utf-8 str to Label # recurse placement for Collision check ['Placement.multiply']: now it works also with App::Part, Body & Compound in FC0.17 # added links to pdf files in Help # added config value for autoconstraints in sketcher # auto constraint coincident points and vertical/horizontal # some cleaning on warn messages for wrong scale, missing models # boost in cutting drills # added sketch button # started experimental Sketch support # improve help and add sketch functionality # auto constraint coincident points and vertical/horizontal # edge_width from kicad_pcb # managed not supported geometry BSplines & Ellipses with optimized deviation # loop in App.ActiveDocument.PCB_Sketch.Geometry searching for coincident vertex # aligned Sketch to center of A4 in pcbnew if first time # removing sketches from export 3d to step button # removing sketches in FC0.16 and when exporting automatically # added use grid_origin as reference point for placing the board and for sketch!!! # this will allow to copy sketches between boards releases to keep constraints # managed write permissions error message # fixed App::Part list inverted after FC 12090 https://github.com/FreeCAD/FreeCAD/pull/916 # fixed case of pcb with one drill only # minor fix when exporting wrl from multi objects # fixed tabify # added better support for Body (hidden Parts) # fixed a regression in Sketch # fixed Sketch inverted # converted Bspline to Arcs https://github.com/FreeCAD/FreeCAD/commit/6d9cf80 # fixed Arc orientation when creating Sketch from dxf,svg # improved FC0.16 compatibility # aligned GridOrigin also in case of Sketch copy/paste from empty board # aligned pushing pcb to GridOrigin from empty Sketch/Board (only GridOrigin set) # removed draftify bugged function # added support for arc of 360 deg # added oval exception if only one value is done # starting py3 compatibility # added offset in mm after kicad_pcb version 20171114 # moved edgestofaces to internal function # fixed ellipses # added new materials # improved bspline to arcs # started footprint exporter (smd [rect, oval, circle]) # added Round Rectangles and Poly Lines (for RF design) # simple copy button added # improved TH, NPTH and SMD pads # bspline allowed on footprint F_Silks # solder Mask Zones for Polylines # ZLength for BBox height # added Check for Solid property # skipped Points in geometry of sketch # added loading pad poly in fp # fixed simplify sketch # improved Qt5 compatibility # added workaround for STEP exporting OCC 7.2.0 # improved STEP exporting with hierarchy, onelevel, flat options # added QtGui.QApplication.processEvents() for Qt5 # skipping \" characters # added 'links' for import mode settings # moved the generation of PCB inside the Sketch to Face process # adding Geometry and Constraints as a single instruction to avoid long delay with sketches # added Constrainator # allowed ArcOfCircle for Polyline Pads # roundrect pads for import footprint supported # assigned combobox to defined colors # improved generation of complex footprint with arcs # partially implemented Circle Geometry primitive # improved writing fp data # push Moved 3D model(s) to kicad PCB # sync Reference in case of lost correct Label (import export STEP file with Links) # improved precision on board data using "{:.3f}".format for pushpcb & Pushfootprint and angles # re-introduced ability to use footprints with edge cuts inside # restored ability to load pcb edge from footprint instead of Sketch # added option (not used) to simplify compsolid to solid # added support for pcb reading gr_poly on Edge.Cuts # improved fp parsing for custom geo # improved fp parsing for custom geo again # first implementation of bspline edge import (TBD: spline w control points, push to pcb) # pushpcb working with gr_curves, downgrading to arcs and lines (TBD: use spline) # spline w control points # fixing pushing pcb # adding support for double quotes on kicad_pcb and kicad_mod format # bsplines allowed for push&pull kicad_pcb # fixed freezing issue on AppImages (thread) # improved simplify sketch # workaround force saving before exporting VRML (win freeze bug) # importing custom Geo also for bottom layer # fixed wbs ordering # added edges2sketch function # local coordinate system reference added # improved search for 3d models on local path without using KIPRJMOD # initial support for App::Link & App::LinkGroup # added 3d model position pull&push update # added Sketches pull&push update # added 'stpZ', 'wrz' compressed files support # hide main kSU dialog unless when opening a footprint model # full import of kicad_pcb allowed # generating uid for pcb & containers # pcb as solid (removed compsolid) # applying transparency in case of LED or GLASS material found in wrl/wrz file model # most clean code and comments done ##todo ## collaps the App::LinkGroups at the end of loading ## multi-board compatibility with asm3 A3 ## check "{:.3f}".format for pushpcb & Pushfootprint ## check utf-8 directories and spaces compatibility ## add edit and help to WB menu (self unresolved) ## copy objects and apply absolute placement to each one, then check collisions ## remove print and makesketch ## remove print say etc, remove shape show, remove extra import, remove extra functions FC_016 ## App -> FreeCAD # done: allow bspline on fp generator # done: completing py3 compatibility ## started to implement isInside using bboxes to check if drills are nested, and then ## use dxf2face to extrude pcb instead of actual cutting (it should be much faster again) ## or use a python feature as in pcb for having the board updated live ## approximate arcs from segments https://www.freecadweb.org/wiki/Macro_EdgesToArc ## add AP214-203 settings read,set,restore both on step-step(h) ## when exporting step and step+wrl ## ... there is a mess in Tool variables after jm pr910 ## use float in all data from semantic parser !!!!! ## export AppPart groups in a single STEP file with hierarchy also when fuseall is selected ## evaluate to add comment line and behavior for font_size = 0 for default size ## evaluate makeCompound as comment line and option beside fuseAll (not useful) ## check basepoint ## simplify pdf manual and update internal help ## assign shininess and specular color for faces? available? ## adding top bottom lights ## load local config if exists ## add transp 25 50 75 to asis in exp wrl or keep transp ? ### knowing issues ## VBO with multipart crash on loading parts -> disabled when loading ## Part-oMagic trigs errors on loading parts -> disabled when loading # none atm :) # use isInside/common ( TopoShape ) to cut only intersection objs # multi board # test option placement # check line 772 abs ZMax = height? -> ZLength # fix fonts for html and new buttons # ... # respect transparency on shapes NOT possible on STEP objects because of FC # pad type trapez, rect rounded ## import statements import FreeCAD,FreeCADGui,Part,Mesh #import PySide from collections import namedtuple import PySide from PySide import QtGui, QtCore from TranslateUtils import translate QtWidgets = QtGui from time import sleep from math import sqrt, tan, atan, atan2, degrees, radians, hypot, sin, cos, pi, fmod import Draft, Part, DraftVecUtils #from Draft import * from FreeCAD import Vector import Sketcher from collections import namedtuple from FreeCAD import Base import sys, os from os.path import expanduser import shutil from shutil import copyfile import tempfile, errno import re import time max_recursion_limit=5000 # kSU issue#198 sys.setrecursionlimit(max_recursion_limit) import ksu_locator if (sys.version_info > (3, 0)): #py3 import builtins as builtin #py3 import gzip as gz else: #py2 import __builtin__ as builtin #py2 try: import gzip_utf8 as gz except: FreeCAD.Console.PrintError("'.stpZ' not supported") pass import zipfile as zf # sys.path.append('C:\Cad\Progetti_K\3D-FreeCad-tools/') #sys.path.append(os.path.realpath(__file__)) #workaround to test OpenSCAD2DgeomMau #import OpenSCAD2DgeomMau # #reload(OpenSCAD2DgeomMau) #import OpenSCAD2Dgeom import ImportGui from math import sqrt, atan, sin, cos, radians, degrees, pi import argparse from threading import Timer #try: # import __builtin__ as builtin #py2 #except: # import builtins as builtin #py3 if FreeCAD.GuiUp: from PySide import QtCore, QtGui # import OpenSCADFeatures import DraftGeomUtils #from DraftGeomUtils import * from pivy.coin import SoDirectionalLight import codecs #utf-8 config parser import io #import configparser #utf-8 #from codecs import open #maui to verify import unicodedata pythonopen = builtin.open # to distinguish python built-in open function from the one declared here ## Constant definitions ___ver___ = "12.0.1" __title__ = "kicad_StepUp" __author__ = "maurice & mg" __Comment__ = 'Kicad STEPUP(TM) (3D kicad board and models exported to STEP) for FreeCAD' ___ver_ksu___ = "4.1.3.0 April 2017" ___ver_GUI___ = "ksu-docked-v3.2" IDF_ImporterVersion="3.9.2" __Icon__ = "stepup.png" global userCancelled, userOK, show_mouse_pos, min_val, last_file_path, resetP global start_time, show_messages global show_messages, applymaterials global real_board_pos_x, real_board_pos_y, board_base_point_x, board_base_point_y global ksu_config_fname, ini_content, configFilePath global models3D_prefix, models3D_prefix2, models3D_prefix3, models3D_prefix4 global blacklisted_model_elements, col, colr, colg, colb global bbox, volume_minimum, height_minimum, idf_to_origin, aux_orig global base_orig, base_point, bbox_all, bbox_list, whitelisted_model_elements global fusion, addVirtual, blacklisted_models, exportFusing, min_drill_size global last_fp_path, last_pcb_path, plcmnt, xp, yp, exportFusing, exportS global full_placement, shape_col, align_vrml_step_colors global timer_Collisions, last_3d_path, expanded_view, mingui global textEdit_dim_base, textEdit_dim_hide #textEdit dimensions for hiding showing text content global warning_nbr, original_filename, edge_width, load_sketch, grid_orig, dvm, pt_osx, pt_lnx, dqd, running_time global addConstraints, precision, conv_offs, maxRadius, pad_nbr, use_pypro, accept_spline, maxDegree, maxSegments global zfit zfit = False maxRadius = 500.0 # 500mm maxRadius of arc from bspline pad_nbr = 1 #first nbr of fp pads use_pypro = False #False #enable/disable timestamp as python property; False=disabled -> use the Label accept_spline = True # include spline in PCB_Sketch for KiCAD >= v5.1 maxDegree = 3 # spline degree for KiCAD integration maxSegments = 999 #for splines conv_offs = 1.0 #conversion offset from decimils to mm pcb version >= 20171114 original_filename="" edge_width = None load_sketch=True dvm = 3.0 # obsolete: discretizer multiplier factor dqd = 0.02 #discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the edge (faster) precision = 0.1 # precision in spline or bezier conversion pt_osx=False #platform OSX pt_lnx=False #platform Linux running_time = 0 warning_nbr=10 #if missing more than 'warning_nbr' models, a warning will raise #set to -1 for skipping the test timer_Collisions= 3000 # ms mingui = 0 #Gui status: mingui = 1 -> minimized last_3d_path=u'' expanded_view=0 # 0=not expanded; 1 edit expanded; 2 help expanded shape_col=(1.0, 0.0, 0.0) align_vrml_step_colors=True textEdit_dim_base=(176,36,305,453) #default value textEdit_dim_hide=(30000,30000,0,0) #hide value fake position last_pcb_path=u'' #py3 exportS=True last_file_path='' pcb_path =u'' resetP=True global rot_wrl, test_flag, test_flag_pads rot_wrl=0.0 #global module_3D_dir userCancelled = "Cancelled" userOK = "OK" show_mouse_pos = True #module_3D_dir="C:/Cad/Progetti_K/a_mod" min_val=0.001 conflict_tolerance=1e-6 #volume tolerance #edge_tolerance=0.005 #edge coincidence tolerance (5000nm = 1/2 centesimo) base is mm edge_tolerance=0.01 #edge coincidence tolerance (500nm = 0.1 decimo) base is mm edge_tolerance_warning = 1e-6 #(1nm) base is mm apply_edge_tolerance = False #True simplifyComSolid = False #True this can be quite time consuming font_size=8 bbox_r_col=(0.411765, 0.411765, 0.411765) #dimgrey bbox_c_col=(0.823529, 0.411765, 0.117647) #chocolate bbox_x_col=(0.862745, 0.862745, 0.862745) #gainsboro bbox_l_col=(0.333333, 0.333333, 0.333333) #sgidarkgrey bbox_IC_col=(0.156863, 0.156863, 0.156863) #sgiverydarkgrey bbox_default_col=(0.439216, 0.501961, 0.564706) #slategrey mat_section=u""" [Materials] mat = enablematerials ;; VRML models to be or not exported with material properties ;mat = enablematerials\n;mat = nomaterials """ dock_section=u""" [docking] dkmode = float ;;docking mode ;dkmode = left ;dkmode = right ;dkmode = float """ turntable_section=u""" [turntable] spin = enabled ;;turntable spin after loading ;spin = disabled ;spin = enabled """ compound_section=u""" [compound] compound = allowed ;;allow compound for STEP models ;compound = allowed ;compound = disallowed ;compound = simplified """ constraints_section=u""" [sketch_constraints] constraints = all ;constraints = all ;constraints = coincident ;constraints = none ;;constraints generated for pcb sketch """ exporting_mode_section=u""" [step_exporting_mode] exporting_mode = hierarchy ;exporting_mode = hierarchy ;exporting_mode = flat ;exporting_mode = onelevel ;;step exporting mode """ font_section=u""" [fonts] font_size = 8 ;;font size for ksu widget """ links_importing_mode_section=u""" [links_importing_mode] importing_mode = standard ;importing_mode = links ;importing_mode = standard ;;models importing mode: use Assembly3 Links or Standard mode """ global ini_vars, num_min_lines ini_vars=[] for i in range (0,20): ini_vars.append('-') num_min_lines=22 #min numbers of ini lines for a ksu-config file #FreeCAD.Console.PrintError(len(ini_vars)) #FreeCAD.Console.PrintWarning('\n') global extrude_holes test_flag=False #test_flag=True test_flag_exit=False #True 4 testing test_flag_pads=False #True 4 testing remove_pcbPad=True #False 4 testing close_doc=False show_border=False #False show_data=False #False show_debug=False #False show_shapes=False disable_cutting=False # enable_materials=True not used test_extrude=False #False holes_solid=False #False #cutting sketch face with faces or solid drills (sd very intensive i.e. with drills=-1 (vias) + memory leak) extrude_holes = True # fixing coplanar shapes cut for holes emn_version=3.0 show_messages=True #False 4 testing #show_messages=False # mauitest full_placement=True # for offset in xyz and rotation in xyz # FreeCAD.Console.PrintWarning(full_placement) # FreeCAD.Console.PrintWarning('\n') global disable_VBO disable_VBO = True # False to test crash if disable_VBO == False: FreeCAD.Console.PrintWarning("VBO check disabled\n") global disable_PoM_Observer disable_PoM_Observer = False try: import PartOMagic import PartOMagic.Gui.Observer as Observer disable_PoM_Observer = True FreeCAD.Console.PrintWarning("PoM present\n") except: FreeCAD.Console.PrintWarning("PoM not present\n") ## def say_inline(msg): FreeCAD.Console.PrintMessage(msg+' ') def say(msg): FreeCAD.Console.PrintMessage(msg) FreeCAD.Console.PrintMessage('\n') def sayw(msg): FreeCAD.Console.PrintWarning(msg) FreeCAD.Console.PrintWarning('\n') def sayerr(msg): FreeCAD.Console.PrintError(msg) FreeCAD.Console.PrintMessage('\n') ## global use_AppPart, use_Links, use_LinkGroups use_AppPart=False # False use_Links=False use_LinkGroups = False if 'LinkView' in dir(FreeCADGui): prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if prefs.GetBool('asm3_linkGroups'): use_LinkGroups = True use_Links=True #False sayw('using \'LinkGroups\' and \'Links\'') elif prefs.GetBool('asm3_links'): use_Links=True #False sayw('using \'Part\' container and \'Links\'') else: use_LinkGroups = False sayw('using \'Part\' container') else: use_LinkGroups = False sayw('using \'Part\' container') # global FC_export_min_version FC_export_min_version="11670" #11670 latest JM def ZoomFitThread(): FreeCAD.Console.PrintWarning('thread ViewFitting\n') if FreeCAD.ActiveDocument is not None: FreeCADGui.SendMsgToActiveView("ViewFit") #stop def collaps_Tree(): FreeCAD.Console.PrintWarning('thread Collapsing\n') if FreeCAD.ActiveDocument is not None: mw1 = FreeCADGui.getMainWindow() treesSel = mw1.findChildren(QtGui.QTreeWidget) for tree in treesSel: items = tree.selectedItems() for item in items: if item.isExpanded() == True: collapse = True print ("collapsing") tree.collapseItem(item) FreeCADGui.Selection.clearSelection() # ##-------------------------------------------------------------------------------------- py2=False try: ## maui py3 unicode = unicode except NameError: # 'unicode' is undefined, must be Python 3 str = str unicode = str bytes = bytes basestring = (str,bytes) else: # 'unicode' exists, must be Python 2 str = str unicode = unicode bytes = str basestring = basestring py2=True def isConstruction(geo): if hasattr(geo,'Construction'): # print(geo.Construction,'geo.Construction') return geo.Construction else: # print('geo.Content',geo.Content) if 'geometryModeFlags="00000000000000000000000000000010"' in geo.Content: # print('geometryModeFlags', True) return True else: # print('geometryModeFlags', False) return False # PY3 = sys.version_info[0] == 3 # maui @realthunder fcad_pcb py3 if PY3: string_types = str, else: string_types = basestring, ### # sexp # maui import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser ### def rotatePoint(r,sa,da,c): # sa, da in degrees x = c[0] - cos(radians(sa+da)) * r y = c[1] - sin(radians(sa+da)) * r return [x,y] ### def getFCversion(): FC_majorV=int(float(FreeCAD.Version()[0])) FC_minorV=int(float(FreeCAD.Version()[1])) try: FC_git_Nbr=int (float(FreeCAD.Version()[2].strip(" (Git)").split(' ')[0])) #+int(FreeCAD.Version()[2].strip(" (Git)").split(' ')[1]) except: FC_git_Nbr=0 return FC_majorV,FC_minorV,FC_git_Nbr FC_majorV,FC_minorV,FC_git_Nbr=getFCversion() FreeCAD.Console.PrintWarning('FC Version '+str(FC_majorV)+str(FC_minorV)+"-"+str(FC_git_Nbr)+'\n') if FC_majorV == 0 and FC_minorV == 17: if FC_git_Nbr >= int(FC_export_min_version): use_AppPart=True #if FreeCAD.Version()[2] == 'Unknown': #workaround for local building # use_AppPart=True if FC_majorV > 0: use_AppPart=True if FC_majorV == 0 and FC_minorV > 17: #if FC_git_Nbr >= int(FC_export_min_version): use_AppPart=True #if use_AppPart: # FreeCAD.Console.PrintWarning("creating hierarchy\n") if int(FC_majorV) <= 0: if int(FC_minorV) == 15: load_sketch=False global force_oldGroups force_oldGroups=False # False try: from freecad.asm3 import assembly as asm #use_Links=True #False FreeCAD.Console.PrintWarning('Asm3 WB present\n') except: pass #FreeCAD.Console.PrintWarning('Asm3 WB not present\n') global export_board_2step #export_board_2step=False save_temp_data=False global ignore_utf8 ignore_utf8=False global ignore_utf8_incfg ignore_utf8_incfg=True global animate_result animate_result=True #False turntable global allow_compound allow_compound='False' #allow compound in ksu global apply_reflex apply_reflex=True #True adds shininess for Single Color Shapes valid ONLY if turntable is enabled global apply_reflex_all apply_reflex_all=False #True not suggested wip simulate shininess for faces global force_transparency force_transparency = False #True for testing wip global apply_light apply_light=False #True not suggested wip add light up and down # disabling pcurves paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") paramGet.SetInt("WriteSurfaceCurveMode", 0) current_milli_time = lambda: int(round(time.time() * 1000)) Materials=True ## "PIN-01";"metal grey pins" ## "PIN-02";"gold pins" ## "IC-BODY-EPOXY-04";"black body" ## "RES-SMD-01";"resistor black body" ## "IC-BODY-EPOXY-01";"grey body" ## "CAP-CERAMIC-05";"dark grey body" ## "CAP-CERAMIC-06";"brown body" ## "PLASTIC-GREEN-01";"green body" ## "PLASTIC-BLUE-01";"blue body" ## "PLASTIC-WHITE-01";"white body" ## "IC-LABEL-01";"light brown label" ## LED-GREEN, LED-RED, LED-BLUE as_is="" metal_grey_pins="""material DEF PIN-01 Material { ambientIntensity 0.271 diffuseColor 0.824 0.820 0.781 specularColor 0.328 0.258 0.172 emissiveColor 0.0 0.0 0.0 shininess 0.70 transparency 0.0 }""" # http://vrmlstuff.free.fr/materials/ metal_grey="""material DEF MET-01 Material { ambientIntensity 0.249999 diffuseColor 0.298 0.298 0.298 specularColor 0.398 0.398 0.398 emissiveColor 0.0 0.0 0.0 shininess 0.056122 transparency 0.0 }""" gold_pins="""material DEF PIN-02 Material { ambientIntensity 0.379 diffuseColor 0.859 0.738 0.496 specularColor 0.137 0.145 0.184 emissiveColor 0.0 0.0 0.0 shininess 0.40 transparency 0.0 }""" black_body="""material DEF IC-BODY-EPOXY-04 Material { ambientIntensity 0.293 diffuseColor 0.148 0.145 0.145 specularColor 0.180 0.168 0.160 emissiveColor 0.0 0.0 0.0 shininess 0.35 transparency 0.0 }""" resistor_black_body="""material DEF RES-SMD-01 Material { diffuseColor 0.082 0.086 0.094 emissiveColor 0.000 0.000 0.000 specularColor 0.066 0.063 0.063 ambientIntensity 0.638 transparency 0.0 shininess 0.3 }""" dark_grey_body="""material DEF CAP-CERAMIC-05 Material { ambientIntensity 0.179 diffuseColor 0.273 0.273 0.273 specularColor 0.203 0.188 0.176 emissiveColor 0.0 0.0 0.0 shininess 0.15 transparency 0.0 }""" grey_body="""material DEF IC-BODY-EPOXY-01 Material { ambientIntensity 0.117 diffuseColor 0.250 0.262 0.281 specularColor 0.316 0.281 0.176 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" brown_body="""material DEF CAP-CERAMIC-06 Material { ambientIntensity 0.453 diffuseColor 0.379 0.270 0.215 specularColor 0.223 0.223 0.223 emissiveColor 0.0 0.0 0.0 shininess 0.15 transparency 0.0 }""" light_brown_body="""material DEF RES-THT-01 Material { ambientIntensity 0.149 diffuseColor 0.883 0.711 0.492 specularColor 0.043 0.121 0.281 emissiveColor 0.0 0.0 0.0 shininess 0.40 transparency 0.0 }""" blue_body="""material DEF PLASTIC-BLUE-01 Material { ambientIntensity 0.565 diffuseColor 0.137 0.402 0.727 specularColor 0.359 0.379 0.270 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" green_body="""material DEF PLASTIC-GREEN-01 Material { ambientIntensity 0.315 diffuseColor 0.340 0.680 0.445 specularColor 0.176 0.105 0.195 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" orange_body="""material DEF PLASTIC-ORANGE-01 Material { ambientIntensity 0.284 diffuseColor 0.809 0.426 0.148 specularColor 0.039 0.102 0.145 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" red_body="""material DEF RED-BODY Material { ambientIntensity 0.683 diffuseColor 0.700 0.100 0.050 emissiveColor 0.000 0.000 0.000 specularColor 0.300 0.400 0.150 shininess 0.25 transparency 0.0 }""" pink_body="""material DEF CAP-CERAMIC-02 Material { ambientIntensity 0.683 diffuseColor 0.578 0.336 0.352 specularColor 0.105 0.273 0.270 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" yellow_body="""material DEF PLASTIC-YELLOW-01 Material { ambientIntensity 0.522 diffuseColor 0.832 0.680 0.066 specularColor 0.160 0.203 0.320 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" white_body="""material DEF PLASTIC-WHITE-01 Material { ambientIntensity 0.494 diffuseColor 0.895 0.891 0.813 specularColor 0.047 0.055 0.109 emissiveColor 0.0 0.0 0.0 shininess 0.25 transparency 0.0 }""" light_brown_label="""material DEF IC-LABEL-01 Material { ambientIntensity 0.082 diffuseColor 0.691 0.664 0.598 specularColor 0.000 0.000 0.000 emissiveColor 0.0 0.0 0.0 shininess 0.01 transparency 0.0 }""" led_red="""material DEF LED-RED Material { ambientIntensity 0.789 diffuseColor 0.700 0.100 0.050 emissiveColor 0.000 0.000 0.000 specularColor 0.300 0.400 0.150 shininess 0.125 transparency 0.15 }""" led_green="""material DEF LED-GREEN Material { ambientIntensity 0.789 diffuseColor 0.400 0.700 0.150 emissiveColor 0.000 0.000 0.000 specularColor 0.600 0.300 0.100 shininess 0.05 transparency 0.15 }""" led_blue="""material DEF LED-BLUE Material { ambientIntensity 0.789 diffuseColor 0.100 0.250 0.700 emissiveColor 0.000 0.000 0.000 specularColor 0.500 0.600 0.300 shininess 0.125 transparency 0.15 }""" led_yellow="""material DEF LED-YELLOW Material { ambientIntensity 0.522 diffuseColor 0.98 0.840 0.066 specularColor 0.160 0.203 0.320 emissiveColor 0.0 0.0 0.0 shininess 0.125 transparency 0.15 }""" led_white="""material DEF LED-WHITE Material { ambientIntensity 0.494 diffuseColor 0.895 0.891 0.813 specularColor 0.047 0.055 0.109 emissiveColor 0.0 0.0 0.0 shininess 0.125 transparency 0.15 }""" led_grey="""material DEF LED-GREY Material { ambientIntensity 0.494 diffuseColor 0.27 0.25 0.27 specularColor 0.5 0.5 0.6 emissiveColor 0.0 0.0 0.0 shininess 0.35 transparency 0.15 }""" led_black="""material DEF LED-BLACK Material { ambientIntensity 0.494 diffuseColor 0.1 0.05 0.1 specularColor 0.6 0.5 0.6 emissiveColor 0.0 0.0 0.0 shininess 0.5 transparency 0.15 }""" glass_grey="""material DEF GLASS-19 Material { ambientIntensity 2.018212 diffuseColor 0.400769 0.441922 0.459091 specularColor 0.573887 0.649271 0.810811 emissiveColor 0.000000 0.000000 0.000000 shininess 0.127273 transparency 0.37 }""" glass_gold="""material DEF GLASS-29 Material { ambientIntensity 0.379 diffuseColor 0.859 0.738 0.496 specularColor 0.137 0.145 0.184 emissiveColor 0.0 0.0 0.0 shininess 0.40 transparency 0.39 }""" glass_blue="""material DEF GLASS-13 Material { ambientIntensity 0.250000 diffuseColor 0.000000 0.631244 0.748016 specularColor 0.915152 0.915152 0.915152 emissiveColor 0.000000 0.000000 0.000000 shininess 0.642424 transparency 0.39 }""" glass_green="""material DEF GLASS-GREEN Material { ambientIntensity 0.250000 diffuseColor 0.000000 0.75 0.44 specularColor 0.915152 0.915152 0.915152 emissiveColor 0.000000 0.000000 0.000000 shininess 0.642424 transparency 0.39 }""" glass_orange="""material DEF GLASS-ORANGE Material { ambientIntensity 0.250000 diffuseColor 0.75 0.44 0.000000 specularColor 0.915152 0.915152 0.915152 emissiveColor 0.000000 0.000000 0.000000 shininess 0.642424 transparency 0.39 }""" pcb_green="""material DEF BOARD-GREEN-02 Material { ambientIntensity 1 diffuseColor 0.07 0.3 0.12 specularColor 0.07 0.3 0.12 emissiveColor 0.0 0.0 0.0 shininess 0.40 transparency 0.0 }""" pcb_blue="""material DEF BOARD-BLUE-01 Material { ambientIntensity 1 diffuseColor 0.07 0.12 0.3 specularColor 0.07 0.12 0.3 emissiveColor 0.0 0.0 0.0 shininess 0.40 transparency 0.0 }""" pcb_black="""material DEF BOARD-BLACK-03 Material { ambientIntensity 1 diffuseColor 0.16 0.16 0.16 specularColor 0.16 0.16 0.16 emissiveColor 0.0 0.0 0.0 shininess 0.40 transparency 0.0 }""" metal_aluminum="""material DEF MET-ALUMINUM Material { ambientIntensity 0.256000 diffuseColor 0.372322 0.371574 0.373173 specularColor 0.556122 0.554201 0.556122 emissiveColor 0.0 0.0 0.0 shininess 0.127551 transparency 0.0 }""" ##http://vrmlstuff.free.fr/materials/metalsframe.html # metal_bronze="""material DEF MET-BRONZE Material { # ambientIntensity 0.022727 # diffuseColor 0.314286 0.074365 0.000000 # specularColor 0.780612 0.598604 0.000000 # emissiveColor 0.000000 0.000000 0.000000 # shininess 0.107143 # transparency 0.0 # }""" metal_bronze="""material DEF MET-BRONZE Material { ambientIntensity 0.022727 diffuseColor 0.714 0.4284 0.18144 specularColor 0.393548 0.271906 0.166721 emissiveColor 0.000000 0.000000 0.000000 shininess 0.2 transparency 0.0 }""" #bronze 0.2125 0.1275 0.054 0.714 0.4284 0.18144 0.393548 0.271906 0.166721 0.2 #http://devernay.free.fr/cours/opengl/materials.html #silver 0.19225 0.19225 0.19225 0.50754 0.50754 0.50754 0.508273 0.508273 0.508273 0.4 metal_silver="""material DEF MET-SILVER Material { ambientIntensity 0.022727 diffuseColor 0.50754 0.50754 0.50754 specularColor 0.508273 0.508273 0.508273 emissiveColor 0.000000 0.000000 0.000000 shininess 0.4 transparency 0.0 }""" metal_copper="""material DEF MET-COPPER Material { ambientIntensity 0.022727 diffuseColor 0.7038 0.27048 0.0828 specularColor 0.780612 0.37 0.000000 emissiveColor 0.000000 0.000000 0.000000 shininess 0.2 transparency 0.0 }""" #specularColor 0.780612 0.598604 0.000000 material_properties_names=["as is","metal grey pins","metal grey","gold pins", "black body","resistor black body","grey body","dark grey body","brown body",\ "light brown body","blue body","green body","orange body","red_body",\ "pink body","yellow body","white body",\ "light brown label",\ "led red","led green","led blue","led yellow","led white", "led grey", "led black",\ "glass grey","glass gold","glass blue","glass green","glass orange", \ "pcb green", "pcb blue", "pcb black",\ "metal aluminum", "metal bronze", "metal silver", "metal copper"] material_properties=[as_is, metal_grey_pins, metal_grey, gold_pins,\ black_body,resistor_black_body, grey_body,dark_grey_body,brown_body,\ light_brown_body,blue_body, green_body,orange_body,red_body,\ pink_body,yellow_body,white_body, light_brown_label,\ led_red,led_green,led_blue,led_yellow,led_white, led_grey, led_black, \ glass_grey, glass_gold, glass_blue, glass_green,glass_orange, \ pcb_green, pcb_blue, pcb_black,\ metal_aluminum, metal_bronze, metal_silver, metal_copper] material_properties_diffuse=[(0.,0.,0.,0.),(0.824,0.820,0.781,0.),( 0.298, 0.298, 0.298,0.),(0.859,0.738,0.496,0.), \ (0.148, 0.145, 0.145,0.), (0.082, 0.086, 0.094,0.),(0.250, 0.262, 0.281,0.), (0.273, 0.273, 0.273,0.), (0.379, 0.270, 0.215,0.), \ (0.883, 0.711, 0.492,0.), (0.137, 0.402, 0.727,0.),(0.340, 0.680, 0.445,0.), (0.809, 0.426, 0.148,0.), (0.700, 0.100, 0.050,0.), \ (0.578, 0.336, 0.352,0.), (0.832, 0.680, 0.066,0.), (0.895, 0.891, 0.813,0.), \ (0.691, 0.664, 0.598,0.), \ (0.700, 0.100, 0.050,0.15), (0.400, 0.700, 0.150,0.15), (0.100, 0.250, 0.700,0.15), (0.98, 0.840, 0.066,0.15), (0.895, 0.891, 0.813,0.15),(0.27, 0.25, 0.27,0.15), (0.1, 0.05, 0.1,0.15),\ (0.400769, 0.441922, 0.459091,0.37), (0.859, 0.738, 0.496,0.39), (0.000000, 0.631244, 0.748016,0.39), (0.000000, 0.75, 0.44,0.39), (0.75, 0.44, 0.0,0.39), \ (0.07, 0.3, 0.12,0.), (0.07, 0.12, 0.3,0.), (0.16, 0.16, 0.16,0.), \ (0.372322, 0.371574, 0.373173,0.), (0.714, 0.4284, 0.18144,0.), (0.50754, 0.50754, 0.50754,0.), (0.7038, 0.27048, 0.0828,0.)] # (0.314286, 0.074365, 0.000000)] #FreeCAD.Console.PrintMessage (len (material_properties_names)) #FreeCAD.Console.PrintMessage (len (material_properties)) #FreeCAD.Console.PrintMessage (len (material_properties_diffuse)) #stop material_definitions="" for mat in material_properties[1:]: material_definitions+="Shape {\n appearance Appearance {"+mat+"\n }\n}\n" material_ids=[] material_ids.append("") for mat in material_properties[1:]: m = re.search('DEF\s(.+?)\sMaterial', mat) if m: found = m.group(1) #say(found) material_ids.append(found) #say(material_ids) #say (material_definitions) def clear_console(): #clearing previous messages mw=FreeCADGui.getMainWindow() c=mw.findChild(QtGui.QPlainTextEdit, "Python console") c.clear() r=mw.findChild(QtGui.QTextEdit, "Report view") r.clear() #if not Mod_ENABLED: if 0: clear_console() # points: [Vector, Vector, ...] # faces: [(pi, pi, pi), ], pi: point index # color: (Red, Green, Blue), values range from 0 to 1.0 Mesh = namedtuple('Mesh', ['points', 'faces', 'color', 'transp']) from sys import platform as _platform # window GUI dimensions parameters if _platform == "linux" or _platform == "linux2": # linux pt_lnx=True sizeXmin=172;sizeYmin=34+34 sizeX=172;sizeY=516 #536 sizeXright=172;sizeYright=536 #556 else: sizeXmin=172;sizeYmin=34 sizeX=172;sizeY=482#502 sizeXright=172;sizeYright=502#522 if _platform == "darwin": pt_osx=True ## # MAC OS X ##elif _platform == "win32": ## # Windows #sizeXmin=172;sizeYmin=30+34 sizeXMax=487 #487 btn_sizeX=32;btn_sizeY=32; chkb_sizeX=20;chkb_sizeY=20; btn_sm_sizeX=20;btn_sm_sizeY=20; btn_md_sizeX=26;btn_md_sizeY=26; def close_ksu(): #def closeEvent(self, e): spc="""****************************************************************************
""" msg=translate("Close", "Do you want to quit? " "Have you saved your STEP artwork?
" ) #confirm on exit QtGui.QApplication.restoreOverrideCursor() #self.setGeometry(25, 250, 500, 500) #self.setWindowState(QtCore.Qt.WindowMinimized) res='' if test_flag_exit==False: QtGui.QApplication.restoreOverrideCursor() res = QtGui.QMessageBox.question( None,translate("Close", "Close"),msg,QtGui.QMessageBox.Yes|QtGui.QMessageBox.No ) if res is not QtGui.QMessageBox.No: #e.ignore() # KSUWidget.close() KSUWidget.deleteLater() #self.setWindowState(QtCore.Qt.WindowActive) doc=FreeCAD.ActiveDocument if doc is not None: FreeCAD.setActiveDocument(doc.Name) #FreeCAD.ActiveDocument=FreeCAD.getDocument(doc.Label) #FreeCADGui.ActiveDocument=FreeCADGui.getDocument(doc.Label) if close_doc==True: FreeCAD.closeDocument(doc.Name) say(doc.Label) ## def tabify(): #global KSUWidget KSUWidget.setFloating(False) #dock KSUWidget.resize(sizeX,sizeY) KSUWidget.activateWindow() KSUWidget.raise_() t=FreeCADGui.getMainWindow() cv = t.findChild(QtGui.QDockWidget, "Combo View") if cv is None: cv = t.findChild(QtGui.QDockWidget, "ComboView") if cv is None: cv = t.findChild(QtGui.QDockWidget, "Model") if cv is None: cv = t.findChild(QtGui.QDockWidget, "Tree view") if KSUWidget and cv: dw=t.findChildren(QtGui.QDockWidget) try: t.tabifyDockWidget(cv,KSUWidget) except: pass k_tab = t.findChild(QtGui.QDockWidget, "kicadStepUp") #"kicad StepUp 3D tools") k_tab.activateWindow() k_tab.raise_() KSUWidget.showMaximized() KSUWidget.activateWindow() KSUWidget.raise_() say( "Tabified done !") ksu_tab = t.findChild(QtGui.QDockWidget, "kicadStepUp") #"kicad StepUp 3D tools") if ksu_tab: #say ("ksu tab ->"+ksu_tab.objectName()) KSUWidget.resize(sizeX,sizeY) ksu_tab.activateWindow() ksu_tab.raise_() #say ("focus on me!") def dock(): global expanded_view, mingui expanded_view=0; mingui=0 #KSUmw = FreeCADGui.getMainWindow() # PySide # the active qt window, = the freecad window since we are inside it #KSUmw.addDockWidget(QtCore.Qt.LeftDockWidgetArea,KSUWidget) # add the widget to the main window textEdit_dim=textEdit_dim_hide KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) KSUWidget.ui.textEdit.Visible=False KSUWidget.setFloating(False) #dock KSUWidget.resize(sizeX,sizeY) KSUWidget.activateWindow() KSUWidget.raise_() #KSUWidget.ui.HelpPB.clicked.connect(KSUWidget.ui.onHelp) cfg_read_all() ini_vars[16] = u'left' docking_mode='left' #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") dock_mode = pg.SetInt("dockingMode", 1) tabify() #KSUWidget.setFloating(False) #dock #say ("now!") ## def dock_right(): global expanded_view, mingui expanded_view=0;mingui=0 KSUmw = FreeCADGui.getMainWindow() # PySide # the active qt window, = the freecad window since we are inside it t=FreeCADGui.getMainWindow() dw=t.findChildren(QtGui.QDockWidget) looping=False ldw=len (dw) if ldw>0: looping=True idw=0 cv=None while looping and idw < ldw: #for d in dw: d=dw[idw] idw+=1 area = t.dockWidgetArea(d) #if area == QtCore.Qt.LeftDockWidgetArea: # print (d.windowTitle(), '(Left)') if area == QtCore.Qt.RightDockWidgetArea: print (d.windowTitle(), '(Right)') r_w=str(d.objectName()) #;print(r_w) cv = t.findChild(QtGui.QDockWidget, r_w) looping=False KSUmw.addDockWidget(QtCore.Qt.RightDockWidgetArea,KSUWidget) KSUWidget.setFloating(False) #dock #RHDockWidget.resize(sizeXright,sizeYright) KSUWidget.activateWindow() KSUWidget.raise_() if KSUWidget and cv is not None: dw=t.findChildren(QtGui.QDockWidget) #t.tabifyDockWidget(cv,RHDockWidget) try: t.tabifyDockWidget(cv,KSUWidget) say( "Tabified done !") #stop except: say('exception raised') pass #KSUmw = FreeCADGui.getMainWindow() # PySide # the active qt window, = the freecad window since we are inside it #KSUmw.addDockWidget(QtCore.Qt.RightDockWidgetArea,KSUWidget) # add the widget to the main window textEdit_dim=textEdit_dim_hide KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) KSUWidget.ui.textEdit.Visible=False KSUWidget.setFloating(False) #dock KSUWidget.resize(sizeXright,sizeYright) KSUWidget.activateWindow() KSUWidget.raise_() cfg_read_all() ini_vars[16] = u'right' docking_mode='right' #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") dock_mode = pg.SetInt("dockingMode", 2) #KSUWidget.setFloating(False) #dock #say ("now!") ## def undock(): global expanded_view, mingui expanded_view=0; mingui=0 textEdit_dim=textEdit_dim_base KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) KSUWidget.ui.textEdit.Visible=True KSUWidget.setFloating(True) #undock KSUWidget.resize(sizeX,sizeY) KSUWidget.activateWindow() KSUWidget.raise_() cfg_read_all() ini_vars[16] = u'float' docking_mode='float' #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") dock_mode = pg.SetInt("dockingMode", 0) #KSUWidget.resize(QtCore.QSize(300,100).expandedTo(KSUWidget.maximumSize())) # sets size of the widget #KSUWidget.setFloating(False) #dock #say ("now!") ## def temporary_undock(): global expanded_view, mingui mingui=0 #expanded_view=0 #tabify() textEdit_dim=textEdit_dim_base KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) KSUWidget.setFloating(True) #undock KSUWidget.resize(sizeX,sizeY) KSUWidget.activateWindow() KSUWidget.raise_() #KSUWidget.resize(QtCore.QSize(300,100).expandedTo(KSUWidget.maximumSize())) # sets size of the widget #KSUWidget.setFloating(False) #dock #say ("now!") ## def minimz(): #clear_console() global mingui #sayerr(mingui) if mingui==0: KSUWidget.setFloating(True) #undock # KSUWidget.hide(); # KSUWidget.setWindowState(QtCore.Qt.WindowMinimized) #KSUWidget.resize(500, 500) KSUWidget.resize(sizeXmin,sizeYmin) KSUWidget.activateWindow() KSUWidget.raise_() mingui=1 else: mingui=0 cfg_read_all() if docking_mode == 'float': undock() KSUWidget.setVisibility=True elif docking_mode == 'left': #tabify() dock() KSUWidget.setVisibility=True else: dock_right() KSUWidget.setVisibility=True #sayw(mingui) #sayw("kicad StepUp version "+str(___ver___)) ## def minimz_alt(): KSUWidget.setFloating(True) #undock KSUWidget.setWindowState(QtCore.Qt.WindowMinimized) KSUWidget.resize(sizeX,sizeY) KSUWidget.activateWindow() #KSUWidget.raise_() ## def onDestroy(): say ("Do stuff here") ## #font_size=12 #################################### # embedded button images import base64 # "b64_data" is a variable containing your base64 encoded jpeg axis_b64 =\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNjRweCIKICAgaGVpZ2h0PSI2NHB4IgogICBpZD0ic3ZnODI3NyIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkyLjAgcjE1Mjk5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJBeGlzLnN2ZyI+CiAgPGRlZnMKICAgICBpZD0iZGVmczgyNzkiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjcuNzc4MTc0NiIKICAgICBpbmtzY2FwZTpjeD0iOS4wMTQ4ODU4IgogICAgIGlua3NjYXBlOmN5PSIzMy4zNDUxNzgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNTM2IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgwMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9ImZhbHNlIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDU2MjUiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhODI4MiI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgICA8ZGM6dGl0bGU+UGF0aC1BeGlzPC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDE1LTA3LTA0PC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL1BhdGgvR3VpL1Jlc291cmNlcy9pY29ucy9QYXRoLUF4aXMuc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxjYzpsaWNlbnNlPmh0dHBzOi8vd3d3LmdudS5vcmcvY29weWxlZnQvbGVzc2VyLmh0bWw8L2NjOmxpY2Vuc2U+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9Imc2ODQ2IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wNjE5MTI2LDAsMCwxLjA2MTkxMjYsMy44NzYxNzM1LC0zLjgzNDE4NDMpIj4KICAgICAgPHBhdGgKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJzc3NzcyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg2NTE0IgogICAgICAgICBkPSJtIDQ2LDIwLjg0NDgxMSBjIDEuODg2NjU2LDIuODU0OTU4IDEsNy40NTc1MjkgLTEsOC4zODk3MiAtMiwwLjkzMjE5MSAtNC4xMTMzNDQsMC44NzE0ODUgLTYsLTEuOTgzNDc0IC0xLjg4NjY1NSwtMi44NTQ5NTkgLTIuNTU3NDk3LC02LjUxMTExMiAtMSwtOC4yNzA2MjkgMS41NTc0OTcsLTEuNzU5NTE3IDYuMTEzMzQ0LC0wLjk5MDU3NiA4LDEuODY0MzgzIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiM4YWUyMzQ7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiMxNzJhMDQ7c3Ryb2tlLXdpZHRoOjIuMDAwMDAwMjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjMyLjIwMDAwMDc2O3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNjQxOSIKICAgICAgICAgZD0ibSAxMCwxOC45ODA0MjkgNCwyMC41MDgyMDUiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNjQxOS02IgogICAgICAgICBkPSJtIDEwLDE4Ljk4MDQyOSA0LDIwLjUwODIwNSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzM0NjVhNDtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg2NDE5LTYtMiIKICAgICAgICAgZD0ibSA5LDE4Ljk4MDQyOSA0LDIwLjUwODIwNSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDY0NzUtMy01IgogICAgICAgICBkPSJtIDQ2LDI4LjMwMjMzOSBjIDIsLTIuNzk2NTczIDAsLTEwLjI1NDEwMiAtNiwtMTAuMjU0MTAyIGwgMTIsMWUtNiB6IgogICAgICAgICBzdHlsZT0iZmlsbDojNzNkMjE2O3N0cm9rZTojMTcyYTA0O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDozMi4yMDAwMDA3NjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDY1NzEiCiAgICAgICAgIGQ9Im0gNDQsMTkuOTEyNjIgNi45NTQ1NDYsLTAuMDIxMTkiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM4YWUyMzQ7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDY0NzUtMy01LTEiCiAgICAgICAgIGQ9Im0gNDUuOTc3MjczLDI4LjMyMzUyNiBjIDIsLTIuNzk2NTc0IDAsLTEwLjI1NDEwMyAtNiwtMTAuMjU0MTAzIGwgMTIsMWUtNiB6IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMTcyYTA0O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDozMi4yMDAwMDA3NjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDYzOTctMyIKICAgICAgICAgZD0iTSAxNCwzOS40ODg2MzQgNDAsMjQuNTczNTc2IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMTcyYTA0O3N0cm9rZS13aWR0aDo4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDYzOTctMy03IgogICAgICAgICBkPSJNIDE0LDM5LjQ4ODYzNCA0MCwyNC41NzM1NzYiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3M2QyMTY7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNjM5Ny0zLTctNyIKICAgICAgICAgZD0ibSAxMy41OTA5MDksMzguNTk4ODE2IDI2LC0xNC45MTUwNTkiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM4YWUyMzQ7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNjM5NyIKICAgICAgICAgZD0iTSAxNCwzOS40ODg2MzQgMzIsNTQuNDAzNjkyIgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMjgwMDAwO3N0cm9rZS13aWR0aDo4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDYzOTctNSIKICAgICAgICAgZD0iTSAxNCwzOS40ODg2MzQgMzIsNTQuNDAzNjkyIgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojY2MwMDAwO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDYzOTctNS0wIgogICAgICAgICBkPSJtIDEzLjE2MDcwNiw0MC4wNjEyODQgMTgsMTQuOTE1MDU4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZWYyOTI5O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNjQ3NSIKICAgICAgICAgZD0iTSAxNiwxNS4yNTE2NjQgQyAxMy45MzI1NTQsMTkuMDI0NzEyIDYsMTkuOTEyNjIgMywxOC4wNDgyMzggTCA3LDYuODYxOTQzOCBaIgogICAgICAgICBzdHlsZT0iZmlsbDojMzQ2NWE0O3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDozMi4yMDAwMDA3NjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNjQ3NS02IgogICAgICAgICBkPSJNIDEzLjMyMTQxMiwxNS4zNzE1MTEgQyAxMS45NjY1OCwxNi42OTI4MjQgOC4wMDc5ODYsMTcuNDc1MTcyIDUuNTAwNzA5NiwxNi45MTAwNDYgTCA3LjgwNzE1MjcsMTAuMjkxMDkxIFoiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjMyLjIwMDAwMDc2O3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjYyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg2NDc1LTMiCiAgICAgICAgIGQ9Im0gMjYuNzA1NzcxLDU3Ljg1ODE4IGMgLTAuMjMzMjkxLC00LjIzMTE4MiA2LjE2MDI1NCwtOC42OTc0NjIgOS43NTgzMzEsLTguNDgxMTQ3IEwgMzksNjAuOTI5MDMxIFoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNjYzAwMDA7c3Ryb2tlOiMyODAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjMyLjIwMDAwMDc2O3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjYyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg2NDc1LTYtMiIKICAgICAgICAgZD0ibSAyOC45MzYyNDcsNTYuNTA2NDc0IGMgMC40NjI5MzcsLTEuNzc2MTQ5IDMuNDY5MTgsLTQuMzAxMjIyIDUuOTQzMDI0LC00Ljk4MjQ1OSBsIDEuNTU5MTY4LDYuODA1OTM3IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNlZjI5Mjk7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjMyLjIwMDAwMDc2O3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICA8L2c+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoLTAuMTM3NDgyNDgsLTAuNDQxODAxMTYsMC40NDE4MDExNiwtMC4xMzc0ODI0OCwzOC4yMTMwNSw1OC43NjkwMjYpIgogICAgICAgaWQ9Imc2ODQ2LTgiIC8+CiAgPC9nPgo8L3N2Zz4K """ exportBoard_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY4LjI2NjY3IgogICBoZWlnaHQ9IjY4LjI2NjY3IgogICBpZD0ic3ZnMjg2MCIKICAgc29kaXBvZGk6dmVyc2lvbj0iMC4zMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4wIHIxNTI5OSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZXhwb3J0UGFydF91cGRhdGUuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgNjQgNjQiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyODYyIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzk0NSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YWUyMzQ7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzOTQ3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzNkMjE2O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzk0OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQzODciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQzODkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDM5MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzciCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzNjkyIgogICAgICAgY3g9IjQ1Ljg4MzMyNyIKICAgICAgIGN5PSIyOC44Njk1NjgiCiAgICAgICBmeD0iNDUuODgzMzI3IgogICAgICAgZnk9IjI4Ljg2OTU2OCIKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMC4yMzQ0MzIyNCwwLjIzNDQzMTk4KSIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDM4NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM3MDMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIxMzEuNDgxODciCiAgICAgICBjeT0iOTMuNTU3Mjg5IgogICAgICAgZng9IjEzMS40ODE4NyIKICAgICAgIGZ5PSI5My41NTcyODkiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNTI3MTEwNjQsMS44MTU4ODc0LC0xLjQ1MzQ4NDMsMC40MjE5MTMzMSwyMDMuMjM0MDUsLTE4Ny42NTgzKSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzM3NyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmFmZjJiO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMzgxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmFhMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDM4NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM3MDUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIxNDcuMDU3MTMiCiAgICAgICBjeT0iODMuOTg5MTQzIgogICAgICAgZng9IjE0Ny4wNTcxMyIKICAgICAgIGZ5PSI4My45ODkxNDMiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMjk2NjAyOCwwLjE3NzExMjMxLC0wLjE0MDkyODYxLDEuMDMxNzA5NCwtMzIuNjg5OTI5LC0yOS4xMDkyNzQpIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMxLjk5OTk5OCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogOTk5Ljk5OTk1IDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjYzLjk5OTk5NyA6IDMxLjk5OTk5OCA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzEuOTk5OTk4IDogMjEuMzMzMzMyIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4NjgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzctMyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM2OTItNSIKICAgICAgIGN4PSI0NS44ODMzMjciCiAgICAgICBjeT0iMjguODY5NTY4IgogICAgICAgZng9IjQ1Ljg4MzMyNyIKICAgICAgIGZ5PSIyOC44Njk1NjgiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuMjM0NDMyMjQsMC4yMzQ0MzE5OCkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMzNzctMyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OS04IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZmMmI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMzODEtMyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZhYTAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtNCkiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNzY3IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzc3MyIKICAgICAgIHgxPSIyMi4xMTY1MTYiCiAgICAgICB5MT0iNTUuNzE3NTE4IgogICAgICAgeDI9IjE3LjMyODU0NyIKICAgICAgIHkyPSIyMS4zMTEzNCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzY3Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM3NjkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNzcxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgwLC00KSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM3NzciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzgzIgogICAgICAgeDE9IjUzLjg5Njc2MyIKICAgICAgIHkxPSI1MS4xNzk3ODciCiAgICAgICB4Mj0iNDcuNTAyMjM1IgogICAgICAgeTI9IjIxLjgzNzQyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NzciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzc3OSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM3ODEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ4NjYyIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50Mzc1NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwwLjUzNjcyMywwLDE2Ljg3MzA2KSIKICAgICAgIGN4PSIyNC44MzcxMjYiCiAgICAgICBjeT0iMzYuNDIxMTI3IgogICAgICAgZng9IjI0LjgzNzEyNiIKICAgICAgIGZ5PSIzNi40MjExMjciCiAgICAgICByPSIxNS42NDQ3MzciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDg2NjIiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDg2NjQiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wODY2NiIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwO3N0b3Atb3BhY2l0eTowOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI4NDciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzU5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC02NC4yNDc4MTEsNS41ODI1MTM4KSIKICAgICAgIHgxPSIzMi42NDc5NzIiCiAgICAgICB5MT0iMzAuNzQ4ODQ2IgogICAgICAgeDI9IjM3LjEyNDQ2MiIKICAgICAgIHkyPSIyNC44NDIyNTMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI2ODIiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzYxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC02NC4yNDc4MTEsNS41ODI1MTM4KSIKICAgICAgIHgxPSIzNi43MTM4MzciCiAgICAgICB5MT0iMzEuNDU1OTUyIgogICAgICAgeDI9IjM3LjEyNDQ2MiIKICAgICAgIHkyPSIyNC44NDIyNTMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI2ODIiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI2ODQiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzRlOWEwNjtzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyNjg2IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4OWFlZGM7c3RvcC1vcGFjaXR5OjA7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MjQwMiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NjctMyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjE4LjkzNTc2NiIKICAgICAgIHkxPSIyMy42Njc4OTYiCiAgICAgICB4Mj0iNTMuNTg4NjIzIgogICAgICAgeTI9IjI2LjY0OTM2MyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjQwMiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjQwNCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyNDA2IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM1MjhhYzU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mjg3MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NjkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSI0Ni44MzQ4MTYiCiAgICAgICB5MT0iNDUuMjY0MTIyIgogICAgICAgeDI9IjQ1LjM4MDQzNiIKICAgICAgIHkyPSI1MC45Mzk2NjciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI4NzEiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI4NzMiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjg3NSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzQ2NWE0O3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzk0NSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NjMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSIxOC45MzU3NjYiCiAgICAgICB5MT0iMjMuNjY3ODk2IgogICAgICAgeDI9IjUzLjU4ODYyMyIKICAgICAgIHkyPSIyNi42NDkzNjMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNDkiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMxNTEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzE1MyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNTI4YWM1O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI3OTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzcxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iNS45NjQ5MTc3IgogICAgICAgeTE9IjI2LjA0ODE2NCIKICAgICAgIHgyPSI1Mi44NTQwOTUiCiAgICAgICB5Mj0iMjYuMDQ4MTY0IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyNzk3IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyNzk5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI4MDEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyODQ3IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzc0NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtNjMuNzgyMzk4LDUuMzA0OTIwOCkiCiAgICAgICB4MT0iMTMuNDc4NTU0IgogICAgICAgeTE9IjEwLjYxMjIwNiIKICAgICAgIHgyPSIxNS40MTk0MTciCiAgICAgICB5Mj0iMTkuMTE1MTIyIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyODMxIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyODMzIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzNDY1YTQ7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNWI4NmJlO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwLjMzMzMzMzM0IgogICAgICAgICBpZD0ic3RvcDI4NTUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjgzNSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojODNhOGQ4O3N0b3Atb3BhY2l0eTowOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI4NDciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzQ3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0icm90YXRlKDE4MCwtOC4zNTk5NTA1LDI1LjcxNDk2MikiCiAgICAgICB4MT0iMzcuMTI4MDUyIgogICAgICAgeTE9IjI5LjcyOTYwNSIKICAgICAgIHgyPSIzNy4wNjU0MTQiCiAgICAgICB5Mj0iMjYuMTk0MDcxIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyODQ3IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyODQ5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM0ZTlhMDY7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjg1MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzQ2NWE0O3N0b3Atb3BhY2l0eTowOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIzODAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzUzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iNjIuNTEzODM2IgogICAgICAgeTE9IjM2LjA2MTIzNyIKICAgICAgIHgyPSIxNS45ODQ4NjMiCiAgICAgICB5Mj0iMjAuNjA4NTgiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIzODAiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDIzODIiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2I5Y2ZlNztzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyMzg0IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzOTQ1IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzc0OSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjYyLjUxMzgzNiIKICAgICAgIHkxPSIzNi4wNjEyMzciCiAgICAgICB4Mj0iMTUuOTg0ODYzIgogICAgICAgeTI9IjIwLjYwODU4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTgwIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMTgyIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNiOWNmZTc7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzE4NCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mjc5NyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NTUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSI1Ljk2NDkxNzciCiAgICAgICB5MT0iMjYuMDQ4MTY0IgogICAgICAgeDI9IjUyLjg1NDA5NSIKICAgICAgIHkyPSIyNi4wNDgxNjQiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDg2NjIiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzOTY4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMTMxNDQ2NSwwLDAsMS40NzAxNDI1LDExLjkxMjI4NywtNjkuNzYzNTYpIgogICAgICAgY3g9IjI0LjgzNzEyNiIKICAgICAgIGN5PSIzNi40MjExMjciCiAgICAgICBmeD0iMjQuODM3MTI2IgogICAgICAgZnk9IjM2LjQyMTEyNyIKICAgICAgIHI9IjE1LjY0NDczNyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mjg0NyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5NzAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC43NTk0OTQ2MSwwLDAsMC43NTk0OTQ2MSwtNTguMzk5OTI2LC0wLjE3NjMwODg2KSIKICAgICAgIHgxPSIzMi42NDc5NzIiCiAgICAgICB5MT0iMzAuNzQ4ODQ2IgogICAgICAgeDI9IjM3LjEyNDQ2MiIKICAgICAgIHkyPSIyNC44NDIyNTMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI2ODIiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTcyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNzU5NDk0NjEsMCwwLDAuNzU5NDk0NjEsLTU4LjM5OTkyNiwtMC4xNzYzMDg4NikiCiAgICAgICB4MT0iMzYuNzEzODM3IgogICAgICAgeTE9IjMxLjQ1NTk1MiIKICAgICAgIHgyPSIzNy4xMjQ0NjIiCiAgICAgICB5Mj0iMjQuODQyMjUzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzOTE2IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzk2MyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgwMDAwMDA3LDAsMCwwLjc4NDMxMzY4LDAuMjAwMDAwODIsLTMwLjA5ODAzOCkiCiAgICAgICB4MT0iMzcuMjQ5OTk2IgogICAgICAgeTE9IjI1LjA5MjM0MiIKICAgICAgIHgyPSIzOS43NDk5OTYiCiAgICAgICB5Mj0iNDAuOTI0OTk5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTE2Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzOTE4IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YWUyMzQ7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzkyMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNGU5YTA2O3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjkuMDgyMDMwOCIKICAgICBpbmtzY2FwZTpjeD0iMzQuMTMzMzM1IgogICAgIGlua3NjYXBlOmN5PSIzNC4xMzMzMzUiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0iZzM5NTEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE1MzYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODAxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDAxIgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIgogICAgICAgb3JpZ2lueD0iMCIKICAgICAgIG9yaWdpbnk9IjAiCiAgICAgICBzcGFjaW5neD0iMSIKICAgICAgIHNwYWNpbmd5PSIxIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI4NjUiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bU3RlZmFuIFRyw7ZnZXJdPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzp0aXRsZT5mZW0tYm94PC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDE1LTExLTE1PC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kLzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOiM3MjlmY2Y7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMywxMyAzNywxOSA2MSwxMSAzMSw3IFoiCiAgICAgICBpZD0icGF0aDI5OTMiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mzc4Myk7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNjEsMTEgViA0NyBMIDM3LDU3IFYgMTkgWiIKICAgICAgIGlkPSJwYXRoMjk5NSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiCiAgICAgICBpZD0icGF0aDM4MjUiCiAgICAgICBkPSJtIDMsMTMgMzQsNiBWIDU3IEwgMyw1MSBaIgogICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM3NzMpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNSwxNS40Mjc3MiAwLjAwODY3LDMzLjkxOTExNiAzMC4wMDg2NzEsNS4yNjg3OTkgLTAuMDA4NywtMzMuOTMzNjE0IHoiCiAgICAgICBpZD0icGF0aDM3NjUiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzQ2NWE0O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM5LjAxMjQzLDIwLjQzMzgzMyAtMC4wMTIyNiwzMy41MzUzMDEgMjAuMDAxMTA1LC04LjMwMDk5MyAzLjZlLTQsLTMxLjg2NzM2MyB6IgogICAgICAgaWQ9InBhdGgzNzc1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICA8ZwogICAgICAgaWQ9ImczOTUxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuMjQ0MTQ5LDEyLjI4MDcxNykiPgogICAgICA8ZWxsaXBzZQogICAgICAgICByeT0iMjMuMDAwMDAyIgogICAgICAgICByeD0iMTcuNzAxMTgzIgogICAgICAgICBjeT0iLTE2LjIxOTI4NCIKICAgICAgICAgY3g9IjQwLjAxNDE2OCIKICAgICAgICAgaW5rc2NhcGU6cl9jeT0idHJ1ZSIKICAgICAgICAgaW5rc2NhcGU6cl9jeD0idHJ1ZSIKICAgICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7b3BhY2l0eTowLjM4MzMzMzMzO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDM5NjgpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxLjkxNTc3MTAxO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO21hcmtlcjpub25lIgogICAgICAgICBpZD0icGF0aDg2NjAiCiAgICAgICAgIHRyYW5zZm9ybT0ic2NhbGUoLTEpIiAvPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmJsb2NrO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM5NzApO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTp1cmwoI2xpbmVhckdyYWRpZW50Mzk3Mik7c3Ryb2tlLXdpZHRoOjAuNzU4MDAwMDI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lIgogICAgICAgICBkPSJtIC0zNi44NDkyNjcsMjUuMjE5MjkyIGMgMCwwIDYuNzg3OTgzLDAuNDc0Njg0IDQuNjk5MzczLC03LjUwMDAwOSBoIDUuOTA1NzQ1IGMgMCwxLjE0MTIxOCAtMC40NDY4NzcsOS4wMTg5OTkgLTEwLjYwNTExOCw3LjUwMDAwOSB6IgogICAgICAgICBpZD0icGF0aDI4MzkiCiAgICAgICAgIGlua3NjYXBlOnJfY3g9InRydWUiCiAgICAgICAgIGlua3NjYXBlOnJfY3k9InRydWUiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjYyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPGcKICAgICAgICAgaWQ9ImcxNjk2IgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjE0Mzg0ODgsMCwwLDEuMTQzODQ4OCwxLjUyMDAzNTQsLTQuMzk0NjExNykiPgogICAgICAgIDxnCiAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO3N0cm9rZS13aWR0aDoxLjMyOTA3MTQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgICAgaWQ9ImczOTU5IgogICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuOTM3NDk5OTQsMCwwLDAuOTM3NDk5OTQsLTU2LjUwNDM5MywyNS44NjI3MTMpIj4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgICAgICBpZD0icGF0aDMzNDMtMCIKICAgICAgICAgICAgIGQ9Im0gMzUsLTEzIHYgNCBjIC0xMCwwIC0xNywyIC0yMyw3IGwgNSw3IGMgNS41MzMxMDcsLTMuMjk2OTI0MyAxMS4zMDQ3MDMsLTUuOTMxMzgxMzcgMTgsLTYgdiA0IGwgMTQsLTggeiIKICAgICAgICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzOTYzKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzE3MmEwNDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgICAgICBpZD0icGF0aDMzNDMtMi05IgogICAgICAgICAgICAgZD0ibSAzNywtOS41ODIyMzA0IC0wLjAzNDUzLDIuNDk1OTEzNyBDIDI0LC03IDIwLC01IDE0Ljc0MjMyNCwtMS42MjE0ODA1IGwgMi44MjM4OTcsMy45NzQ2MTY5IEMgMjAuMjI0NDI0LDAuOTEzNjgzMjUgMjYsLTMgMzcsLTMgdiAyLjU4MjIzMDQgTCA0NSwtNSBaIgogICAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6bm9uZTtzdHJva2U6IzhhZTIzNDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgICA8L2c+CiAgICAgIDwvZz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ importBoard_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIGhlaWdodD0iNjQiCiAgIHdpZHRoPSI2NCIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4wIHIxNTI5OSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iaW1wb3J0Qm9hcmQuc3ZnIj4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyOCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxIgogICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAiCiAgICAgZ3VpZGV0b2xlcmFuY2U9IjEwIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNTM2IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgwMSIKICAgICBpZD0ibmFtZWR2aWV3MjYiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjcuMzc1IgogICAgIGlua3NjYXBlOmN4PSIxNS4zODU2MjgiCiAgICAgaW5rc2NhcGU6Y3k9IjIxLjk2Njk0MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnMiIgLz4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHgxPSI4MS44OTgwMDMiCiAgICAgICB5MT0iMTcuNzI5IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjczODczLDAsMCwxLjM1MzcsMCwyLjAwMDA5MTcpIgogICAgICAgeDI9IjMuMDQ1OCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeTI9IjE3LjcyOSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4NjYiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjgiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3RvcC1jb2xvcj0iI2NjODAwMCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODcwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0b3AtY29sb3I9IiNmZmVkMDAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0iYyIKICAgICAgIHkyPSIxNy43MjkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgyPSIzLjA0NTgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNzM4NzMsMCwwLDEuMzUzNywwLDIuMDAwMDkxNykiCiAgICAgICB5MT0iMTcuNzI5IgogICAgICAgeDE9IjgxLjg5OCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNjYzgwMDAiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZlZDAwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wOSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJkIgogICAgICAgeTI9IjEuODQ2OCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDI9IjQ4LjI2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjAwMDAxODgsMCwwLDEuMDAwMDE4OCwwLDIuMDAwMDkxNykiCiAgICAgICB5MT0iMzMuNjEyIgogICAgICAgeDE9IjM0LjI5Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2FmN2QwMCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDEyIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZlZDAwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjZCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI5OTMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4wMDAwMTg4LDAsMCwxLjAwMDAxODgsMCwxMC4wMDAwOTIpIgogICAgICAgeDE9IjM0LjI5IgogICAgICAgeTE9IjMzLjYxMiIKICAgICAgIHgyPSI0OC4yNiIKICAgICAgIHkyPSIxLjg0NjgiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNjIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mjk5NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjczODczLDAsMCwxLjM1MzcsMCwxMC4wMDAwOTIpIgogICAgICAgeDE9IjgxLjg5OCIKICAgICAgIHkxPSIxNy43MjkiCiAgICAgICB4Mj0iMy4wNDU4IgogICAgICAgeTI9IjE3LjcyOSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2MtNyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI5OTctMSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjczODczLDAsMCwxLjM1MzcsMCwxMC4wMDAwOTIpIgogICAgICAgeDE9IjgxLjg5ODAwMyIKICAgICAgIHkxPSIxNy43MjkiCiAgICAgICB4Mj0iMy4wNDU4IgogICAgICAgeTI9IjE3LjcyOSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImMtNyIKICAgICAgIHkyPSIxNy43MjkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgyPSIzLjA0NTgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNzM4NzMsMCwwLDEuMzUzNywwLDIuMDAwMDkxNykiCiAgICAgICB5MT0iMTcuNzI5IgogICAgICAgeDE9IjgxLjg5ODAwMyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNjYzgwMDAiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A3LTQiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNmZmVkMDAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3A5LTAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjZC00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mjk5My05IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMDAwMDE4OCwwLDAsMS4wMDAwMTg4LDAsMTAuMDAwMDkyKSIKICAgICAgIHgxPSIzNC4yOTAwMDEiCiAgICAgICB5MT0iMzMuNjEyIgogICAgICAgeDI9IjQ4LjI1OTk5OCIKICAgICAgIHkyPSIxLjg0NjgiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJkLTQiCiAgICAgICB5Mj0iMS44NDY4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4Mj0iNDguMjU5OTk4IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjAwMDAxODgsMCwwLDEuMDAwMDE4OCwwLDIuMDAwMDkxNykiCiAgICAgICB5MT0iMzMuNjEyIgogICAgICAgeDE9IjM0LjI5MDAwMSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNhZjdkMDAiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AxMi04IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZlZDAwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMTQtOCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIxLjg0NjgiCiAgICAgICB4Mj0iNDguMjU5OTk4IgogICAgICAgeTE9IjMzLjYxMiIKICAgICAgIHgxPSIzNC4yOTAwMDEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMDAwMDE4OCwwLDAsMS4wMDAwMTg4LDAsMTAuMDAwMDkyKSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAyNyIKICAgICAgIHhsaW5rOmhyZWY9IiNkLTQiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNjLTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyOTk3LTE3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNzM4NzMsMCwwLDEuMzUzNywwLDEwLjAwMDA5MikiCiAgICAgICB4MT0iODEuODk4MDAzIgogICAgICAgeTE9IjE3LjcyOSIKICAgICAgIHgyPSIzLjA0NTgiCiAgICAgICB5Mj0iMTcuNzI5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0iYy0xIgogICAgICAgeTI9IjE3LjcyOSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDI9IjMuMDQ1OCIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC43Mzg3MywwLDAsMS4zNTM3LDAsMi4wMDAwOTE3KSIKICAgICAgIHkxPSIxNy43MjkiCiAgICAgICB4MT0iODEuODk4MDAzIj4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2NjODAwMCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDctMSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2ZmZWQwMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDktNSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNkLTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyOTkzLTIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4wMDAwMTg4LDAsMCwxLjAwMDAxODgsMCwxMC4wMDAwOTIpIgogICAgICAgeDE9IjM0LjI5MDAwMSIKICAgICAgIHkxPSIzMy42MTIiCiAgICAgICB4Mj0iNDguMjU5OTk4IgogICAgICAgeTI9IjEuODQ2OCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImQtNyIKICAgICAgIHkyPSIxLjg0NjgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgyPSI0OC4yNTk5OTgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMDAwMDE4OCwwLDAsMS4wMDAwMTg4LDAsMi4wMDAwOTE3KSIKICAgICAgIHkxPSIzMy42MTIiCiAgICAgICB4MT0iMzQuMjkwMDAxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2FmN2QwMCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDEyLTYiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNmZmVkMDAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AxNC0xIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjEuODQ2OCIKICAgICAgIHgyPSI0OC4yNTk5OTgiCiAgICAgICB5MT0iMzMuNjEyIgogICAgICAgeDE9IjM0LjI5MDAwMSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4wMDAwMTg4LDAsMCwxLjAwMDAxODgsMCwxMC4wMDAwOTIpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI3LTQiCiAgICAgICB4bGluazpocmVmPSIjZC03IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjZC00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA5MiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjE5NDU3OTM4LDAsMCwwLjE5NDU3OTM4LDExLjkyOTE2MywyMi41NDEzMzEpIgogICAgICAgeDE9IjM0LjI5MDAwMSIKICAgICAgIHkxPSIzMy42MTIiCiAgICAgICB4Mj0iNDguMjU5OTk4IgogICAgICAgeTI9IjEuODQ2OCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2QtNyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwOTYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4xNDM3Mzg5MiwwLDAsMC4yNjMzOTcxNSwxMS45MjkxNjMsMjIuNTQxMzMxKSIKICAgICAgIHgxPSI4MS44OTgwMDMiCiAgICAgICB5MT0iMTcuNzI5IgogICAgICAgeDI9IjMuMDQ1OCIKICAgICAgIHkyPSIxNy43MjkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNkLTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODc3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuMTQzNzM4OTIsMCwwLDAuMjYzMzk3MTUsMTEuOTI5MTYzLDIyLjU0MTMzMSkiCiAgICAgICB4MT0iODEuODk4MDAzIgogICAgICAgeTE9IjE3LjcyOSIKICAgICAgIHgyPSIzLjA0NTgiCiAgICAgICB5Mj0iMTcuNzI5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjZC00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg3OSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjE5NDU3OTM4LDAsMCwwLjE5NDU3OTM4LDExLjkyOTE2MywyMi41NDEzMzEpIgogICAgICAgeDE9IjM0LjI5MDAwMSIKICAgICAgIHkxPSIzMy42MTIiCiAgICAgICB4Mj0iNDguMjU5OTk4IgogICAgICAgeTI9IjEuODQ2OCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2QtNyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4ODIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4zNjkxNDY5NiwwLDAsMC4zNjkxNDY5NiwyOC40NTE2NCwxOC4xMzgxOTEpIgogICAgICAgeDE9IjM0LjI5MDAwMSIKICAgICAgIHkxPSIzMy42MTIiCiAgICAgICB4Mj0iNDguMjU5OTk4IgogICAgICAgeTI9IjEuODQ2OCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2QiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODg2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuMjcyNjk0ODEsMCwwLDAuNDk5NzA0ODUsMjguNDUxNjQsMTguMTM4MTkxKSIKICAgICAgIHgxPSI4MS44OTgwMDMiCiAgICAgICB5MT0iMTcuNzI5IgogICAgICAgeDI9IjMuMDQ1OCIKICAgICAgIHkyPSIxNy43MjkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIzODAiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDIzODIiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2I5Y2ZlNztzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyMzg0IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTQ1Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzhhZTIzNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM5NDciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3M2QyMTY7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzOTQ5IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzkxNiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5NjMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MDAwMDAwNywwLDAsMC43ODQzMTM2OCwwLjIwMDAwMDgyLC0zMC4wOTgwMzgpIgogICAgICAgeDE9IjM3LjI0OTk5NiIKICAgICAgIHkxPSIyNS4wOTIzNDIiCiAgICAgICB4Mj0iMzkuNzQ5OTk2IgogICAgICAgeTI9IjQwLjkyNDk5OSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzkxNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzkxOCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojOGFlMjM0O3N0b3Atb3BhY2l0eToxIiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM5MjAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzRlOWEwNjtzdG9wLW9wYWNpdHk6MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxnCiAgICAgaWQ9Imc0NjA1IgogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMDg4MzM4NSwwLDAsMS4wODgzMzg1LC00LjMyMjg2MjIsMC42NzQ4NzIzKSI+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwyKSIKICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSIzMDAiCiAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iMzAwIgogICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSJDOlxrdzM0bVxtYXVpLW1vZFxwbmdzXG1hY2QucG5nIgogICAgICAgaWQ9ImczMDAwIj4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mjk5Nyk7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIgogICAgICAgICBpZD0icGF0aDIwIgogICAgICAgICBkPSJNIDU4Ljc1MDQ1OCwzNS43NTA4NSA0Mi4wMDA0OTQsNDUuMzc1NjU3IDI1LjI1MDUzLDU1LjAwMTgxOCAzLjc0OTc5MzUsNDAuNzUwMDY0IDMuMjUwMDQyNiwyOC4wMDA5MTcgYyAwLDAgMTAuOTE2OTUyNCwtNS4wMDAwMjYgMTYuMzc0Njg5NCwtNy41MDAxNzQgNS40NTgzMjgsLTIuNTAwMDE0IDE2LjM3NDY4OSwtNy41MDAxNzUgMTYuMzc0Njg5LC03LjUwMDE3NSBsIDIzLjQ5OTc0LDEyLjI1MDE3MyAtMC43NTAwMzMsMTAuNTAwMTA5IHoiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgIGQ9Im0gMy44NzUwMDgyLDI4LjAwMDkxNyBjIDAsMCA3LjA1NTk3OTgsNC41NDQ2NDIgMTAuNTgzNzg0OCw2LjgxNzA5OCAzLjUyODAyNywyLjI3MjMyMSAxMC41ODM3ODUsNi44MTcwOTggMTAuNTgzNzg1LDYuODE3MDk4IGwgLTAuMTY3ODkyLDEzLjExNjI3IgogICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICBkPSJtIDI1LjI3MTIxNSw0MS42ODY4MjQgYyAwLDAgMTEuMzI2MjA4LC01LjUyMDI1MyAxNi45ODkzMTIsLTguMjgwNDQ3IDUuNjYzMTA0LC0yLjc2MDE5NCAxNi45ODkzMTMsLTguMjgwNDQ4IDE2Ljk4OTMxMywtOC4yODA0NDgiCiAgICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDI5OTMpO3N0cm9rZTojNWUzODAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lam9pbjpyb3VuZCIgLz4KICAgIDwvZz4KICAgIDxnCiAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iMzAwIgogICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjMwMCIKICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iQzpca3czNG1cbWF1aS1tb2RccG5nc1xtYWNkLnBuZyIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KC0xLDAsMCwxLDM2LjcxMTg2NSwzLjIyMDMzOSkiCiAgICAgICBpZD0iZzM4NzIiPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzODc3KTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzVlMzgwMDtzdHJva2Utd2lkdGg6MC4zODkxNTE0NTtzdHJva2UtbGluZWpvaW46cm91bmQiCiAgICAgICAgIGlkPSJwYXRoMjAtNCIKICAgICAgICAgZD0ibSAyMy4zNjA1NzYsMjcuNTUxODAzIC0zLjI1OTEzNywxLjg3Mjc1NCAtMy4yNTkxMzYsMS44NzMwMTcgLTQuMTgzNTIxLC0yLjc3MzA0NSAtMC4wOTcyNCwtMi40ODA2NzQgYyAwLDAgMi4xMjQxNzQsLTAuOTcyODg0IDMuMTg2MTE3LC0xLjQ1OTM1MiAxLjA2MjA1OCwtMC40ODY0NDIgMy4xODYxMTcsLTEuNDU5MzUyIDMuMTg2MTE3LC0xLjQ1OTM1MiBsIDQuNTcyNDc5LDIuMzgzNTg2IC0wLjE0NTkzOCwyLjA0MzA2NiB6IiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDIyLTUiCiAgICAgICAgIGQ9Im0gMTIuNjgzMTQ2LDI2LjA0Mzg1NSBjIDAsMCAxLjM3MjkyMiwwLjg4NDI3NyAyLjA1OTM0NywxLjMyNjQ0MSAwLjY4NjQ2OCwwLjQ0MjEzOSAyLjA1OTM0OCwxLjMyNjQ0MiAyLjA1OTM0OCwxLjMyNjQ0MiBsIC0wLjAzMjY3LDIuNTUyMTA4IgogICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjAuMzg5MTUxNDU7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDI0LTUiCiAgICAgICAgIGQ9Im0gMTYuODQ2MzI4LDI4LjcwNjggYyAwLDAgMi4yMDM4MDUsLTEuMDc0MTA3IDMuMzA1NzA3LC0xLjYxMTE3NCAxLjEwMTkwMywtMC41MzcwNjcgMy4zMDU3MDgsLTEuNjExMTc0IDMuMzA1NzA4LC0xLjYxMTE3NCIKICAgICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mzg3OSk7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjAuMzg5MTUxNDU7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIiAvPgogICAgPC9nPgogICAgPHBhdGgKICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSIzMDAiCiAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iMzAwIgogICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSJDOlxrdzM0bVxtYXVpLW1vZFxwbmdzXG1hY2QucG5nIgogICAgICAgZD0ibSA1MC4xMzg3ODUsMjcuNjQzODI2IC02LjE4MzA4MiwzLjU1MjkwMiAtNi4xODMwODIsMy41NTM0MDEgLTcuOTM2NzgyLC01LjI2MDg5MyAtMC4xODQ0NzgsLTQuNzA2MjIgYyAwLDAgNC4wMjk4ODQsLTEuODQ1NzEgNi4wNDQ1NTMsLTIuNzY4NjE0IDIuMDE0ODg3LC0wLjkyMjg1NiA2LjA0NDU1MywtMi43Njg2MTUgNi4wNDQ1NTMsLTIuNzY4NjE1IGwgOC42NzQ2OTQsNC41MjIwMjkgLTAuMjc2ODY3LDMuODc2MDEgeiIKICAgICAgIGlkPSJwYXRoMjAtMiIKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM4ODYpO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojNWUzODAwO3N0cm9rZS13aWR0aDowLjczNzk5OTk4O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjMwMCIKICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSIzMDAiCiAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9IkM6XGt3MzRtXG1hdWktbW9kXHBuZ3NcbWFjZC5wbmciCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjAuNzM4MjgwMDY7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIgogICAgICAgZD0ibSAyOS44ODIwNjEsMjQuNzgzMDE2IGMgMCwwIDIuNjA0NjQ0LDEuNjc3NjA5IDMuOTA2ODk4LDIuNTE2NDY0IDEuMzAyMzM2LDAuODM4ODA0IDMuOTA2ODk5LDIuNTE2NDYzIDMuOTA2ODk5LDIuNTE2NDYzIGwgLTAuMDYxOTgsNC44NDE3NDEiCiAgICAgICBpZD0icGF0aDIyLTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSIzMDAiCiAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iMzAwIgogICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSJDOlxrdzM0bVxtYXVpLW1vZFxwbmdzXG1hY2QucG5nIgogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mzg4Mik7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjAuNzM4MjgwMDY7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIgogICAgICAgZD0ibSAzNy43ODAyNTcsMjkuODM1MDMyIGMgMCwwIDQuMTgwOTU2LC0yLjAzNzc0NiA2LjI3MTQzNSwtMy4wNTY2NDQgMi4wOTA0NzgsLTEuMDE4ODk4IDYuMjcxNDM1LC0zLjA1NjY0NSA2LjI3MTQzNSwtMy4wNTY2NDUiCiAgICAgICBpZD0icGF0aDI0LTEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogIDwvZz4KICA8ZwogICAgIGlkPSJnMTY5NiIKICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjkxNDM0NzQ4LDAuNjQwMjMzMDIsLTAuNjQwMjMzMDIsMC45MTQzNDc0OCw1Ny45NTAzNCw4LjM0NDYxOTkpIj4KICAgIDxnCiAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7c3Ryb2tlLXdpZHRoOjEuMzI5MDcxNDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgIGlkPSJnMzk1OSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuOTM3NDk5OTQsMCwwLDAuOTM3NDk5OTQsLTU2LjUwNDM5MywyNS44NjI3MTMpIj4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMCIKICAgICAgICAgZD0ibSAzNSwtMTMgdiA0IGMgLTEwLDAgLTE3LDIgLTIzLDcgbCA1LDcgYyA1LjUzMzEwNywtMy4yOTY5MjQzIDExLjMwNDcwMywtNS45MzEzODEzNyAxOCwtNiB2IDQgbCAxNCwtOCB6IgogICAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mzk2Myk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMxNzJhMDQ7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMi05IgogICAgICAgICBkPSJtIDM3LC05LjU4MjIzMDQgLTAuMDM0NTMsMi40OTU5MTM3IEMgMjQsLTcgMjAsLTUgMTQuNzQyMzI0LC0xLjYyMTQ4MDUgbCAyLjgyMzg5NywzLjk3NDYxNjkgQyAyMC4yMjQ0MjQsMC45MTM2ODMyNSAyNiwtMyAzNywtMyB2IDIuNTgyMjMwNCBMIDQ1LC01IFoiCiAgICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtmaWxsOm5vbmU7c3Ryb2tlOiM4YWUyMzQ7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ exportPart_b64 =\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY4LjI2NjY3IgogICBoZWlnaHQ9IjY4LjI2NjY3IgogICBpZD0ic3ZnMjg2MCIKICAgc29kaXBvZGk6dmVyc2lvbj0iMC4zMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4wIHIxNTI5OSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZXhwb3J0UGFydF91cGRhdGUuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgNjQgNjQiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyODYyIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzk0NSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YWUyMzQ7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzOTQ3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzNkMjE2O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzk0OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQzODciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQzODkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDM5MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzciCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzNjkyIgogICAgICAgY3g9IjQ1Ljg4MzMyNyIKICAgICAgIGN5PSIyOC44Njk1NjgiCiAgICAgICBmeD0iNDUuODgzMzI3IgogICAgICAgZnk9IjI4Ljg2OTU2OCIKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMC4yMzQ0MzIyNCwwLjIzNDQzMTk4KSIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDM4NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM3MDMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIxMzEuNDgxODciCiAgICAgICBjeT0iOTMuNTU3Mjg5IgogICAgICAgZng9IjEzMS40ODE4NyIKICAgICAgIGZ5PSI5My41NTcyODkiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNTI3MTEwNjQsMS44MTU4ODc0LC0xLjQ1MzQ4NDMsMC40MjE5MTMzMSwyMDMuMjM0MDUsLTE4Ny42NTgzKSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzM3NyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmFmZjJiO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMzgxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmFhMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDM4NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM3MDUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIxNDcuMDU3MTMiCiAgICAgICBjeT0iODMuOTg5MTQzIgogICAgICAgZng9IjE0Ny4wNTcxMyIKICAgICAgIGZ5PSI4My45ODkxNDMiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMjk2NjAyOCwwLjE3NzExMjMxLC0wLjE0MDkyODYxLDEuMDMxNzA5NCwtMzIuNjg5OTI5LC0yOS4xMDkyNzQpIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMxLjk5OTk5OCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogOTk5Ljk5OTk1IDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjYzLjk5OTk5NyA6IDMxLjk5OTk5OCA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzEuOTk5OTk4IDogMjEuMzMzMzMyIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4NjgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzctMyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM2OTItNSIKICAgICAgIGN4PSI0NS44ODMzMjciCiAgICAgICBjeT0iMjguODY5NTY4IgogICAgICAgZng9IjQ1Ljg4MzMyNyIKICAgICAgIGZ5PSIyOC44Njk1NjgiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuMjM0NDMyMjQsMC4yMzQ0MzE5OCkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMzNzctMyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OS04IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZmMmI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMzODEtMyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZhYTAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtNCkiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNzY3IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzc3MyIKICAgICAgIHgxPSIyMi4xMTY1MTYiCiAgICAgICB5MT0iNTUuNzE3NTE4IgogICAgICAgeDI9IjE3LjMyODU0NyIKICAgICAgIHkyPSIyMS4zMTEzNCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzY3Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM3NjkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNzcxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgwLC00KSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM3NzciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzgzIgogICAgICAgeDE9IjUzLjg5Njc2MyIKICAgICAgIHkxPSI1MS4xNzk3ODciCiAgICAgICB4Mj0iNDcuNTAyMjM1IgogICAgICAgeTI9IjIxLjgzNzQyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NzciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzc3OSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM3ODEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ4NjYyIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50Mzc1NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwwLjUzNjcyMywwLDE2Ljg3MzA2KSIKICAgICAgIGN4PSIyNC44MzcxMjYiCiAgICAgICBjeT0iMzYuNDIxMTI3IgogICAgICAgZng9IjI0LjgzNzEyNiIKICAgICAgIGZ5PSIzNi40MjExMjciCiAgICAgICByPSIxNS42NDQ3MzciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDg2NjIiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDg2NjQiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wODY2NiIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwO3N0b3Atb3BhY2l0eTowOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI4NDciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzU5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC02NC4yNDc4MTEsNS41ODI1MTM4KSIKICAgICAgIHgxPSIzMi42NDc5NzIiCiAgICAgICB5MT0iMzAuNzQ4ODQ2IgogICAgICAgeDI9IjM3LjEyNDQ2MiIKICAgICAgIHkyPSIyNC44NDIyNTMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI2ODIiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzYxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC02NC4yNDc4MTEsNS41ODI1MTM4KSIKICAgICAgIHgxPSIzNi43MTM4MzciCiAgICAgICB5MT0iMzEuNDU1OTUyIgogICAgICAgeDI9IjM3LjEyNDQ2MiIKICAgICAgIHkyPSIyNC44NDIyNTMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI2ODIiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI2ODQiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzRlOWEwNjtzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyNjg2IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4OWFlZGM7c3RvcC1vcGFjaXR5OjA7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MjQwMiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NjctMyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjE4LjkzNTc2NiIKICAgICAgIHkxPSIyMy42Njc4OTYiCiAgICAgICB4Mj0iNTMuNTg4NjIzIgogICAgICAgeTI9IjI2LjY0OTM2MyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjQwMiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjQwNCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyNDA2IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM1MjhhYzU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mjg3MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NjkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSI0Ni44MzQ4MTYiCiAgICAgICB5MT0iNDUuMjY0MTIyIgogICAgICAgeDI9IjQ1LjM4MDQzNiIKICAgICAgIHkyPSI1MC45Mzk2NjciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI4NzEiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI4NzMiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjg3NSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzQ2NWE0O3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzk0NSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NjMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSIxOC45MzU3NjYiCiAgICAgICB5MT0iMjMuNjY3ODk2IgogICAgICAgeDI9IjUzLjU4ODYyMyIKICAgICAgIHkyPSIyNi42NDkzNjMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNDkiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMxNTEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzE1MyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNTI4YWM1O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI3OTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzcxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iNS45NjQ5MTc3IgogICAgICAgeTE9IjI2LjA0ODE2NCIKICAgICAgIHgyPSI1Mi44NTQwOTUiCiAgICAgICB5Mj0iMjYuMDQ4MTY0IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyNzk3IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyNzk5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI4MDEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyODQ3IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzc0NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtNjMuNzgyMzk4LDUuMzA0OTIwOCkiCiAgICAgICB4MT0iMTMuNDc4NTU0IgogICAgICAgeTE9IjEwLjYxMjIwNiIKICAgICAgIHgyPSIxNS40MTk0MTciCiAgICAgICB5Mj0iMTkuMTE1MTIyIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyODMxIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyODMzIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzNDY1YTQ7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNWI4NmJlO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwLjMzMzMzMzM0IgogICAgICAgICBpZD0ic3RvcDI4NTUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjgzNSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojODNhOGQ4O3N0b3Atb3BhY2l0eTowOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI4NDciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzQ3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0icm90YXRlKDE4MCwtOC4zNTk5NTA1LDI1LjcxNDk2MikiCiAgICAgICB4MT0iMzcuMTI4MDUyIgogICAgICAgeTE9IjI5LjcyOTYwNSIKICAgICAgIHgyPSIzNy4wNjU0MTQiCiAgICAgICB5Mj0iMjYuMTk0MDcxIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyODQ3IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyODQ5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM0ZTlhMDY7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjg1MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzQ2NWE0O3N0b3Atb3BhY2l0eTowOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIzODAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzUzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iNjIuNTEzODM2IgogICAgICAgeTE9IjM2LjA2MTIzNyIKICAgICAgIHgyPSIxNS45ODQ4NjMiCiAgICAgICB5Mj0iMjAuNjA4NTgiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIzODAiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDIzODIiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2I5Y2ZlNztzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyMzg0IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzOTQ1IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzc0OSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjYyLjUxMzgzNiIKICAgICAgIHkxPSIzNi4wNjEyMzciCiAgICAgICB4Mj0iMTUuOTg0ODYzIgogICAgICAgeTI9IjIwLjYwODU4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTgwIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMTgyIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNiOWNmZTc7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzE4NCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mjc5NyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM3NTUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSI1Ljk2NDkxNzciCiAgICAgICB5MT0iMjYuMDQ4MTY0IgogICAgICAgeDI9IjUyLjg1NDA5NSIKICAgICAgIHkyPSIyNi4wNDgxNjQiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDg2NjIiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzOTY4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMTMxNDQ2NSwwLDAsMS40NzAxNDI1LDExLjkxMjI4NywtNjkuNzYzNTYpIgogICAgICAgY3g9IjI0LjgzNzEyNiIKICAgICAgIGN5PSIzNi40MjExMjciCiAgICAgICBmeD0iMjQuODM3MTI2IgogICAgICAgZnk9IjM2LjQyMTEyNyIKICAgICAgIHI9IjE1LjY0NDczNyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mjg0NyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5NzAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC43NTk0OTQ2MSwwLDAsMC43NTk0OTQ2MSwtNTguMzk5OTI2LC0wLjE3NjMwODg2KSIKICAgICAgIHgxPSIzMi42NDc5NzIiCiAgICAgICB5MT0iMzAuNzQ4ODQ2IgogICAgICAgeDI9IjM3LjEyNDQ2MiIKICAgICAgIHkyPSIyNC44NDIyNTMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDI2ODIiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTcyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNzU5NDk0NjEsMCwwLDAuNzU5NDk0NjEsLTU4LjM5OTkyNiwtMC4xNzYzMDg4NikiCiAgICAgICB4MT0iMzYuNzEzODM3IgogICAgICAgeTE9IjMxLjQ1NTk1MiIKICAgICAgIHgyPSIzNy4xMjQ0NjIiCiAgICAgICB5Mj0iMjQuODQyMjUzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzOTE2IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzk2MyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgwMDAwMDA3LDAsMCwwLjc4NDMxMzY4LDAuMjAwMDAwODIsLTMwLjA5ODAzOCkiCiAgICAgICB4MT0iMzcuMjQ5OTk2IgogICAgICAgeTE9IjI1LjA5MjM0MiIKICAgICAgIHgyPSIzOS43NDk5OTYiCiAgICAgICB5Mj0iNDAuOTI0OTk5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTE2Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzOTE4IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YWUyMzQ7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzkyMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNGU5YTA2O3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjkuMDgyMDMwOCIKICAgICBpbmtzY2FwZTpjeD0iMzQuMTMzMzM1IgogICAgIGlua3NjYXBlOmN5PSIzNC4xMzMzMzUiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0iZzM5NTEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE1MzYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODAxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDAxIgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIgogICAgICAgb3JpZ2lueD0iMCIKICAgICAgIG9yaWdpbnk9IjAiCiAgICAgICBzcGFjaW5neD0iMSIKICAgICAgIHNwYWNpbmd5PSIxIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI4NjUiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bU3RlZmFuIFRyw7ZnZXJdPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzp0aXRsZT5mZW0tYm94PC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDE1LTExLTE1PC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kLzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOiM3MjlmY2Y7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMywxMyAzNywxOSA2MSwxMSAzMSw3IFoiCiAgICAgICBpZD0icGF0aDI5OTMiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mzc4Myk7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNjEsMTEgViA0NyBMIDM3LDU3IFYgMTkgWiIKICAgICAgIGlkPSJwYXRoMjk5NSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiCiAgICAgICBpZD0icGF0aDM4MjUiCiAgICAgICBkPSJtIDMsMTMgMzQsNiBWIDU3IEwgMyw1MSBaIgogICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM3NzMpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNSwxNS40Mjc3MiAwLjAwODY3LDMzLjkxOTExNiAzMC4wMDg2NzEsNS4yNjg3OTkgLTAuMDA4NywtMzMuOTMzNjE0IHoiCiAgICAgICBpZD0icGF0aDM3NjUiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzQ2NWE0O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM5LjAxMjQzLDIwLjQzMzgzMyAtMC4wMTIyNiwzMy41MzUzMDEgMjAuMDAxMTA1LC04LjMwMDk5MyAzLjZlLTQsLTMxLjg2NzM2MyB6IgogICAgICAgaWQ9InBhdGgzNzc1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICA8ZwogICAgICAgaWQ9ImczOTUxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzMuMjQ0MTQ5LDEyLjI4MDcxNykiPgogICAgICA8ZWxsaXBzZQogICAgICAgICByeT0iMjMuMDAwMDAyIgogICAgICAgICByeD0iMTcuNzAxMTgzIgogICAgICAgICBjeT0iLTE2LjIxOTI4NCIKICAgICAgICAgY3g9IjQwLjAxNDE2OCIKICAgICAgICAgaW5rc2NhcGU6cl9jeT0idHJ1ZSIKICAgICAgICAgaW5rc2NhcGU6cl9jeD0idHJ1ZSIKICAgICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7b3BhY2l0eTowLjM4MzMzMzMzO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDM5NjgpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxLjkxNTc3MTAxO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO21hcmtlcjpub25lIgogICAgICAgICBpZD0icGF0aDg2NjAiCiAgICAgICAgIHRyYW5zZm9ybT0ic2NhbGUoLTEpIiAvPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmJsb2NrO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM5NzApO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTp1cmwoI2xpbmVhckdyYWRpZW50Mzk3Mik7c3Ryb2tlLXdpZHRoOjAuNzU4MDAwMDI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lIgogICAgICAgICBkPSJtIC0zNi44NDkyNjcsMjUuMjE5MjkyIGMgMCwwIDYuNzg3OTgzLDAuNDc0Njg0IDQuNjk5MzczLC03LjUwMDAwOSBoIDUuOTA1NzQ1IGMgMCwxLjE0MTIxOCAtMC40NDY4NzcsOS4wMTg5OTkgLTEwLjYwNTExOCw3LjUwMDAwOSB6IgogICAgICAgICBpZD0icGF0aDI4MzkiCiAgICAgICAgIGlua3NjYXBlOnJfY3g9InRydWUiCiAgICAgICAgIGlua3NjYXBlOnJfY3k9InRydWUiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjYyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPGcKICAgICAgICAgaWQ9ImcxNjk2IgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjE0Mzg0ODgsMCwwLDEuMTQzODQ4OCwxLjUyMDAzNTQsLTQuMzk0NjExNykiPgogICAgICAgIDxnCiAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO3N0cm9rZS13aWR0aDoxLjMyOTA3MTQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgICAgaWQ9ImczOTU5IgogICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuOTM3NDk5OTQsMCwwLDAuOTM3NDk5OTQsLTU2LjUwNDM5MywyNS44NjI3MTMpIj4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgICAgICBpZD0icGF0aDMzNDMtMCIKICAgICAgICAgICAgIGQ9Im0gMzUsLTEzIHYgNCBjIC0xMCwwIC0xNywyIC0yMyw3IGwgNSw3IGMgNS41MzMxMDcsLTMuMjk2OTI0MyAxMS4zMDQ3MDMsLTUuOTMxMzgxMzcgMTgsLTYgdiA0IGwgMTQsLTggeiIKICAgICAgICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzOTYzKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzE3MmEwNDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgICAgICBpZD0icGF0aDMzNDMtMi05IgogICAgICAgICAgICAgZD0ibSAzNywtOS41ODIyMzA0IC0wLjAzNDUzLDIuNDk1OTEzNyBDIDI0LC03IDIwLC01IDE0Ljc0MjMyNCwtMS42MjE0ODA1IGwgMi44MjM4OTcsMy45NzQ2MTY5IEMgMjAuMjI0NDI0LDAuOTEzNjgzMjUgMjYsLTMgMzcsLTMgdiAyLjU4MjIzMDQgTCA0NSwtNSBaIgogICAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6bm9uZTtzdHJva2U6IzhhZTIzNDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgICA8L2c+CiAgICAgIDwvZz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ importFP_b64=\ """PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIGhlaWdodD0iMjYiCiAgIHdpZHRoPSIyNiIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMjYgMjYiCiAgIGlkPSJzdmcyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkyLjAgcjE1Mjk5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJpbXBvcnRGUC5zdmciPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTEwMCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxIgogICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAiCiAgICAgZ3VpZGV0b2xlcmFuY2U9IjEwIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNTM2IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgwMSIKICAgICBpZD0ibmFtZWR2aWV3OTgiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6em9vbT0iMTEuODY1Mzg1IgogICAgIGlua3NjYXBlOmN4PSItNS41NzQ0NyIKICAgICBpbmtzY2FwZTpjeT0iMTkuNjgyMDM4IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmcyIgogICAgIGlua3NjYXBlOnNuYXAtdG8tZ3VpZGVzPSJmYWxzZSIKICAgICBpbmtzY2FwZTpzbmFwLWdyaWRzPSJmYWxzZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDQ4IgogICAgICAgZW1wc3BhY2luZz0iMSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaWQ9ImUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN5PSIyMC40OTM5OTkiCiAgICAgICBjeD0iMzUuMjkyOTk5IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLC0wLjg0MzAyLDEuMDIwMiwwLC00Ljg5NjMyNDksNDEuMDU5NDE4KSIKICAgICAgIHI9IjE2Ljk1NTk5OSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiM3M2QyMTYiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AxMi03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjNGU5YTA2IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMTQtMiIgLz4KICAgIDwvcmFkaWFsR3JhZGllbnQ+CiAgICA8cGF0dGVybgogICAgICAgeT0iMCIKICAgICAgIHg9IjAiCiAgICAgICBoZWlnaHQ9IjYiCiAgICAgICB3aWR0aD0iNiIKICAgICAgIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0iRU1GaGJhc2VwYXR0ZXJuIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMzgwIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyMzgyIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNiOWNmZTc7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjM4NCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzk0NSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YWUyMzQ7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzOTQ3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzNkMjE2O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzk0OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM5MTYiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTYzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODAwMDAwMDcsMCwwLDAuNzg0MzEzNjgsMC4yMDAwMDA4MiwtMzAuMDk4MDM4KSIKICAgICAgIHgxPSIzNy4yNDk5OTYiCiAgICAgICB5MT0iMjUuMDkyMzQyIgogICAgICAgeDI9IjM5Ljc0OTk5NiIKICAgICAgIHkyPSI0MC45MjQ5OTkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5MTYiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM5MTgiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzhhZTIzNDtzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzOTIwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM0ZTlhMDY7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8ZwogICAgIGlkPSJnOTE5IgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMSkiPgogICAgPHJlY3QKICAgICAgIGlkPSJyZWN0NTEiCiAgICAgICB4PSItMjAuNTkzNTY1IgogICAgICAgeT0iMS40OTQ3NDkzIgogICAgICAgd2lkdGg9IjE0LjA4ODY5IgogICAgICAgaGVpZ2h0PSIyMi45MjQzNTUiCiAgICAgICB0cmFuc2Zvcm09InJvdGF0ZSgtOTApIgogICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtzdHJva2U6IzU0NTQ1NDtzdHJva2Utd2lkdGg6MC45NzcxMTI4OTtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDUzIgogICAgICAgZD0ibSAwLjk4NjIzOTI0LDExLjA4MjEyNiBhIDIuMjI0MDU4MSwxLjk1ODk0NDkgMCAxIDEgMC4wMTM3NjAxLDMuOTE3ODcxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM1NDU0NTQ7c3Ryb2tlLXdpZHRoOjE7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICA8ZwogICAgICAgaWQ9ImczOTgzIj4KICAgICAgPHJlY3QKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwYzkyMTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzU0NTQ1NDtzdHJva2Utd2lkdGg6MC45MzI0MTMyMjtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgdHJhbnNmb3JtPSJyb3RhdGUoLTkwKSIKICAgICAgICAgaGVpZ2h0PSI0LjA2NTk3MDkiCiAgICAgICAgIHdpZHRoPSI2LjA1OTA4MzUiCiAgICAgICAgIHk9IjQuNTEyMzQwMSIKICAgICAgICAgeD0iLTguNTMxNzc2NCIKICAgICAgICAgaWQ9InJlY3Q3MyIgLz4KICAgICAgPHJlY3QKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwYzkyMTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzU0NTQ1NDtzdHJva2Utd2lkdGg6MC45MzI0MTMyMjtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgdHJhbnNmb3JtPSJyb3RhdGUoLTkwKSIKICAgICAgICAgaGVpZ2h0PSI0LjA2NTk3MDkiCiAgICAgICAgIHdpZHRoPSI2LjA1OTA4MzUiCiAgICAgICAgIHk9IjExLjQ1MTYxNyIKICAgICAgICAgeD0iLTguNTA3NjYxOCIKICAgICAgICAgaWQ9InJlY3Q3My01IiAvPgogICAgICA8cmVjdAogICAgICAgICBzdHlsZT0iZmlsbDojMDBjOTIxO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojNTQ1NDU0O3N0cm9rZS13aWR0aDowLjkzMjQxMzIyO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICB0cmFuc2Zvcm09InJvdGF0ZSgtOTApIgogICAgICAgICBoZWlnaHQ9IjQuMDY1OTcwOSIKICAgICAgICAgd2lkdGg9IjYuMDU5MDgzNSIKICAgICAgICAgeT0iMTguNDg4ODk1IgogICAgICAgICB4PSItOC41NDk4MDA5IgogICAgICAgICBpZD0icmVjdDczLTU2IiAvPgogICAgPC9nPgogICAgPHJlY3QKICAgICAgIGlkPSJyZWN0NzMtMCIKICAgICAgIHg9Ii0yNC42MjI2MjUiCiAgICAgICB5PSI0LjU2NjE4ODgiCiAgICAgICB3aWR0aD0iNi4wNTkwODM1IgogICAgICAgaGVpZ2h0PSI0LjA2NTk3MDkiCiAgICAgICB0cmFuc2Zvcm09InJvdGF0ZSgtOTApIgogICAgICAgc3R5bGU9ImZpbGw6IzAwYzkyMTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzU0NTQ1NDtzdHJva2Utd2lkdGg6MC45MzI0MTMyMjtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgIDxyZWN0CiAgICAgICBpZD0icmVjdDczLTUtNCIKICAgICAgIHg9Ii0yNC41OTg1MTEiCiAgICAgICB5PSIxMS41MDU0NjYiCiAgICAgICB3aWR0aD0iNi4wNTkwODM1IgogICAgICAgaGVpZ2h0PSI0LjA2NTk3MDkiCiAgICAgICB0cmFuc2Zvcm09InJvdGF0ZSgtOTApIgogICAgICAgc3R5bGU9ImZpbGw6IzAwYzkyMTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzU0NTQ1NDtzdHJva2Utd2lkdGg6MC45MzI0MTMyMjtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgIDxyZWN0CiAgICAgICBpZD0icmVjdDczLTU2LTkiCiAgICAgICB4PSItMjQuNjQwNjUiCiAgICAgICB5PSIxOC41NDI3NDQiCiAgICAgICB3aWR0aD0iNi4wNTkwODM1IgogICAgICAgaGVpZ2h0PSI0LjA2NTk3MDkiCiAgICAgICB0cmFuc2Zvcm09InJvdGF0ZSgtOTApIgogICAgICAgc3R5bGU9ImZpbGw6IzAwYzkyMTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzU0NTQ1NDtzdHJva2Utd2lkdGg6MC45MzI0MTMyMjtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICA8L2c+CiAgPGcKICAgICBpZD0iZzE2OTYiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC40OTMwNzQzNCwwLjM0NTI1NDM5LC0wLjM0NTI1NDM5LDAuNDkzMDc0MzQsMzEuMzM5MDIyLDQuNDk3NDA5NikiPgogICAgPGcKICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtzdHJva2Utd2lkdGg6MS4zMjkwNzE0O3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgaWQ9ImczOTU5IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC45Mzc0OTk5NCwwLDAsMC45Mzc0OTk5NCwtNTYuNTA0MzkzLDI1Ljg2MjcxMykiPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0wIgogICAgICAgICBkPSJtIDM1LC0xMyB2IDQgYyAtMTAsMCAtMTcsMiAtMjMsNyBsIDUsNyBjIDUuNTMzMTA3LC0zLjI5NjkyNDMgMTEuMzA0NzAzLC01LjkzMTM4MTM3IDE4LC02IHYgNCBsIDE0LC04IHoiCiAgICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzOTYzKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzE3MmEwNDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0yLTkiCiAgICAgICAgIGQ9Im0gMzcsLTkuNTgyMjMwNCAtMC4wMzQ1MywyLjQ5NTkxMzcgQyAyNCwtNyAyMCwtNSAxNC43NDIzMjQsLTEuNjIxNDgwNSBsIDIuODIzODk3LDMuOTc0NjE2OSBDIDIwLjIyNDQyNCwwLjkxMzY4MzI1IDI2LC0zIDM3LC0zIHYgMi41ODIyMzA0IEwgNDUsLTUgWiIKICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6bm9uZTtzdHJva2U6IzhhZTIzNDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg== """ collisions_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI1NjgiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMCByMTUyOTkiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkNvbGxpc2lvbnMuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjU3MCI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDg0NiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZjAwMDA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wODQyIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmYwMDAwO3N0b3Atb3BhY2l0eTowOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDg0NCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4NjQiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjYiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcxYjJmODtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2OCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAyNzk1O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM1OTMiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojYzhlMGY5O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM1OTUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM2MzdkY2E7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzU5NyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU3NiIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE0MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzNDY1YTQ7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTQ1IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzE0NyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNDMtNyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTQ1LTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMTQ3LTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iMjYuMjgxNzQ0IgogICAgICAgZng9IjQ2LjUzNDEzNCIKICAgICAgIGN5PSIyNi4yODE3NDQiCiAgICAgICBjeD0iNDYuNTM0MTM0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDkwLTMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzLTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMTQ4NjA4OCwxLjg0Nzc2MTcsLTMuMDU2NjczLDEuOTAwMDk0LDczLjI1Nzc0NSwtMTEyLjEwMTYpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTQzLTYiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzE0NS03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZDNkN2NmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzE0Ny01IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgcj0iMTkuNTcxNDI4IgogICAgICAgZnk9IjI2LjI4MTc0NCIKICAgICAgIGZ4PSI0Ni41MzQxMzQiCiAgICAgICBjeT0iMjYuMjgxNzQ0IgogICAgICAgY3g9IjQ2LjUzNDEzNCIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4xNDg2MDg4LDEuODQ3NzYxNywtMy4wNTY2NzMsMS45MDAwOTQsNzMuMjU3NzQ1LC0xMTIuMTAxNikiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwMTciCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzLTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIHI9IjE5LjU3MTQyOCIKICAgICAgIGZ5PSIyNi4yODE3NDQiCiAgICAgICBmeD0iNDYuNTM0MTM0IgogICAgICAgY3k9IjI2LjI4MTc0NCIKICAgICAgIGN4PSI0Ni41MzQxMzQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwOTAtNSIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMxNDMtNjIiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMTQ4NjA4OCwxLjg0Nzc2MTcsLTMuMDU2NjczLDEuOTAwMDk0LDczLjI1Nzc0NSwtMTEyLjEwMTYpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTQzLTYyIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDMxNDUtOSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2QzZDdjZjtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMxNDctMSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIHI9IjE5LjU3MTQyOCIKICAgICAgIGZ5PSIyNi4yODE3NDQiCiAgICAgICBmeD0iNDYuNTM0MTM0IgogICAgICAgY3k9IjI2LjI4MTc0NCIKICAgICAgIGN4PSI0Ni41MzQxMzQiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMTQ4NjA4OCwxLjg0Nzc2MTcsLTMuMDU2NjczLDEuOTAwMDk0LDczLjI1Nzc0NSwtMTEyLjEwMTYpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDE3LTIiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzLTYyIgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ4NDYiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ4NDgiCiAgICAgICB4MT0iMjQiCiAgICAgICB5MT0iMzAiCiAgICAgICB4Mj0iNDAiCiAgICAgICB5Mj0iNDAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjQuNzIzMjE2MSIKICAgICBpbmtzY2FwZTpjeD0iMzEuNjg2NDg1IgogICAgIGlua3NjYXBlOmN5PSI0OC42MzU3NTUiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0iZzM1NjAiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE1MzYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODAxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQyOTk2IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI1NzMiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6dGl0bGU+UGFydF9Db21tb248L2RjOnRpdGxlPgogICAgICAgIDxkYzpkYXRlPjIwMTEtMTAtMTA8L2RjOmRhdGU+CiAgICAgICAgPGRjOnJlbGF0aW9uPmh0dHA6Ly93d3cuZnJlZWNhZHdlYi5vcmcvd2lraS9pbmRleC5waHA/dGl0bGU9QXJ0d29yazwvZGM6cmVsYXRpb24+CiAgICAgICAgPGRjOnB1Ymxpc2hlcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQ8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnB1Ymxpc2hlcj4KICAgICAgICA8ZGM6aWRlbnRpZmllcj5GcmVlQ0FEL3NyYy9Nb2QvUGFydC9HdWkvUmVzb3VyY2VzL2ljb25zL1BhcnRfQ29tbW9uLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPGcKICAgICAgIGlkPSJnMzU2MCIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuOTEyNzM2MTgsMCwwLDAuOTAzNDk1MTUsMTA1Ljg1Mzc0LDIuODAwMzYzKSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMTIwOTIyMSwwLDAsMS4xMzIzNTQ2LC0xNTEuNTIwMzksMi4wMjU5MikiCiAgICAgICAgIGQ9Ik0gNzEuNzg1NzE1LDM0LjU3MTQyNiBBIDE4LjU3MTQyOCwxOC41NzE0MjggMCAwIDEgNTMuMjE0Mjg3LDUzLjE0Mjg1NSAxOC41NzE0MjgsMTguNTcxNDI4IDAgMCAxIDM0LjY0Mjg1OSwzNC41NzE0MjYgMTguNTcxNDI4LDE4LjU3MTQyOCAwIDAgMSA1My4yMTQyODcsMTUuOTk5OTk4IDE4LjU3MTQyOCwxOC41NzE0MjggMCAwIDEgNzEuNzg1NzE1LDM0LjU3MTQyNiBaIgogICAgICAgICBzb2RpcG9kaTpyeT0iMTguNTcxNDI4IgogICAgICAgICBzb2RpcG9kaTpyeD0iMTguNTcxNDI4IgogICAgICAgICBzb2RpcG9kaTpjeT0iMzQuNTcxNDI2IgogICAgICAgICBzb2RpcG9kaTpjeD0iNTMuMjE0Mjg3IgogICAgICAgICBpZD0icGF0aDM1NTAtMy03IgogICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI3JhZGlhbEdyYWRpZW50MzAxNy0yKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzJlMzQzNjtzdHJva2Utd2lkdGg6MS45NTI3MzcyMTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIiAvPgogICAgICA8cGF0aAogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjEyMDk1NDEsMCwwLDEuMTMyNDE5NCwtMTMxLjgwMDU4LC0xMy40NzI4OTYpIgogICAgICAgICBkPSJNIDcxLjc4NTcxNSwzNC41NzE0MjYgQSAxOC41NzE0MjgsMTguNTcxNDI4IDAgMCAxIDUzLjIxNDI4Nyw1My4xNDI4NTUgMTguNTcxNDI4LDE4LjU3MTQyOCAwIDAgMSAzNC42NDI4NTksMzQuNTcxNDI2IDE4LjU3MTQyOCwxOC41NzE0MjggMCAwIDEgNTMuMjE0Mjg3LDE1Ljk5OTk5OCAxOC41NzE0MjgsMTguNTcxNDI4IDAgMCAxIDcxLjc4NTcxNSwzNC41NzE0MjYgWiIKICAgICAgICAgc29kaXBvZGk6cnk9IjE4LjU3MTQyOCIKICAgICAgICAgc29kaXBvZGk6cng9IjE4LjU3MTQyOCIKICAgICAgICAgc29kaXBvZGk6Y3k9IjM0LjU3MTQyNiIKICAgICAgICAgc29kaXBvZGk6Y3g9IjUzLjIxNDI4NyIKICAgICAgICAgaWQ9InBhdGgzNTUwLTMtMyIKICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDMwMTcpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMmUzNDM2O3N0cm9rZS13aWR0aDoxLjk1MjY1MzQxO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQ4NDgpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojODUxNTIxO3N0cm9rZS13aWR0aDoxLjk5NzgyODcyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBkPSJNIDIyIDIxIEMgMjEuODgzMzk2IDIxIDIxLjc3MjM1NCAyMS4wMjkxNTggMjEuNjU2MjUgMjEuMDMxMjUgQyAyMS4yMjg4NTIgMjIuNjE0MTMxIDIxIDI0LjI4MjAwNCAyMSAyNiBDIDIxIDM2LjQ5NDAxIDI5LjUwNTk5MSA0NSA0MCA0NSBDIDQwLjExNjYwNCA0NSA0MC4yMjc2NDYgNDQuOTcwODQyIDQwLjM0Mzc1IDQ0Ljk2ODc1IEMgNDAuNzcxMTQ4IDQzLjM4NTg2OSA0MSA0MS43MTc5OTYgNDEgNDAgQyA0MSAyOS41MDU5OSAzMi40OTQwMDkgMjEgMjIgMjEgeiAiCiAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMDk1NjA2OCwwLDAsMS4xMDY4MTI4LC0xMTUuOTc0MDgsLTMuMDk5NDc3NikiCiAgICAgICAgIGlkPSJwYXRoMzU1MC0zIiAvPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZmYyYzJjO3N0cm9rZS13aWR0aDoxLjk5NzgyODY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICAgIGQ9Ik0gMjMuMjgxMjUgMjMuMDYyNSBDIDIzLjExNTQ5OCAyNC4wMTU0NjggMjMgMjQuOTk5NjE1IDIzIDI2IEMgMjMgMzQuOTU5ODk5IDI5LjkyNTcxNyA0Mi4yODQ1ODcgMzguNzE4NzUgNDIuOTM3NSBDIDM4Ljg4NDUwMiA0MS45ODQ1MzIgMzkgNDEuMDAwMzg1IDM5IDQwIEMgMzkgMzEuMDQwMTAxIDMyLjA3NDI4MyAyMy43MTU0MTMgMjMuMjgxMjUgMjMuMDYyNSB6ICIKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wOTU2MDY4LDAsMCwxLjEwNjgxMjgsLTExNS45NzQwOCwtMy4wOTk0Nzc2KSIKICAgICAgICAgaWQ9InBhdGgzNTUwLTMtMSIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ export3DModel_b64=\ """ iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUBSURBVEiJtZVtiFzVGcd/577M3Xm5M7OzMzs7s5u4myWY7ULBGDG120akDfRbhQj90qIIkZAvCir9IMF+KIV+aDVCkNDQL0UtikKLqAGpsVIbUHxZzMTpromy2ZfMvkz27syd+/r0w+7MrgQ0kfiHA5fzwPmd//+55xzFlo4fP/77KIqejOPYANA0LUwkEn84efLkiVtRN7qgKIqeOHbsmGHbNgCO4xinTp36LXDiVtR7oDiOzVwuRyKRYGtHiIh5q+pGtVp9GqBer3PmzBl0Xe86pF6vc6vqqlqtyuHDh/k+dfbs2c3oRkdHcRznphcIgpDG6jrLjo8YfYjEqMgnldCxkzo5O027tQHs6NGNgi58scCq45EfHCZTHKZ8+xSTlTG0rcgA2utrOCuL/Pu9f7K83CBO794G3aiyo/uZmPgRztoSHeca8/WPqf3nTVJ2lureO2g5a8x8+gGkypiDBxjeU+bqR6/cPChhJUnlSiSzRUQEUAB4rsN64wozXyyRGv85AiBCFEcIgmFZFjMzM0RRRPcMfJMkBs8PiGNBZHvEYtKXLaGMBFEUbm5CBJQCEYx8Pk+tVgNgZGSEcrn8jaA4jvG8kCiKtiD0YJrEiAhRuA1SmgYChlKqt0iz2fxWkIjg+T5hGH/NkQhYetQD0YXrOkKMISJ0Yblc7luji0XwvJAgCK9zpFuCSEwUBtCdh01HzWaTqakpwjC8oR7FkeD5Ab4fXueoz1BIDPEOR2y2CMPzPMbHx5m7MseH9XM47jKu7yJbW0mYCZKJDNnkAAV7kNAuYACe5xPvdBQLoWX2otOUhmmZiNsgdOZ3/N4izDVmOfRIhV0/LKHpCgSCjuAs+cxNX2X6nTp++xyZ2ZcpVqfor9yNlhrAsDK03YCgs4REy1h9BTR3jqRziWKfx3RncRvUbruYhsnIZJLS7hQdL0Apha4rhvamuP1QgfuOD7Ox6nPxX+u8/7eXWJh9lYH0OLo2gpGrsrD4Or67wljxJ+weKqL6UzhOBGxdQZcvX2ZlZQVDN9hYC8gPh8xfbqDpGvrWMBI6yZRFMmNx15ESd95fZPb8Nd7600UI/0fGHSDwGgyMJWkufk5mVftab3XbttXs7Oy5fD5/r9YXktnjU91r43dCjISOUgoRCP0IrxPgtjw6rofSFIPjGe46UiI/bFA7v8gvHt/FgQdKvPf3S9xz5yEWF65Sq9V+B5zrHaKjR4+Kb60yn36HqQerm7GZGoah93roeSGdlofb9gmDEMPUsfMp0ukkkcRomkIpxbt/WcCrVRjr38/p06dVLzoATdOCQrZovvFak0/+0exZ7rP1oDBqxtV9aWvXHWmG9mbIDWTwvZCNa23WGg7rqy2yhQxWn0kUxxz89SDPH5nG3JOLetF1Pw4ePGi5TnBPpXCbNjq0j7HKRDiQG3xhbv7L59avhtPeFbt84e2V0n9fXOTSeQd7UGdoj42dTxMGIc2VFq11l6ATkc0nyVYN3n/jopscDJ5tLRFs3z83qKHJxA9E4l9quhwzknrlZ49WtIkfl5Rzrc3SlVV0Xae/ZJPtT/PXhy8EzcXgj0ufhU/dNKiryUkSK8r46sCvCsWf/ma37m54BEGIUgqlKzJ2kvnPHV58bDZAGftu+j3qqiHmQ0qknKuYzNWaaIaGUoBSKKC9uoFhQaZg6q1m8Mx3dlSeMP6MzsN0X74dum5CZPn/vEjUhOHm6mAAAAAASUVORK5CYII= """ virtual_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNDgiCiAgIGhlaWdodD0iNDgiCiAgIHZpZXdCb3g9IjAgMCA0OC4wMDAwMDEgNDguMDAwMDAxIgogICBpZD0ic3ZnMTU4MjUiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4wIHIxNTI5OSIKICAgc29kaXBvZGk6ZG9jbmFtZT0ibWVjaGFuaWNhbC5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMxNTgyNyIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTIuNjI1IgogICAgIGlua3NjYXBlOmN4PSIxMC40OTUwNSIKICAgICBpbmtzY2FwZTpjeT0iMjQiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgdW5pdHM9InB4IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI2MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI3ODciCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjE4NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iODciCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGExNTgzMCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiCiAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtMTAwNC4zNjIyKSI+CiAgICA8ZwogICAgICAgaWQ9Imc3ODQ3IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS42ODA5MDc0LDAsMCwxLjY4MDkwNzQsLTMyLjcwNTkzOSwtNjk5Ljc5OTc3KSIKICAgICAgIHN0eWxlPSJzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjY2NjYyIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwxMDA0LjM2MjIpIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDc4NDUiCiAgICAgICAgIGQ9Im0gMjYuNDYxMTA3LDEzLjM0MDgzMSBjIC0xLjM2MTU2NywxLjcyOTgzMyAtMi4xNzIzNzYsMy42NjI3NzIgLTQuMTE4MDM4LDguMDg1NzA1IC0wLjQ4NjkzMiwxLjY3MDkyOCAtMC43MjYwODMsMi42MTc4ODkgLTAuNTY5MDcyLDUuOTA4OTYzIDIuMzcyMzk2LDIuOTIzMTc2IDYuMzQxMjI4LDYuNzA0MjAyIDcuODQ5NTQ5LDcuMDUwMDQxIDEuOTczMDQsMC40NDg0NzkgOS45MDc3OSwtMC42OTQzOTIgMTEuODkwMTYxLC0xLjA2NjIxMSAxLjc5NzkzMiwtMi43ODYzMjMgMy4zMTI2NzMsLTYuMTAyNDczIDQuNTExNjAyLC04LjU3MjcwOSAwLjA0NjE4LC0yLjEwOTU4NiAtMC4wMDYsLTMuMTI4ODk1IC0wLjAwNTEsLTUuMTk1MjM2IC0xLjQ0NzIxNCwtMi42NDMwMiAtNC45MjA1NjMsLTQuOTQ0OTQzIC03LjYxOTU4NiwtNy4wNDQ3MSAtMy44ODAzMjgsLTAuNDQxMTk5IC03Ljk1OTM0NywwLjA5MTgzIC0xMS45Mzk1MTYsMC44MzQxNTcgeiBtIDYuNzM2NjUzLDguODU1MjgzIGMgMS4zNTA4OTYsLTAuMDE5MDIgMS4yMzc3MzMsMC4wOTg0NSAyLjQ2OTc2OCwwLjIzNDk5MSAxLjM2NDkxNSwwLjg4OTQ1NCAwLjY4MzQ3MiwwLjIzNDA1NiAxLjg1NTM3MSwxLjMzMjkyMSAtMS4wMzI1NDcsMC41ODI5MzMgLTEuMTY3MDk2LDAuNTE2NTQ5IC0xLjkyNjU3MSwwLjg0NTIwMSAtMi4yNjM3MzcsMC4wOTc2MSAtMS42NTEyMDQsMC4wNTc5MyAtMy42MzU3NSwtMC4zMDgwMDEgLTEuNDczNDk5LC0wLjczNjg3OCAtMC4yNTk5NSwwLjA1NzI3IC0xLjc0NzYxMSwtMS4yMzU0MjQgMi44MTEyNjMsLTAuOTA0Mzk0IDEuMjkzNjI3LC0wLjU5Nzk2IDIuOTg0NzkzLC0wLjg2OTY4OCB6IgogICAgICAgICBzdHlsZT0ib3BhY2l0eToxO2ZpbGw6I2ZmYWEwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2NjAiCiAgICAgICAgIGQ9Im0gMzcuNzcwNzU0LDEwMjguMjkzIGEgNS40OTI5ODUzLDQuMDI3OTE2MiA1LjMzMDU5OTQgMCAwIDEuOTM2NDE3LC0yLjcyOSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDY2MiIKICAgICAgICAgZD0ibSAzOS43MDcxNzEsMTAyNS41NjQgYSA1LjQ5MzI0NDgsNC4wMjgxMDY1IDUuMzMwNTk5NCAxIDAgLTEwLjkzODg5OCwtMS4wMjE1IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0NjY0IgogICAgICAgICBkPSJtIDI4Ljc2ODI3MywxMDI0LjU0MjUgYSA1LjQ5Mjk4NTMsNC4wMjc5MTYyIDUuMzMwNTk5NCAwIDAgOS4wMDI0ODEsMy43NTA1IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPGcKICAgICAgICAgaWQ9Imc0NjY2IgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgyLjY0NTk4NzMsMC4yNDY4ODYxOSwwLjI0Njg4NjE5LC0yLjY0NTk4NzMsLTM2OC44NjQ5MSwxMTE2LjY1NjMpIgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjIyMzg2NDk4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSI+CiAgICAgICAgPGVsbGlwc2UKICAgICAgICAgICBpZD0iZWxsaXBzZTQ2NjgiCiAgICAgICAgICAgcnk9IjIuMTA4MTkwMSIKICAgICAgICAgICByeD0iMi44NzUiCiAgICAgICAgICAgY3k9IjQ4LjU1OTUwMiIKICAgICAgICAgICBjeD0iMTQ3LjgyOCIKICAgICAgICAgICBzdHlsZT0ic3Ryb2tlLXdpZHRoOjAuMjIzODY0OTg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDwvZz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0NjcwIgogICAgICAgICBkPSJtIDM3Ljk1MDExNiwxMDI4LjE4MzMgYSA1LjQ5Mjk4NTMsNC4wMjc5MTYyIDUuMzMwNTk5NCAwIDAgLTcuOTM4ODUxLC0wLjc0MDUiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2NzIiCiAgICAgICAgIGQ9Im0gNDEuMTA5ODk1LDEwMzAuOTMxMiBhIDEwLjYyOTkyMSw3Ljc5NDc2MTYgNS4zMzA1OTk0IDAgMCAzLjE0MDc4OCwtMy4xODIiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2NzQiCiAgICAgICAgIGQ9Im0gMzYuMTM3NjQzLDEwMzIuNDMzMyBhIDEwLjYyOTkyMSw3Ljc5NDc2MTYgNS4zMzA1OTk0IDAgMCA0Ljk3MjI1MiwtMS41MDIxIgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0Njc2IgogICAgICAgICBkPSJtIDQ0LjI1MDY4MywxMDI3Ljc0OTIgYSAxMC42Mjk5MjEsNy43OTQ3NjE2IDUuMzMwNTk5NCAwIDAgLTEuODYyODU4LC03Ljc2ODEiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2NzgiCiAgICAgICAgIGQ9Im0gMjYuMTYxNzQ1LDEwMjkuMzQ5MiBhIDEwLjYyOTkyMSw3Ljc5NDc2MTYgNS4zMzA1OTk0IDAgMCA5Ljk3NTg5OCwzLjA4NDEiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2ODAiCiAgICAgICAgIGQ9Im0gNDIuMzg3ODI1LDEwMTkuOTgxMSBhIDEwLjYyOTkyMSw3Ljc5NDc2MTYgNS4zMzA1OTk0IDAgMCAtOS45NzY5OTcsLTMuMDg2NiIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDY4MiIKICAgICAgICAgZD0ibSAyNC4yOTc3ODksMTAyMS41Nzg2IGEgMTAuNjI5OTIxLDcuNzk0NzYxNiA1LjMzMDU5OTQgMCAwIDEuODYzOTU2LDcuNzcwNiIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDY4NCIKICAgICAgICAgZD0ibSAzMi40MTA4MjgsMTAxNi44OTQ1IGEgMTAuNjI5OTIxLDcuNzk0NzYxNiA1LjMzMDU5OTQgMCAwIC04LjExMzAzOSw0LjY4NDEiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2ODYiCiAgICAgICAgIGQ9Im0gNDQuMjUwNjgzLDEwMjcuNzQ5MiAtMC4xNDE3MzIsMC4yODQ2IC0wLjE0Mjc5NiwwLjI4OCAtMC4xNDMxNDksMC4yOTQ1IC0wLjE0NDU2NywwLjI5ODMgLTAuMTQ0NTY3LDAuMzA3MiAtMC4xNDcwNDcsMC4zMTE5IC0wLjE0OTg4MiwwLjMxNjcgLTAuMTUwNTkxLDAuMzI2NCAtMC4xNTI3MTYsMC4zMzM3IC0wLjE1NDQ4OSwwLjM0NDEgLTAuMTU3Njc3LDAuMzUxOSAtMC4xNjEyMiwwLjM2IC0wLjE2NDA1NSwwLjM3MSAtMC4xNjcyNDUsMC4zODE4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0Njg4IgogICAgICAgICBkPSJtIDQyLjEyODMxMywxMDMyLjMxOTQgLTAuNDYzNSwtMC4wMTggLTAuNDU0ODksLTAuMDE0IC0wLjQ0NzIzNiwtMC4wMSAtMC40NDExNDIsLTAuMDEgLTAuNDM1NDcyLDAgLTAuNDI5NDE0LDAgLTAuNDI0NzAxLDAuMDEgLTAuNDE5NTYzLDAuMDExIC0wLjQxNzI5NSwwLjAxNCAtMC40MTU1MjQsMC4wMTQgLTAuNDExNzY3LDAuMDIxIC0wLjQxMTEzLDAuMDI1IC0wLjQwOTM5NCwwLjAzMiAtMC40MDk2NDIsMC4wMzUiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2OTAiCiAgICAgICAgIGQ9Im0gNDYuMjk0MjUsMTAyNC4wMTYzIC0wLjE1NTkwNiwwLjI2NDcgLTAuMTU0NDg4LDAuMjYxMiAtMC4xNTEyOTksMC4yNjIyIC0wLjE0OTUyOCwwLjI2MTEgLTAuMTQ2NjkzLDAuMjYyOSAtMC4xNDc0MDEsMC4yNjAxIC0wLjE0Mzg1OSwwLjI2NDMgLTAuMTQzODU4LDAuMjY0IC0wLjE0MzUwNCwwLjI2NCAtMC4xNDEzNzgsMC4yNjg5IC0wLjE0MjA4NiwwLjI2OTMgLTAuMTQwMzE1LDAuMjc0MyAtMC4xNDIwODcsMC4yNzUzIC0wLjE0MDY2OSwwLjI4MDYiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2OTIiCiAgICAgICAgIGQ9Im0gNDIuMzg3ODI1LDEwMTkuOTgxMSAwLjI2Nzg3NCwwLjI0NzMgMC4yNjg1ODIsMC4yNTMgMC4yNjc4NzQsMC4yNTY1IDAuMjY4OTM3LDAuMjYyMiAwLjI3MTQxOCwwLjI3IDAuMjcyODM0LDAuMjc1MyAwLjI3Mzg5OCwwLjI4MDcgMC4yNzY3MzIsMC4yODgxIDAuMjc5NTY3LDAuMjk1OCAwLjI4MzExLDAuMzAzIDAuMjg3MzYyLDAuMzEyOCAwLjI5MDkwNiwwLjMyIDAuMjk2OTI5LDAuMzMxNyAwLjMwMDQ3MywwLjMzOTEiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ2OTQiCiAgICAgICAgIGQ9Im0gMzAuMDY4MTM1LDEwMzMuMzg0NSAtMC4zMDA0NzIsLTAuMzM5MSAtMC4yOTY5MjksLTAuMzMxNyAtMC4yOTA5MDYsLTAuMzE5OSAtMC4yODczNjIsLTAuMzEyOSAtMC4yODMxMSwtMC4zMDI5IC0wLjI3OTU2NywtMC4yOTU5IC0wLjI3NjczMywtMC4yODgxIC0wLjI3Mzg5NywtMC4yODA2IC0wLjI3MjgzNSwtMC4yNzUzIC0wLjI3MTA2MywtMC4yNyAtMC4yNjg5MzcsLTAuMjYxOSAtMC4yNjc4NzQsLTAuMjU2NSAtMC4yNjg1ODIsLTAuMjUzIC0wLjI2Nzg3NSwtMC4yNDczIgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0Njk2IgogICAgICAgICBkPSJtIDM2LjEzNzY0MywxMDMyLjQzMzMgLTAuNDEwNjY5LDAuMDM4IC0wLjQxMDQ5MiwwLjA0NCAtMC40MTM1MDQsMC4wNDUgLTAuNDE0NTMxLDAuMDUyIC0wLjQxODQ2NSwwLjA1NSAtMC40MjA2MjYsMC4wNjIgLTAuNDI1OTA2LDAuMDY1IC0wLjQzMTQzMywwLjA2OCAtMC40MzUwMTEsMC4wNzYgLTAuNDQzMTk3LDAuMDc4IC0wLjQ0OTI5MiwwLjA4NCAtMC40NTcyOTksMC4wODkgLTAuNDY0MjgsMC4wOTYgLTAuNDc0ODAzLDAuMDk5IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0Njk4IgogICAgICAgICBkPSJtIDM4LjQwMTUzMywxMDE2Ljc4MDYgMC4zMTE4MTEsMC4yMjE4IDAuMzA2MTQyLDAuMjIyMiAwLjMwMDgyNywwLjIyMjEgMC4yOTU1MTEsMC4yMjIyIDAuMjkwNTUyLDAuMjIxOCAwLjI4NzAwOCwwLjIyMzkgMC4yODM0NjQsMC4yMjYxIDAuMjc5MjEzLDAuMjI1NCAwLjI3NzQ0MSwwLjIyOTYgMC4yNzMxODksMC4yMjg5IDAuMjcyODM0LDAuMjM1MiAwLjI3MDM1NSwwLjIzNjcgMC4yNjkyOTEsMC4yNDAzIDAuMjY4NTgzLDAuMjQzNyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDcwMCIKICAgICAgICAgZD0ibSAzMi40MTA4MjgsMTAxNi44OTQ1IDAuNDA5Njc4LC0wLjAzNSAwLjQxMDc0LC0wLjAyOCAwLjQwOTc4MywtMC4wMjggMC40MTMwNzksLTAuMDIxIDAuNDE0MjEzLC0wLjAxOCAwLjQxNzI5NSwtMC4wMTQgMC40MjA4NzQsLTAuMDExIDAuNDI0NzAxLC0wLjAxIDAuNDI5NDEzLDAgMC40MzQxNjEsMTBlLTQgMC40NDExNDIsMC4wMSAwLjQ0NzIzNiwwLjAxIDAuNDU0ODksMC4wMTQgMC40NjM1LDAuMDE4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGg0NzAyIgogICAgICAgICBkPSJtIDIyLjE3NTQxOSwxMDI2LjE0ODcgMC4xNjcyNDQsLTAuMzgxOCAwLjE2NDA1NSwtMC4zNzEgMC4xNjEyMiwtMC4zNiAwLjE1NzY3OCwtMC4zNTE5IDAuMTU1OTA1LC0wLjM0MTYgMC4xNTMwNzEsLTAuMzMzNyAwLjE1MDU5LC0wLjMyNjQgMC4xNDg4MTksLTAuMzE5MiAwLjE0NzA0OCwtMC4zMTE5IDAuMTQ1NjMsLTAuMzA1IDAuMTQzNTAzLC0wLjMwMDUgMC4xNDQ1NjcsLTAuMjkyIDAuMTQxNzMzLC0wLjI5MDUgMC4xNDE3MzIsLTAuMjg0NSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDcwNCIKICAgICAgICAgZD0ibSAyNi4xNjE3NDUsMTAyOS4zNDkyIC0wLjI2ODU4MiwtMC4yNDM4IC0wLjI2OTI5MiwtMC4yNDAyIC0wLjI3MDM1NCwtMC4yMzY3IC0wLjI3MjgzNSwtMC4yMzUzIC0wLjI3MzE4OSwtMC4yMjg5IC0wLjI3NzQ0LC0wLjIyOTYgLTAuMjc4ODU5LC0wLjIyNTMgLTAuMjgzNDY0LC0wLjIyNjEgLTAuMjg3MDA4LC0wLjIyMzkgLTAuMjkwNTUxLC0wLjIyMTggLTAuMjk1NTEyLC0wLjIyMjIgLTAuMzAwODI3LC0wLjIyMjIgLTAuMzA2NDk2LC0wLjIyMjEgLTAuMzExODExLC0wLjIyMTgiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ3MDYiCiAgICAgICAgIGQ9Im0gMjYuMzQyNjY3LDEwMTcuODQ4IDAuNDczNDkyLC0wLjEwMTMgMC40NjU4MzgsLTAuMDk0IDAuNDU3MDg3LC0wLjA4OSAwLjQ0OTI1NiwtMC4wODQgMC40NDE4ODYsLTAuMDggMC40MzYzMjMsLTAuMDc0IDAuNDMwMTIyLC0wLjA3IDAuNDI1OTA1LC0wLjA2NSAwLjQyMTkzNywtMC4wNTkgMC40MTcxMTgsLTAuMDU3IDAuNDE1ODc5LC0wLjA1IDAuNDEyNDA1LC0wLjA0OCAwLjQxMTYyNiwtMC4wNDEgMC40MDkyODcsLTAuMDQiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ3MDgiCiAgICAgICAgIGQ9Im0gMjQuMjk3Nzg5LDEwMjEuNTc4NiAwLjE0MjQ0MSwtMC4yNzg1IDAuMTQwNjY5LC0wLjI3NzQgMC4xNDE3MzIsLTAuMjcyMiAwLjE0MDY3LC0wLjI3MTcgMC4xNDI0NDEsLTAuMjY2NSAwLjE0MjQ0LC0wLjI2NjQgMC4xNDM4NTksLTAuMjY0IDAuMTQ1Mjc1LC0wLjI2MTkgMC4xNDU5ODUsLTAuMjYyMiAwLjE0ODExLC0wLjI2MDQgMC4xNDk1MjcsLTAuMjYxMSAwLjE1MTMsLTAuMjYyMyAwLjE1MzQyNSwtMC4yNjMyIDAuMTU2OTY4LC0wLjI2MjYiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ3MTAiCiAgICAgICAgIGQ9Im0gNDEuNjE4OTYyLDEwMzcuNzc3OSAwLjE1NTU1MSwtMC4yNjQ3IDAuMTU0ODQzLC0wLjI2MTIgMC4xNTEyOTksLTAuMjYyMiAwLjE0OTUyOCwtMC4yNjExIDAuMTQ4NDY0LC0wLjI2MDggMC4xNDU5ODUsLTAuMjYyMiAwLjE0Mzg1OCwtMC4yNjQgMC4xNDM4NTgsLTAuMjYzOSAwLjE0MzUwNCwtMC4yNjQgMC4xNDEzNzgsLTAuMjY5IDAuMTQyMDg3LC0wLjI2OTMgMC4xNDE3MzIsLTAuMjcyMSAwLjE0MDY2OSwtMC4yNzc0IDAuMTQxMDI0LC0wLjI4MSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDcxMiIKICAgICAgICAgZD0ibSA0My42NjI1MjksMTAzNC4wNDUgMC4xNDE3MzMsLTAuMjg0NiAwLjE0Mjc5NSwtMC4yODggMC4xNDMxNDksLTAuMjk0NSAwLjE0NDkyMiwtMC4yOTgzIDAuMTQ1NjMsLTAuMzA1MSAwLjE0NzA0NywtMC4zMTE4IDAuMTQ4NDY0LC0wLjMxODkgMC4xNTA1OTEsLTAuMzI2NCAwLjE1MzA3MSwtMC4zMzM3IDAuMTU0NDg4LC0wLjM0MzcgMC4xNTkwOTQsLTAuMzQ5OCAwLjE2MDE1OCwtMC4zNjI0IDAuMTY0MDU1LC0wLjM3MDkgMC4xNjcyNDQsLTAuMzgyIgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgZD0ibSA0NS43ODQ4OTksMTAyOS40NzQ4IDAuNTA5MzUxLC01LjQ1ODUiCiAgICAgICAgIGlkPSIyNzEiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBkPSJtIDQxLjYxODk2MiwxMDM3Ljc3NzkgMC41MDkzNTEsLTUuNDU4NSIKICAgICAgICAgaWQ9IjI3MiIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDcxNiIKICAgICAgICAgZD0ibSAyOS41NTg3ODUsMTAzOC44NDMgMC40NjMyNTIsMC4wMTggMC40NTUxMzgsMC4wMTQgMC40NDgzMzQsMC4wMTEgMC40Mzk3OTYsMCAwLjQzNTcyLDAgMC40MjkyMDEsMCAwLjQyNDkxMywtMC4wMSAwLjQyMDY2MiwtMC4wMTEgMC40MTcyOTUsLTAuMDE0IDAuNDE0NDI1LC0wLjAxOCAwLjQxMTc2OCwtMC4wMjEgMC40MTA4ODIsLTAuMDI1IDAuNDA5NjQyLC0wLjAzMiAwLjQwOTY0MSwtMC4wMzUiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ3MTgiCiAgICAgICAgIGQ9Im0gMzUuNTQ5NDU0LDEwMzguNzI5IDAuNDEwNDIyLC0wLjAzOCAwLjQxMTgzOCwtMC4wNDEgMC40MTIxOTMsLTAuMDQ4IDAuNDE0NTMyLC0wLjA1MiAwLjQxODY3NywtMC4wNTUgMC40MjA2MjYsLTAuMDYyIDAuNDI1NjU3LC0wLjA2NSAwLjQzMTY4MSwtMC4wNjggMC40MzYzMjMsLTAuMDc0IDAuNDQxODg2LC0wLjA4IDAuNDQ5MjkxLC0wLjA4NCAwLjQ1NzA4NywtMC4wODkgMC40NjQyNDQsLTAuMDk2IDAuNDc1MDUxLC0wLjA5OSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGQ9Im0gMjkuNTU4Nzg1LDEwMzguODQzIDAuNTA5MzUsLTUuNDU4NSIKICAgICAgICAgaWQ9IjI3NSIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoNDcyMSIKICAgICAgICAgZD0ibSAyMS42NjYwNjgsMTAzMS42MDczIDAuMzAxNTM2LDAuMzQxMiAwLjI5NTg2NiwwLjMyOTUgMC4yOTE5NjgsMC4zMjI1IDAuMjg2MywwLjMxMDQgMC4yODQxNzMsMC4zMDU0IDAuMjc5OTIxLDAuMjk1NSAwLjI3NjczMiwwLjI4ODEgMC4yNzM4OTgsMC4yODA2IDAuMjcyODM1LDAuMjc1MyAwLjI3LDAuMjY3NSAwLjI3LDAuMjY0NCAwLjI2Nzg3NCwwLjI1NjUgMC4yNjg1ODIsMC4yNTMgMC4yNjc1MiwwLjI0NzciCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjAuNTk0OTE2Nzc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDQ3MjMiCiAgICAgICAgIGQ9Im0gMjUuNTczNTU2LDEwMzUuNjQ0OSAwLjI2ODU4MywwLjI0MzggMC4yNjkyOTEsMC4yNDAzIDAuMjcwNzA5LDAuMjM2MyAwLjI3MTc3MiwwLjIzMjggMC4yNzQyNTIsMC4yMzE0IDAuMjc3NDQxLDAuMjI5NiAwLjI3ODg1OCwwLjIyNTcgMC4yODI0MDIsMC4yMjM2IDAuMjg2NjUzLDAuMjIzOSAwLjI5MTk2OSwwLjIyNDMgMC4yOTU4NjYsMC4yMjIyIDAuMjk5NDA5LDAuMjIgMC4zMDY0OTYsMC4yMjIyIDAuMzExODExLDAuMjIxOCIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC41OTQ5MTY3NztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGQ9Im0gMjEuNjY2MDY4LDEwMzEuNjA3MyAwLjUwOTM1MSwtNS40NTg2IgogICAgICAgICBpZD0iMjc4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowLjU5NDkxNjc3O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ materials_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI5MDEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMCByMTUyOTkiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik1hdGVyaWFscy5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiCiAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvUGFydERlc2lnbl9Hcm9vdmUucG5nIgogICBpbmtzY2FwZTpleHBvcnQteGRwaT0iOTAiCiAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI5MCI+CiAgPGRlZnMKICAgICBpZD0iZGVmczI5MDMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTM3Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzOTM5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmEzMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZjAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwLjQzODYxOCIKICAgICAgICAgaWQ9InN0b3AzOTQxIiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM5NDMiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzdmNTEwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTA1Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzOTA3IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmEzMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZjAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwLjQ1NzcwMzY4IgogICAgICAgICBpZD0ic3RvcDM5MDkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzkxMSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojY2M4MDAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZhMzAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzkwMSIKICAgICAgICAgb2Zmc2V0PSIwLjQwNDc3MTgzIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZjAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzdmNTEwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzOTAzIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2NSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZhMzAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmYwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMC41MzE2MDIzOCIKICAgICAgICAgaWQ9InN0b3AzODY3IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjkiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzdmNTEwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODMwIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODMyIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmEzMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZjAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwLjY0MTM0MDE0IgogICAgICAgICBpZD0ic3RvcDM4MzQiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzgzNiIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojY2M4MDAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQyMzciPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQyMzkiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2Y4MmIzOTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wNDI0MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNTIwMDAxO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNTIiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDA5MGZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQwNTQiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wNDA2MCIKICAgICAgICAgb2Zmc2V0PSIwLjUiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmMGYxZjE7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDA0NmZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDQwNTYiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDQ0Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwOTBmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0MDQ2IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDYxYWZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDQwNDgiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMjczIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMjc1IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMyNzciCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2Y3ZjlmYTtzdG9wLW9wYWNpdHk6MC4wOTY0OTEyMzsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzc3Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMzc5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMzODEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMjc5NTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI5MDkiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTM2NzQiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA0NCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNTAiCiAgICAgICB4MT0iNDQuODU4MjE1IgogICAgICAgeTE9IjE0LjAxNjEyMyIKICAgICAgIHgyPSIzMy45Mjg2ODQiCiAgICAgICB5Mj0iMzMuMjE2MjUxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTc2ODAyMzcsMCwwLDAuOTYwMDM1MDgsMS40Njk0MzE5LDAuMTI3NjU3NjUpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0MDUyIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA1OCIKICAgICAgIHgxPSI0Mi4zNzM3MDciCiAgICAgICB5MT0iNS43OTc0OTg3IgogICAgICAgeDI9IjUyLjMyMzIxOSIKICAgICAgIHkyPSIyMi42NzU4MjEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45NzY4MDIzNywwLDAsMC45NjAwMzUwOCwxLjQ2OTQzMTksMC4xMjc2NTc2NSkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQwNTItMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNTgtNiIKICAgICAgIHgxPSI0Mi4zNzM3MDciCiAgICAgICB5MT0iNS43OTc0OTg3IgogICAgICAgeDI9IjUyLjMyMzIxOSIKICAgICAgIHkyPSIyMi42NzU4MjEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45NzY4MDIzNywwLDAsMC45NjAwMzUwOCwxLjQ2OTQzMTksMC4xMjc2NTc2NSkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNTItMSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmEzMDA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDA1NC01IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQwNjAtOCIKICAgICAgICAgb2Zmc2V0PSIwLjUiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojY2M4MDAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDQwNTYtNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMwLjI5NTIyNSwtMi45Mjg3MTQ3KSIKICAgICAgIHkyPSIyMi42NzU4MjEiCiAgICAgICB4Mj0iNTIuMzIzMjE5IgogICAgICAgeTE9IjUuNzk3NDk4NyIKICAgICAgIHgxPSI0Mi4zNzM3MDciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNzgiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0MDUyLTEiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQwNDQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODg1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTc2ODAyMzcsMCwwLDAuOTYwMDM1MDgsMTA3LjEwNTc5LC0yMi4yMzU5NzgpIgogICAgICAgeDE9IjQ0Ljg1ODIxNSIKICAgICAgIHkxPSIxNC4wMTYxMjMiCiAgICAgICB4Mj0iMzMuOTI4Njg0IgogICAgICAgeTI9IjMzLjIxNjI1MSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5MCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjk3NjgwMjM3LDAsMCwwLjk2MDAzNTA4LDEwNy4xMDU3OSwtMjIuMjM1OTc4KSIKICAgICAgIHgxPSI0Mi4zNzM3MDciCiAgICAgICB5MT0iNS43OTc0OTg3IgogICAgICAgeDI9IjUyLjMyMzIxOSIKICAgICAgIHkyPSIyMi42NzU4MjEiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQwNTIiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODkzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTc2ODAyMzcsMCwwLDAuOTYwMDM1MDgsMTA3LjEwNTc5LC0yMi4yMzU5NzgpIgogICAgICAgeDE9IjQyLjM3MzcwNyIKICAgICAgIHkxPSI1Ljc5NzQ5ODciCiAgICAgICB4Mj0iNTIuMzIzMjE5IgogICAgICAgeTI9IjIyLjY3NTgyMSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA1Mi0xLTAiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDA5MGZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQwNTQtNS01IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQwNjAtOC02IgogICAgICAgICBvZmZzZXQ9IjAuNSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2YwZjFmMTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDQ2ZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDA1Ni00LTYiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMjIuNjc1ODIxIgogICAgICAgeDI9IjUyLjMyMzIxOSIKICAgICAgIHkxPSI1Ljc5NzQ5ODciCiAgICAgICB4MT0iNDIuMzczNzA3IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjk3NjgwMjM3LDAsMCwwLjk2MDAzNTA4LC0zLjM2MzAxOTksLTE4LjMyMjk4MikiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5MTIiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0MDUyLTEtMCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA1Mi0xLTAtMCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDkwZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDA1NC01LTUtOSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3A0MDYwLTgtNi04IgogICAgICAgICBvZmZzZXQ9IjAuNSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2YwZjFmMTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDQ2ZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDA1Ni00LTYtNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIyMi42NzU4MjEiCiAgICAgICB4Mj0iNTIuMzIzMjE5IgogICAgICAgeTE9IjUuNzk3NDk4NyIKICAgICAgIHgxPSI0Mi4zNzM3MDciCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTc2ODAyMzcsMCwwLDAuOTYwMDM1MDgsMTI2LjczNzY5LC03MC4wOTI2ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTg1IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xLTAtMCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA0NC04Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwOTBmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0MDQ2LTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwNjFhZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDA0OC0xIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjMzLjIxNjI1MSIKICAgICAgIHgyPSIzMy45Mjg2ODQiCiAgICAgICB5MT0iMTQuMDE2MTIzIgogICAgICAgeDE9IjQ0Ljg1ODIxNSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45NzY4MDIzNywwLDAsMC45NjAwMzUwOCwtMjYuMjQ2NzcsLTguNDYwNzU5NSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNzIiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0MDQ0LTgiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQwNDQtOC00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg4NS02LTUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45NzY4MDIzNywwLDAsMC45NjAwMzUwOCw1OC4zMDczNjcsNTQuNjcxNDY5KSIKICAgICAgIHgxPSI0NC44NTgyMTUiCiAgICAgICB5MT0iMTQuMDE2MTIzIgogICAgICAgeDI9IjMzLjkyODY4NCIKICAgICAgIHkyPSIzMy4yMTYyNTEiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNDQtOC00Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwOTBmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0MDQ2LTItMCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzA2MWFmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3A0MDQ4LTEtOSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzMy4yMTYyNTEiCiAgICAgICB4Mj0iMzMuOTI4Njg0IgogICAgICAgeTE9IjE0LjAxNjEyMyIKICAgICAgIHgxPSI0NC44NTgyMTUiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTc2ODAyMzcsMCwwLDAuOTYwMDM1MDgsMTQ1LjQxMjk5LDk0Ljc4MjIyMSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQxNjMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0MDQ0LTgtNCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA1Mi0xLTAtOSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDkwZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDA1NC01LTUtMiIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3A0MDYwLTgtNi01IgogICAgICAgICBvZmZzZXQ9IjAuNSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2YwZjFmMTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDQ2ZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDA1Ni00LTYtNSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIyMi42NzU4MjEiCiAgICAgICB4Mj0iNTIuMzIzMjE5IgogICAgICAgeTE9IjUuNzk3NDk4NyIKICAgICAgIHgxPSI0Mi4zNzM3MDciCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTc2ODAyMzcsMCwwLDAuOTYwMDM1MDgsMTI2LjczNzY5LC03MC4wOTI2ODcpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MjE3IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xLTAtOSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xLTA1IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5MC0zLTUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC41NTM3MDYzOCwwLDAsMC41NTM2NDMzMSw3OC43OTQ0MzksLTI2LjE3OTg3OCkiCiAgICAgICB4MT0iNDIuMzczNzA3IgogICAgICAgeTE9IjUuNzk3NDk4NyIKICAgICAgIHgyPSI1Mi4zMjMyMTkiCiAgICAgICB5Mj0iMjIuNjc1ODIxIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDUyLTEtMDUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZhMzAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQwNTQtNS0xIiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQwNjAtOC04IgogICAgICAgICBvZmZzZXQ9IjAuNSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmYwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjYzgwMDA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDA1Ni00LTEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iODYuMTkxOTc4IgogICAgICAgeDI9Ii01OC45MTA4MTIiCiAgICAgICB5MT0iNzEuODY4MDI3IgogICAgICAgeDE9Ii02OS4yMDIyNzEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNTUzNzA2MzgsMCwwLDAuNTUzNjQzMzEsNjMuNzgxMjI2LC05LjM3ODA3MjIpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDU1IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzkwNSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA1MyIKICAgICAgIHgxPSItODUuNjQwNjMzIgogICAgICAgeTE9IjU0LjYwNDQyNCIKICAgICAgIHgyPSItMzcuNjMyMTI2IgogICAgICAgeTI9IjU0LjYwNDQyNCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0MDUyLTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDU3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iLTg1LjY0MDYzMyIKICAgICAgIHkxPSI1NC42MDQ0MjQiCiAgICAgICB4Mj0iLTM3LjYzMjEyNiIKICAgICAgIHkyPSI1NC42MDQ0MjQiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQwNTItMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNjQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSItODUuNjQwNjMzIgogICAgICAgeTE9IjU0LjYwNDQyNCIKICAgICAgIHgyPSItMzcuNjMyMTI2IgogICAgICAgeTI9IjU0LjYwNDQyNCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDA2NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9Ii04NS42NDA2MzMiCiAgICAgICB5MT0iNTQuNjA0NDI0IgogICAgICAgeDI9Ii0zNy42MzIxMjYiCiAgICAgICB5Mj0iNTQuNjA0NDI0IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODMwIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzgyOCIKICAgICAgIHgxPSI0OC43Mzg4MzQiCiAgICAgICB5MT0iNi44OTk3NTQiCiAgICAgICB4Mj0iNTYuOTQwNzAxIgogICAgICAgeTI9IjE5LjI0Mzg5MyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMjIuNjc1ODIxIgogICAgICAgeDI9IjUyLjMyMzIxOSIKICAgICAgIHkxPSI1Ljc5NzQ5ODciCiAgICAgICB4MT0iNDIuMzczNzA3IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjU1MzcwNjM4LDAsMCwwLjU1MzY0MzMxLDYzLjc4MTIyNiwtOS4zNzgwNzIyKSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA1NS0zIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA1Mi0xLTA1LTUiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwNTItMS0wNS01Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmYTMwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0MDU0LTUtMS04IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQwNjAtOC04LTAiCiAgICAgICAgIG9mZnNldD0iMC41IgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZjAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzdmNTEwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3A0MDU2LTQtMS0xIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjIyLjY3NTgyMSIKICAgICAgIHgyPSI1Mi4zMjMyMTkiCiAgICAgICB5MT0iNS43OTc0OTg3IgogICAgICAgeDE9IjQyLjM3MzcwNyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuNDQ3NjE3MTIsLTAuMDM5NzQ2MjQsMC4wMzk2ODU3MiwtMC40NDgxOTc2OSwtNDQuMzQ3NjIyLDkwLjg0MjQzOSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNzQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODYzIgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk3IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NSIKICAgICAgIHgxPSItMTMuMDU0MjA5IgogICAgICAgeTE9IjU1LjMxMjE5NSIKICAgICAgIHgyPSItMTIuOTE1OTQiCiAgICAgICB5Mj0iNDEuNTMwNzU0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM5MzciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTM1IgogICAgICAgeDE9IjE3LjYwMzY2OCIKICAgICAgIHkxPSI0OC40MzgzMTYiCiAgICAgICB4Mj0iMTMuODc0NjE3IgogICAgICAgeTI9IjQzLjA5MDI3NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzOTM3IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg4MiIKICAgICAgIHgxPSItMTMuMjIxNTUiCiAgICAgICB5MT0iNTQuODEzMjQ4IgogICAgICAgeDI9Ii0xMy41MDgwOTgiCiAgICAgICB5Mj0iNDAuOTQ1NTcyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4wNDM0NzgzLDAsMCwxLjA0MzQ3ODMsLTQxLjk1MDE3NiwtMTE0LjU2OTQ3KSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDEyNTEyIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50Mjc4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBjeD0iNTUiCiAgICAgICBjeT0iMTI1IgogICAgICAgZng9IjU1IgogICAgICAgZnk9IjEyNSIKICAgICAgIHI9IjE0LjM3NSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MTI1MTIiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxLjAwMDAwMDA7IgogICAgICAgICBvZmZzZXQ9IjAuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AxMjUxMyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjUyMDtzdG9wLW9wYWNpdHk6MC44OTEwODkwODsiCiAgICAgICAgIG9mZnNldD0iMC41MDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AxMjUxNyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjMwMDtzdG9wLW9wYWNpdHk6MC4wMDAwMDAwOyIKICAgICAgICAgb2Zmc2V0PSIxLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wMTI1MTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iOS42ODc1IgogICAgIGlua3NjYXBlOmN4PSIzMiIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNTM2IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgwMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1ub2Rlcz0idHJ1ZSIKICAgICBpbmtzY2FwZTpvYmplY3QtcGF0aHM9InRydWUiCiAgICAgaW5rc2NhcGU6b2JqZWN0LW5vZGVzPSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtaW50ZXJzZWN0aW9uLXBhdGhzPSJ0cnVlIiAvPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI5MDYiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIj4KICAgIDxnCiAgICAgICBpZD0iZzM4ODQiPgogICAgICA8cGF0aAogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjA4OTUyMjYsLTAuNzI4OTE3MDUsMC43MjcyNzIwNSwxLjA5MTk4NywyMy41NzYyNTEsLTM1Ljc5NzQ2OSkiCiAgICAgICAgIGQ9Im0gLTEuNTcwMDcyMiw0OC4xMjIyMzggYSAxMS41NDM1NDUsMTYuNzc3NzE2IDAgMSAxIC0yMy4wODcwODk4LDAgMTEuNTQzNTQ1LDE2Ljc3NzcxNiAwIDEgMSAyMy4wODcwODk4LDAgeiIKICAgICAgICAgc29kaXBvZGk6cnk9IjE2Ljc3NzcxNiIKICAgICAgICAgc29kaXBvZGk6cng9IjExLjU0MzU0NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjQ4LjEyMjIzOCIKICAgICAgICAgc29kaXBvZGk6Y3g9Ii0xMy4xMTM2MTciCiAgICAgICAgIGlkPSJwYXRoMzgxNS0yLTMiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmM3MDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjEuNTI1MDQ1MzM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIiAvPgogICAgICA8cGF0aAogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjEzNTc3NTEsLTAuNzQ2Nzk3NzEsMC43NTgxNDYyNiwxLjExODc3MzksNi44NzkwMTAxLC0yNi43NTgzMTIpIgogICAgICAgICBkPSJtIC0xLjU3MDA3MjIsNDguMTIyMjM4IGEgMTEuNTQzNTQ1LDE2Ljc3NzcxNiAwIDEgMSAtMjMuMDg3MDg5OCwwIDExLjU0MzU0NSwxNi43Nzc3MTYgMCAxIDEgMjMuMDg3MDg5OCwwIHoiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSIxNi43Nzc3MTYiCiAgICAgICAgIHNvZGlwb2RpOnJ4PSIxMS41NDM1NDUiCiAgICAgICAgIHNvZGlwb2RpOmN5PSI0OC4xMjIyMzgiCiAgICAgICAgIHNvZGlwb2RpOmN4PSItMTMuMTEzNjE3IgogICAgICAgICBpZD0icGF0aDM4MTUtMi0zLTgiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmM3MDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjEuNDc1NjgwMjc7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzY2NzY2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGQ9Im0gMTUuNzQ4NjIzLDE4LjEwMjM0MSBjIDYuMzI2NDU3LC0zLjkxNjU2MSAxNy42Nzc2NDIsLTEuODU3NTE5IDI1Ljk2MTQ0MywxMC4zNDM5NzMgNy41MzE0NDYsMTEuMDkzMzIzIDYuMjMzNDg3LDIyLjMzNTA5NyAtMC41MjE1MTgsMjcuMTk2OTY4IEwgNTYuNDg4NTkxLDQ0LjYzMTE3NiBDIDYzLjcwMzk0NywzOS4xNDk4NjUgNjMuMDgyMTY5LDI3LjkyMTc1OCA1Ni45ODM1NjQsMTguMDc3MzAxIDUwLjEwMTA0LDYuOTY3NDI2MSAzOC42MjYxNjEsNC4yMTIzNDU2IDMyLjA4NDY2NCw3Ljk4OTA4MDcgeiIKICAgICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzgyOCk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDM4NDYtMyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC44MzEyNDkzMywtMC41MzU1MTAxNywwLjU1NDg3MDkxLDAuODAyMjQ1MSwxMi41MTUxODcsLTguNjk5MzEyOCkiCiAgICAgICAgIGQ9Im0gLTIuNDI4MDYzNCw0OC4xMjIyMzggYSAxMC42ODU1NTQsMTYuNzc3NzE2IDAgMSAxIC0yMS4zNzExMDY2LDAgMTAuNjg1NTU0LDE2Ljc3NzcxNiAwIDEgMSAyMS4zNzExMDY2LDAgeiIKICAgICAgICAgc29kaXBvZGk6cnk9IjE2Ljc3NzcxNiIKICAgICAgICAgc29kaXBvZGk6cng9IjEwLjY4NTU1NCIKICAgICAgICAgc29kaXBvZGk6Y3k9IjQ4LjEyMjIzOCIKICAgICAgICAgc29kaXBvZGk6Y3g9Ii0xMy4xMTM2MTciCiAgICAgICAgIGlkPSJwYXRoMzgxNSIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmYzcwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzVlMzgwMDtzdHJva2Utd2lkdGg6Mi4wMzY5OTcwODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGQ9Ik0gNy4wNjYzOTUxLDMxLjM5NDU1MiBDIDE3LjA2NjUyNywyNC43NTY5MjYgMzYuMzA2MTA4LDUxLjM3NDg0MyAyNS42OTU5NzYsNTkuMzAzNDM0IGwgMTEuOTI5NjIyLC04LjkxNDYwMiBjIDExLjY0MDc0NywtOC43NzY2MTYgLTcuMDU3MjgsLTM0LjIxMjEzIC0xOC42MTg5MzMsLTI2LjkxOTY4IHoiCiAgICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMwNTUpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojNWUzODAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgICAgaWQ9InBhdGgzODQ2LTMtMyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC44MzE3MjQ3MiwtMC41NTUxODgyNCwwLjU1NTE4ODI0LDAuODMxNzI0NzIsMC41NzEyMDQxOSwtMS45NTU5ODgzKSIKICAgICAgICAgZD0ibSAtMi4yODQxMjE1LDQ4LjEyMjIzOCBhIDEwLjgyOTQ5NSwxNi43Nzc3MTYgMCAxIDEgLTIxLjY1ODk5MDUsMCAxMC44Mjk0OTUsMTYuNzc3NzE2IDAgMSAxIDIxLjY1ODk5MDUsMCB6IgogICAgICAgICBzb2RpcG9kaTpyeT0iMTYuNzc3NzE2IgogICAgICAgICBzb2RpcG9kaTpyeD0iMTAuODI5NDk1IgogICAgICAgICBzb2RpcG9kaTpjeT0iNDguMTIyMjM4IgogICAgICAgICBzb2RpcG9kaTpjeD0iLTEzLjExMzYxNyIKICAgICAgICAgaWQ9InBhdGgzODE1LTIiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmM3MDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiM1ZTM4MDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIiAvPgogICAgICA8cGF0aAogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjM3OTIzOTEsLTAuMjUzMTQ3NTYsMC4yNTMxNDc1NiwwLjM3OTIzOTEsOC44ODQ0NjE0LDI0LjMwNTQ4NikiCiAgICAgICAgIGQ9Im0gLTIuMjg0MTIxNSw0OC4xMjIyMzggYSAxMC44Mjk0OTUsMTYuNzc3NzE2IDAgMSAxIC0yMS42NTg5OTA1LDAgMTAuODI5NDk1LDE2Ljc3NzcxNiAwIDEgMSAyMS42NTg5OTA1LDAgeiIKICAgICAgICAgc29kaXBvZGk6cnk9IjE2Ljc3NzcxNiIKICAgICAgICAgc29kaXBvZGk6cng9IjEwLjgyOTQ5NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjQ4LjEyMjIzOCIKICAgICAgICAgc29kaXBvZGk6Y3g9Ii0xMy4xMTM2MTciCiAgICAgICAgIGlkPSJwYXRoMzgxNS0yLTIiCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzODgyKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzVlMzgwMDtzdHJva2Utd2lkdGg6NC4zODYyODE0OTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiIC8+CiAgICA8L2c+CiAgICA8Y2lyY2xlCiAgICAgICByPSIxNS4wMDAwMDEiCiAgICAgICBjeT0iMTUuODY1MzIiCiAgICAgICBjeD0iMTUuNDQxMTMiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmJsb2NrO3Zpc2liaWxpdHk6dmlzaWJsZTtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQyNzgpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxLjMwNDM0ODExO21hcmtlcjpub25lIgogICAgICAgaWQ9InBhdGgxMjUxMSIKICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUvamltbWFjL3hpbWlhbl9hcnQvaWNvbnMvbmF1dGlsdXMvc3VzZTkzL3N0b2NrX25ldy0xNi5wbmciCiAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iMzMuODUyMjAzIgogICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjMzLjg1MjIwMyIgLz4KICA8L2c+Cjwvc3ZnPgo= """ fusion_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI1NjgiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMiAoNWMzZTgwZCwgMjAxNy0wOC0wNikiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImZ1c2lvbi5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyNTcwIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzgwNSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzNDY1YTQ7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzgwNyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MDsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODA5IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NCI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2NiIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzU5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzU5NSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzN2RjYTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNTk3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTc2IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iMjYuMjgxNzQ0IgogICAgICAgZng9IjQ2LjUzNDEzNCIKICAgICAgIGN5PSIyNi4yODE3NDQiCiAgICAgICBjeD0iNDYuNTM0MTM0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDkwIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzE0MyIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4xNDg2MDg4LDEuODQ3NzYxNywtMy4wNTY2NzMsMS45MDAwOTQsNzMuMjU3NzQ1LC0xMTIuMTAxNikiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNDMiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzE0NSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMxNDciIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iMjUuMTQ5MDQyIgogICAgICAgZng9IjQ4LjY0NTgzNiIKICAgICAgIGN5PSIyNS4xNDkwNDIiCiAgICAgICBjeD0iNDguNjQ1ODM2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjYzMjk0MDI5LDAuOTMwODkxMzIsLTAuODI2OTUzNzYsMC41NjIyNzAwMiwzOC42NTMwMjIsLTM0LjI3NTQ5NikiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMxNjUiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzLTciCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNDMtNyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTQ1LTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMTQ3LTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzAyMyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjE3NTE4MjMsMS44OTA1MTA0LC0zLjEyNzM5MDQsMS45NDQwNTM0LDYxLjUwNzE4MywtMTI1LjA2NjM3KSIKICAgICAgIGN4PSI0Ni41MzQxMzQiCiAgICAgICBjeT0iMjYuMjgxNzQ0IgogICAgICAgZng9IjQ2LjUzNDEzNCIKICAgICAgIGZ5PSIyNi4yODE3NDQiCiAgICAgICByPSIxOS41NzE0MjgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4MDUiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzODExIgogICAgICAgY3g9IjIyLjYyNTU2MyIKICAgICAgIGN5PSIzMi44MjY2NjQiCiAgICAgICBmeD0iMjIuNjI1NTYzIgogICAgICAgZnk9IjMyLjgyNjY2NCIKICAgICAgIHI9IjEzIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjIyMTg4MDA0LC0wLjE2NjQxMDA0LDAuNjkyMzA3NjYsMC45MjMwNzY4OCw1LjAxMTQ4MzUsNS42OTUxMDU2KSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzLTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ4NjMiCiAgICAgICB4MT0iMS44MjYwOCIKICAgICAgIHkxPSIzMi4xNzM5MDgiCiAgICAgICB4Mj0iNjEuODI2MDgiCiAgICAgICB5Mj0iMzIuMTczOTA4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0LjcyMzIxNjEiCiAgICAgaW5rc2NhcGU6Y3g9Ii05MS42Nzk2NTEiCiAgICAgaW5rc2NhcGU6Y3k9IjE4LjA5NjAzNCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTM2MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMjk5NiIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyNTczIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZSAvPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W3dtYXllcl08L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnRpdGxlPlBhcnRfRnVzZTwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmRhdGU+MjAxMS0xMC0xMDwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9QYXJ0L0d1aS9SZXNvdXJjZXMvaWNvbnMvUGFydF88L2RjOmlkZW50aWZpZXI+CiAgICAgICAgPGRjOnJpZ2h0cz4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQgTEdQTDIrPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpyaWdodHM+CiAgICAgICAgPGNjOmxpY2Vuc2U+aHR0cHM6Ly93d3cuZ251Lm9yZy9jb3B5bGVmdC9sZXNzZXIuaHRtbDwvY2M6bGljZW5zZT4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIj4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjY2MiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDg2Myk7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDEuODI2MDgsMi4xNzM5MDcgdiA2MC4wMDAwMDEgaCA2MCBWIDIuMTczOTA3IFogTSA2LDYgSCA1OCBWIDU4IEggNiBaIgogICAgICAgaWQ9InJlY3QyMjMzIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQzMDIzKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45OTc4Mjg3MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7bWFya2VyOm5vbmU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gNDEsNiBDIDMyLjIyMzk4Niw2IDI0LjgzOTUyMywxMS45NDU0NDEgMjIuNjU2MjUsMjAuMDMxMjUgMTIuMzIzMzA1LDIwLjIxNzQwNiA0LDI4LjYyMjU5NCA0LDM5IDQsNDkuNDk0MDEgMTIuNTA1OTksNTggMjMsNTggMzEuNzc2MDE0LDU4IDM5LjE2MDQ3Nyw1Mi4wNTQ1NTkgNDEuMzQzNzUsNDMuOTY4NzUgNTEuNjc2Njk1LDQzLjc4MjU5NCA2MCwzNS4zNzc0MDYgNjAsMjUgNjAsMTQuNTA1OTkgNTEuNDk0MDEsNiA0MSw2IFoiCiAgICAgICBpZD0icGF0aDM1NTAtMyIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6dXJsKCNyYWRpYWxHcmFkaWVudDM4MTEpO3N0cm9rZS13aWR0aDoxMDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0ibSAyMi43NTc3OTYsMjMuMjMxNTIxIGMgNS43OTkzNywyLjYxNTQ2OSAxMC4yOTYyNTksNy42Mzg5MDMgMTYsMjAiCiAgICAgICBpZD0icGF0aDMwMjciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7ZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxLjk5NzgyODY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJNIDQxLDggQyAzMi42MTA5NDMsOCAyNS42NzEyMTksMTQuMDcxMDc2IDI0LjI4MTI1LDIyLjA2MjUgMjMuODU5NzA5LDIyLjAzMTE5OSAyMy40Mjk1NDEsMjIgMjMsMjIgMTMuNjEwNTU4LDIyIDYsMjkuNjEwNTYgNiwzOSA2LDQ4LjM4OTQ0IDEzLjYxMDU1OCw1NiAyMyw1NiAzMS4zODkwNTcsNTYgMzguMzI4NzgxLDQ5LjkyODkyNCAzOS43MTg3NSw0MS45Mzc1IDQwLjE0MDI5MSw0MS45Njg4MDEgNDAuNTcwNDU5LDQyIDQxLDQyIDUwLjM4OTQ0Miw0MiA1OCwzNC4zODk0NCA1OCwyNSA1OCwxNS42MTA1NiA1MC4zODk0NDIsOCA0MSw4IFoiCiAgICAgICBpZD0icGF0aDM1NTAtMy0xIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjYyIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBpZD0icGF0aDMwNDIiCiAgICAgICBkPSJtIDQ5LjE5OTk4OCw5Ni43OTAyODcgNDcuNjIyNjk0LDAuMDQxOTIgLTAuMDgzODQsLTQ3LjA1NTEwOSIKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjIuNjA4Njk1NzU7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgPC9nPgo8L3N2Zz4K """ #new iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAPdEVYdEF1dGhvcgBbd21heWVyXauF7RsAAAAYdEVYdENyZWF0aW9uIFRpbWUAMjAxMS0xMC0xMEUb11UAABFMSURBVHic5Zt5kFxHfcc//a45dvbS7upYXSvJEkLYJLHlg8NBGCLANsRgMMQJBEjA2MQuCJAQKoQQSIWCkAKDw1EBAmXu4hDBBiJjSKi4JGwBNodkSawlI8s6VsfuzuzMvPe6f/mj5828N8dKPiQo6KqZ6dfT1/fbv1//fn08+B0P6i2f+vFarfgqxmAEjBHECCIGIwYREC0YBIxBC4g2CIIRQYzNY4yxzyKI0QiNciKN8hoRwAhabBtGNDTaNEgrv7H1ixG0ERCxfTG2fttHY+Mitk4jCKk8zTYMJqmj8Z82BoXave/2t17txbh5JfpcQQHSoCWJNiKNH/uvkM5KkpYuhgKRVhYFiP03UwzVSpG2lExXpK29TGOZrjafpWvWbJ2Al0rfoxy52sSNLErjKCCEsFFMOQYdJbUolFKoOCZKiiiFGypCYpvFARUpwkZ9KFCxwqAa+TUqtIU1CpwQFYFu1I9SqDhCATp5DsEltH1yFCpSKEJi1UAWKxwUkYpsG6FCoQDQTgixWgt8JQHdJEAh9X971caf8lseVl/+nsyz1y3TjR+/WySj0y3dTHSrpWempZc90hJdzNbVnmaoHD/M9KE9TB/ZR1SdIazOEtfnEDE4XoCf7yfoW0B+cDHFkeUU+pdgoFVnY57I9rE1L+zZ8hbVjrUrAWczRLUKB3fdxaHd2wmrsz3z6bCKDqvUZo4w8/AuAPx8PwPjGxhc9gc4udKjan9eAv79+ks6GHu8wtDQxFDoq39W8GogDzCydDUbLtrM0nVPptA3iOf7KMdFKYWIIY4i5manObx/Fzu3b+XQAz/n2OR2jk1ur4vIJ91c9PbZhx461t7Wuqv+tX0KbYZfiwQUR1e9IFJ8RMG4chw2PvulPOlpl5Mr9PUso5SDH+QYHFnI4MhC1p3/h9Sqc9y/43v88PZP50wcX2/C4JrS6MRflaf2feF0+3K2CXCLY6s+qOD1AE962hVccsXL8bygmUESfwQBQ9MXAUFhLY/jKjzHJV8o8ntPv4INF2/mnm9/jvt+sGVElPp8aXT1M8pTwzfBjqhHP5rh7BFwzjm54rS+VcGLc/k+Ln/NP7Jw+RqAptNjTEpSxaarxGo3SBFjkFioSYTnOQS+j+/7POX5f87aC57JNz/299Sr5df1jR1bUfHHX8LBg3Pzdcs5k5hTwe2b1l9X8OLS8BjXvu3jTfDGCEZbgKrhW1gfAU41AUWRplypUquFiMDo+Ar+5O8+xuDYUkBd3hflvgSb5h3ks0JA3+iqfwGeOzg6zjVvupkgXwAEra0ZVKoNfC/okv5IM16rR8xWqhgj5PIFXvyG9zM4Og5wRXF0383z9e2ME1AaW3UNijf7QY4X3fQ+/CCHCOjYAC3g9pMddYtPUrgly0HKtdaxZma2gtYGzw+46sb3kSv0oZS6vjq1p2f/zigBQ0MTQwK3AOrK172bIF9oLEZ0w5W2ok5Kz+3HxruNdisTTac/iWojTM9W0MaQLxS4/DXvAuDkL/8XE9fOPgGRp94GjG546vNYuMzqvNY6s2hpAU5/ktFtH/0u0tBGitHCzPQciLBw2QTnXvrHmLjGzIM/PLsE5JesWoniJqUUlzzv5Q3wiUlrrfAyoNPgM2nt0jH/f1GsKc/VAbhw88twPI/q4Z0URpYtPWsEOJG8Eshd8Jxr8XN5q/fapEYtO/JGukjBaYDtJR3lSg2tDUEux/mb/xQRjeN4rz9rBCilrgXYcNFmAGKtW6OOdII3kll8dQOfYS8JXeaHpMxspQrAEzdelsB9hegwg/nxcIRUcWzl7yvlPAvkfEQ9EVgIjA+MLqFQ6re6GRu7T9LsvJ3v0xsiFkM7SelJsTHSp5COJFKdCxnoL1Is9bNwxXqOPLhr6ez+bRtKqy997AQUR5ePO45/nSCvRFjR2pZphfOe/nygsV2GoAQ77Uvb3lCye5SkJ6DoBEqXtK5xQMRQr0UUCgFrNz6bIw/uIpw7dFG6j4+YgIGBZQvinP9OBX8pInmAxRPrWX/xc1i04gkUB4bw/ADHaUlaPdaEUUzgup1KlzJlrZFvkdKhBl3Sus4JjfRaPaRQCFg8sd4ORlTd8KgJKI2tukbDhxWMua7Lxue+gvUbLyPf13sVBxDWIqI4RjuGIOfhqJSkSKLR7cCTtE4L0d16dJeYMLTbc30Dg7Y9HU08CgI2ecWx/R+Qxiru3EtfwEXPuRY/sKs4EYjjmDg2zd1hGxTaaMJ6hNYG5SiMaALfw3XdFPhuwNtFPlGLFFGnAC8ihJEGIMjlG3WZhY+MgGXLCn31/V8CrgzyJa687t2MLV1hydSGWj0iiiJANb26ZICVws7uCHFjFRdrhY6FfA4ct6UPLbBZAiQhp8u8QJtkdFcN63I3VVIoPgICNnl99f1fBq4YHB3nhTe+l1yhCAJz1Tq1WtjcDbZjmf61rTmuQikHEW1NYQxxbM8GCvkA11EkApMe8eS3U+zb5om2tG6T5nxhXgJKCx/8sIgFf/Ub3o8f5Ii1YXa2ita6AdxiVso2aLevpPEMoHAdB6XAiBDHlghjNIiQz/nN0UlLQTdr0HpOAeySliZCNeo2xkoCigrQ3IHpScDkfXchItcFhRJX3fhe/CBHFGumZyqISQFFIaqhzC3UGSlwXYXrOjjKQUxMbHRzD0AakpA5rugm/vOYR7qkJb+B6wJQr1nXWCnnKDA8LwG1uVm23fZJAK587bvIF4rE2nDyZMWyrmyjGRJIn/N0HB3huS6uG6OUQmvTmDAFxLrHhVwyoaaAtxGQmfzaRrzTYti4H1gCytPHbUdcfz+wbl4CfrT189Tnypx36QsZW7oSEeHkybI9U1OgRDVOuqQ16s3jLyCRiDQBntP8RLFDPbSWAXwEBwUEgd8FeA8S2kW+h3ks5i2xh/bbrXTHL/wc+KOeBBQWrFm+50ffx/U8Nm5+KQCz5RpRrBvjqkBZr06UQkn3UbcSkfUMfc8n9DVebIjimDCMmxKlHJvf9ZyWeWzX//nMYxeLoRT09eVBYM/ddwAQFBdn1sUdBDiuvF7HMRdd8WqCXEAcayqNRUULcEu/Sc75mkQkZCT/p3I6isDz0Z4h8lziSBOGMcqxptNBkVM+jlJdgUM3sW+XglbaQH8fSilmZ6aZOrAbhP39Ky/5RVo42wlwgT8DeMIFmwAol2uYhqQrkRYJiSpkZD0LvJsR8j2X2PfwY432DbGuE4YxrrLmUjmKIPCawLMj3wZ6HvMIMDxkPdSd279re6f4rHIDk+5zhoDCwjUXI2bpookNFEslxAiVar3ZaGuWTwFN635XyKnQ+NvzXILAQ2uD1oZ6GBFGMcp1cB0rUZ7rttqdRwo6dB8bHxku4fse1WqVe+/8PMpxMbp+CzCS7lJmaaLEbAJYu/FZAMzVQkzjAkTSSKsz3WbdHp/mpQu75ncche955HyPILBkCNadjiJNFMXNdpMySV3GzNeWzR8EHgsWWN9/++23YrSmuGgDc1O/Otg+Jm1rM7kAYPFKu3Kq1+3NgE5HoyWSzY6l0w2tX9Mtj+C5Dp7vEgR+Y23gIMY6SmGTBOlaNrN7ZLLPjuOwbMkIjoKHHtjL/du/heMXGFh+Ed1CRgUUah20Vk5RFGfsfFrkFZJc+miWzsh5ShukPdaY5X3fw2iD8a3omtigRXC03dezc0IifbZgS//pMI+u47B86Si+71GeneG/P/kOAIbXbEJ5ua4EtK/OlwD4OZs5jOJUA9LsSKfYdYpq8tyx19c2Yp7v4fsuvufieC23NdaGOI57SlD7cy7wmFgxRj7nU6vW2HLLWwnrVYCb8yOru4KHdiug6EfAUUlHWja3Yet6j3jXp86QXqAkUd91MZ4trJXdNhdt0I5CNZyvnu4xipHhEiMjQzgKyrMzbLnlrZSPH0IJW8pTD/w1cNPpEdCjs4n4zQf8dEILu6TitibPdTDigqZ1M8wIWmtcx2lzjMBRMDjQx8hwP75vYRyY3MPWT72DsF5Dob5WLplrmbJXjnqFLAHCLDAiRlCuNUeSnNimbnrRiKoODtoPtrqBzxKQAFMKfNchbhyHK8Bz3ObmitNwooLAp1gMKJXyTUmtzlXZdttn2H33dxpVqg+Wj06+maPzg+8kAB4GRuq1GoW+Ir7nUq9H9B719kPMTqlI7HQnGWnpSsQZHNfDRVMo+EwsX9S75wIz0yfZue0O7r3zi4hogENGmRuqR/Z/rXfBbMgQIMhuhTq3PHuCQl+RwPeo1SIgc4uQ9Jb2fEfYkv3qSEviiRQkGRxHUSoWWmXEqkK9VmP25DEO7dvFnnvu4PjBXyZZ5lB81A/lXSdP7j95uuA7CFA494C86NC++xlbvJR8Icf0zFzbxclOQD0Fv13sU2lp0Gn9ThytYtFaonvu3MKOb3+6a+cVTBqRTyvFxytH9h06PcjZkCHAOOZ7jlHs2fFdzrv4MvoKrU3PDkQp5B2C32V+7ARsvzJS0LTninw+BwK7tn27UYN6COQQwk5QP8Zla/nw5GO+15ghoHp43w/7xlb96uj+ncvL5VlK/f2USgVmZuc6RrpnaNf3TEQyz5KKpKVgaLCEo+Dk1BSVk4cB9lSOTq7jDIR2R8iAfBZg5912BbVguGQ73s35MV0+Xd3j5JlOZygVNyIohAXDAwDcd9e3Eoq668AZIACjvVtc1+cnWz9HvVYj8D2GBkopj6/dE+y+KDHpfKbb6W8bScaWG10wiOc5VMpldv7f1wGqot2zR0D1+N4Day94JkbHbLv9cwCMjg4SBF5PEL3dXzrBGzpAJ+UK+YCx0UFA2HbbZxpzhnygenzvgbNGAMAFm19KrtDPrm3f5OD+SRwFy8ZHcF2nq39vOoD3GPleom8Ez3NZvmzUTu07f8beHXcAHMlp7z3d+nhGCcgV+nnK818FwHc+8Q4q5TKB77Fi6ah1S+cD3QGw25xAppzvuqyeWEzge8xMT3Pnre8GEKXUDceP75056wQArDrvqShRHw1rFbbc8rfUajXy+YBVKxeRy/ltcwLzj7yQXdun4oV8wJrVS8gFHuWZWbZ86C3oKALkPeUjk1/p1b8zTgCAvW4qt88ee5ivffBNVhICj9UrFzM6Ymfqdstgukx43TY2QLFobIhz1owTBHbkv3rzG5mbmQL4ZuXovrefafBwyisyO6KKH74EuG3m2MN86b3Xc+CBvTiOYvHCIdauGWd4qNQ8FjNGOqShBd6mKxRjIwNseMIyFi8aBoRf7vwZX37f9VRnjgPqC5Wjxavh1AuZxyOc+nT44MG5CpuuKo7uuzmsVa6/7SN/w7qLn8cll7+cQiHPsvERxhcPMzM7R7lcY64WEoaxvQ6nFIHn4vkufYUc/aWCXcU1zusq5TJ3/dd/MvnjO5PWbq4cnXwjYM4Y4rZwmvcDvh/PTXFDaWzifwT14d3bvzW6956tnHfZy9hw4bMYGBxkaLDE0OCpX1oQhJPHT3DfD77Bzru+kbjHB0TU6+amJm97TGgeRXhEN0TKR/d9sX/p0jt0GPyT0fFf3Lv11ty9W29ldNk61px/GYsn1lMaHCZfKNqzfxHCMCKs1ShPn+BXe37C5I/uZGaqadbngP8IYvUPJ05MTj/u6E4jzEvADR/ZJplZPfXO0NzMcfbs+C4P/uIupg7sticvpxlyfYOMr38aS9ZdWHSD4k1i5KbWez6mi8XonpZ9/6gzzU66prWp80gJmC8U+oc59xlXs+HpV3Hi0D6O7N/JiUP7KJ84TLU8jY6qKOUSFPrJFfoJSoMsWLKGocXnUBhalHlp6tcZuhLwoddeeEbeFXrgTFT6GMPZemHiNzY0JUBQa9/wiR0//R14dzhHak5Iq0AOOBdo2wKT5k9zA6PjAohki/2GvzucDl48W9jt9VefjArtRabf9neHI3sPAcepAfw/AhxkH2AEn54AAAAASUVORK5CYII= compound_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI5ODAiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMCByMTUyOTkiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImNvbXBvdW5kLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS91c2VyL0Rvd25sb2Fkcy9jYWQvbXlzdHVmZi9pY29ucy9PcGVuU0NBRF93b3JrYmVuY2gvT3BlblNDQURfRXhwbG9kZV9Hcm91cF85XzMycHgucG5nIgogICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNDUiCiAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0NSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczI5ODIiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTk0Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2E0MDAwMDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM5OTYiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNlZjI5Mjk7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzOTk4IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE0MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTQ1IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzQ2NWE0O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzE0NyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQyODciPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQyODkiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2Y4N2M3MTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wNDI5MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmYwMDAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4NjQiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjYiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcxYjJmODtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2OCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAyNzk1O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4NjQiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzODUwIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNjAyODQ1OSwxLjA0NzE2MzksLTEuOTc5NDAyMSwxLjEzOTUyOTUsMTI3Ljk1ODgsLTc0LjQ1NjkwNykiCiAgICAgICBjeD0iNTEuMzI4ODkyIgogICAgICAgY3k9IjMxLjA3NDE0NiIKICAgICAgIGZ4PSI1MS4zMjg4OTIiCiAgICAgICBmeT0iMzEuMDc0MTQ2IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI5ODgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzctNiIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM2OTkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSI3Ni4zODMzMzEiCiAgICAgICBjeT0iOTQuMzY5NTY4IgogICAgICAgZng9Ijc2LjM4MzMzMSIKICAgICAgIGZ5PSI5NC4zNjk1NjgiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTgxODk0MywwLjE4OTQyOTUsLTAuNDEwOTQyNywyLjEzMDA5MjQsNDAuMTYzNDUzLC0xMjEuMTE1NTkpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzc3Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMzc5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZmMmI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMzODEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmYWEwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzc3IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzcwMSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3g9Ijg0Ljg4MzMyNCIKICAgICAgIGN5PSI3Ny4wNDI4NDciCiAgICAgICBmeD0iODQuODgzMzI0IgogICAgICAgZnk9Ijc3LjA0Mjg0NyIKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi44NDkyNDIxLDEuMjU4NTExOSwtMC40MDQwNDE1LDAuOTE0NzQwNywtMTI1Ljg0MTMxLC0xMDAuMjU4MDUpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDA3Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMDA5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZmMmI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMwMTEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmYWEwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBmeT0iNzcuMDQyODQ3IgogICAgICAgZng9Ijg0Ljg4MzMyNCIKICAgICAgIGN5PSI3Ny4wNDI4NDciCiAgICAgICBjeD0iODQuODgzMzI0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjg0OTI0MjEsMS4yNTg1MTE5LC0wLjQwNDA0MTUsMC45MTQ3NDA3LC0xMjUuODQxMzEsLTEwMC4yNTgwNSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwMTciCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzc3LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzctNiIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM2OTktMiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3g9Ijc2LjM4MzMzMSIKICAgICAgIGN5PSI5NC4zNjk1NjgiCiAgICAgICBmeD0iNzYuMzgzMzMxIgogICAgICAgZnk9Ijk0LjM2OTU2OCIKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45ODE4OTQzLDAuMTg5NDI5NSwtMC40MTA5NDI3LDIuMTMwMDkyNCw0MC4xNjM0NTMsLTEyMS4xMTU1OSkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMzNzctNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OS0yIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMGFmZmY7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMzODEtNCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAzNGZmO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGZ5PSI3Ny4wNDI4NDciCiAgICAgICBmeD0iODQuODgzMzI0IgogICAgICAgY3k9Ijc3LjA0Mjg0NyIKICAgICAgIGN4PSI4NC44ODMzMjQiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuODQ5MjQyMSwxLjI1ODUxMTksLTAuNDA0MDQxNSwwLjkxNDc0MDcsLTEyNS44NDEzMSwtMTAwLjI1ODA1KSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzAxNy04IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzM3Ny02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODM1Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODM3IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZmMmI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4MzkiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmYWEwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBmeT0iNzcuMDQyODQ3IgogICAgICAgZng9Ijg0Ljg4MzMyNCIKICAgICAgIGN5PSI3Ny4wNDI4NDciCiAgICAgICBjeD0iODQuODgzMzI0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjg0OTI0MjEsMS4yNTg1MTE5LC0wLjQwNDA0MTUsMC45MTQ3NDA3LC0xMjUuODQxMzEsLTEwMC4yNTgwNSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM4NDQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzc3LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzctNiIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM2OTktNyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3g9Ijc2LjM4MzMzMSIKICAgICAgIGN5PSI5NC4zNjk1NjgiCiAgICAgICBmeD0iNzYuMzgzMzMxIgogICAgICAgZnk9Ijk0LjM2OTU2OCIKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45ODE4OTQzLDAuMTg5NDI5NSwtMC40MTA5NDI3LDIuMTMwMDkyNCw0MC4xNjM0NTMsLTEyMS4xMTU1OSkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMzNzctMiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OS03IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZmMmI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDMzODEtOCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZhYTAwO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGZ5PSI3Ny4wNDI4NDciCiAgICAgICBmeD0iODQuODgzMzI0IgogICAgICAgY3k9Ijc3LjA0Mjg0NyIKICAgICAgIGN4PSI4NC44ODMzMjQiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuODQ5MjQyMSwxLjI1ODUxMTksLTAuNDA0MDQxNSwwLjkxNDc0MDcsLTEyNS44NDEzMSwtMTAwLjI1ODA1KSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzAxNy02IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzM3Ny0yIgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODM1LTEiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4MzctMCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmFmZjJiO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODM5LTkiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmYWEwMDtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBmeT0iNzcuMDQyODQ3IgogICAgICAgZng9Ijg0Ljg4MzMyNCIKICAgICAgIGN5PSI3Ny4wNDI4NDciCiAgICAgICBjeD0iODQuODgzMzI0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjg0OTI0MjEsMS4yNTg1MTE5LC0wLjQwNDA0MTUsMC45MTQ3NDA3LC0xMjUuODQxMzEsLTEwMC4yNTgwNSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM4NDQtNyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzNzctNiIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDAzMiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MWIyZjg7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDAzNCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMjc5NTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3A0MDM2IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDAzMiIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDQxMTQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4xOTAzNjgzMywtMC42MTM4NjM1NywxLjk5ODUzLC0wLjE5MDkyODAxLC00Ny4xMzMxOTksMTY1LjQ1NTU5KSIKICAgICAgIGN4PSIxMTMuNTAxODciCiAgICAgICBjeT0iNjUuODQ5MjgxIgogICAgICAgZng9IjExMy41MDE4NyIKICAgICAgIGZ5PSI2NS44NDkyODEiCiAgICAgICByPSIxOS40Njc0MzYiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNzEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDMxNzMiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzE3NSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQwMzItNyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MWIyZjg7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDAzNC0zIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAyNzk1O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDQwMzYtNiIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQwMzItNyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDQxMTQtMyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjE5MDM2ODMzLC0wLjYxMzg2MzU3LDEuOTk4NTMsLTAuMTkwOTI4MDEsLTQ3LjEzMzE5OSwxNjUuNDU1NTkpIgogICAgICAgY3g9IjExMy41MDE4NyIKICAgICAgIGN5PSI2NS44NDkyODEiCiAgICAgICBmeD0iMTEzLjUwMTg3IgogICAgICAgZnk9IjY1Ljg0OTI4MSIKICAgICAgIHI9IjE5LjQ2NzQzNiIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE3MS05Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcxYjJmODtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTczLTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzE3NS01IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDAzMi04Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcxYjJmODtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0MDM0LTQiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDAzNi0zIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDAzMi04IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50NDExNC05IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuMTkwMzY4MzMsLTAuNjEzODYzNTcsMS45OTg1MywtMC4xOTA5MjgwMSwtNDcuMTMzMTk5LDE2NS40NTU1OSkiCiAgICAgICBjeD0iMTEzLjUwMTg3IgogICAgICAgY3k9IjY1Ljg0OTI4MSIKICAgICAgIGZ4PSIxMTMuNTAxODciCiAgICAgICBmeT0iNjUuODQ5MjgxIgogICAgICAgcj0iMTkuNDY3NDM2IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTcxLTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDMxNzMtMCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMjc5NTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMTc1LTMiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDMyLTAiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQwMzQtMzciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDAzNi0xIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDAzMi0wIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzcwMyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3g9IjEzMi43MDQ1NCIKICAgICAgIGN5PSI5MC4xOTMyNDUiCiAgICAgICBmeD0iMTMyLjcwNDU0IgogICAgICAgZnk9IjkwLjE5MzI0NSIKICAgICAgIHI9IjE5LjQ2NzQzNiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMDgxNjIzMzksMS4zOTQ5MDcyLC0xLjE1NzI1NjksLTAuMjY5NjMzNzQsMjQ1LjIyNzczLC0xMDUuNDQzNjMpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMjg0Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcxYjJmODtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMjg2IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAyNzk1O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMyODgiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNTkzIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzU5OSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3g9IjUxLjYzNzg5NCIKICAgICAgIGN5PSIyNC45NjI3MDQiCiAgICAgICBmeD0iNTEuNjM3ODk0IgogICAgICAgZnk9IjI0Ljk2MjcwNCIKICAgICAgIHI9IjE5LjU3MTQyOCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzU5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzU5NSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzN2RjYTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNTk3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg2NC05IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzU1MiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3g9IjQ4LjY0NTgzNiIKICAgICAgIGN5PSIyNS4xNDkwNDIiCiAgICAgICBmeD0iNDguNjQ1ODM2IgogICAgICAgZnk9IjI1LjE0OTA0MiIKICAgICAgIHI9IjE5LjU3MTQyOCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NC05Ij4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY2LTMiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcxYjJmODtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2OC0zIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgcj0iMTkuNTcxNDI4IgogICAgICAgZnk9IjI1LjE0OTA0MiIKICAgICAgIGZ4PSI0OC42NDU4MzYiCiAgICAgICBjeT0iMjUuMTQ5MDQyIgogICAgICAgY3g9IjQ4LjY0NTgzNiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzMwNCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4NjQtOSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDI4NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDQyODUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuNTQzNTU5NTUsLTAuNjk5NTk1NjcsMS4wOTI0MjQ5LC0wLjYxNDEwNTU4LDk1LjY2NzY0MiwyMDMuMTYxNjEpIgogICAgICAgY3g9IjExMi43NzE4IgogICAgICAgY3k9IjY2LjI1NTUzMSIKICAgICAgIGZ4PSIxMTIuNzcxOCIKICAgICAgIGZ5PSI2Ni4yNTU1MzEiCiAgICAgICByPSIxOS40Njc0MzYiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDQyODciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MjkzIgogICAgICAgeDE9IjI1LjU1OTc3MSIKICAgICAgIHkxPSI0OC40MDM3NTkiCiAgICAgICB4Mj0iMzEuNDc3NTM1IgogICAgICAgeTI9IjUyLjcxMDY4NiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iMjUuMTQ5MDQyIgogICAgICAgZng9IjQ4LjY0NTgzNiIKICAgICAgIGN5PSIyNS4xNDkwNDIiCiAgICAgICBjeD0iNDguNjQ1ODM2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMzA0LTMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTktNiIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NC05LTYiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjYtMy03IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MWIyZjg7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjgtMy01IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgcj0iMTkuNTcxNDI4IgogICAgICAgZnk9IjI1LjE0OTA0MiIKICAgICAgIGZ4PSI0OC42NDU4MzYiCiAgICAgICBjeT0iMjUuMTQ5MDQyIgogICAgICAgY3g9IjQ4LjY0NTgzNiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzA5MCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMxNDMiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuNjMyOTQwMjksMC45MzA4OTEzMiwtMC44MjY5NTM3NiwwLjU2MjI3MDAyLDM4LjY1MzAyMiwtMzQuMjc1NDk2KSIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgcj0iMTkuNTcxNDI4IgogICAgICAgZnk9IjI1LjE0OTA0MiIKICAgICAgIGZ4PSI0OC42NDU4MzYiCiAgICAgICBjeT0iMjUuMTQ5MDQyIgogICAgICAgY3g9IjQ4LjY0NTgzNiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzA5MC01IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg2NC05LTYtNiIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NC05LTYtNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2Ni0zLTctMiIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4LTMtNS05IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgcj0iMTkuNTcxNDI4IgogICAgICAgZnk9IjI1LjE0OTA0MiIKICAgICAgIGZ4PSI0OC42NDU4MzYiCiAgICAgICBjeT0iMjUuMTQ5MDQyIgogICAgICAgY3g9IjQ4LjY0NTgzNiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzA5MC0yIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzE0My03IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjYzMjk0MDI5LDAuOTMwODkxMzIsLTAuODI2OTUzNzYsMC41NjIyNzAwMiwzOC42NTMwMjIsLTM0LjI3NTQ5NikiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNDMtNyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTQ1LTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMTQ3LTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iMjUuMTQ5MDQyIgogICAgICAgZng9IjQ4LjY0NTgzNiIKICAgICAgIGN5PSIyNS4xNDkwNDIiCiAgICAgICBjeD0iNDguNjQ1ODM2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjYzMjk0MDI5LDAuOTMwODkxMzIsLTAuODI2OTUzNzYsMC41NjIyNzAwMiwzOC42NTMwMjIsLTM0LjI3NTQ5NikiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMxNjUiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQzLTciCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM5OTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDAwIgogICAgICAgeDE9IjYyLjAwMDAwNCIKICAgICAgIHkxPSIzNCIKICAgICAgIHgyPSI1MiIKICAgICAgIHkyPSIzNCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgc3ByZWFkTWV0aG9kPSJyZWZsZWN0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjg3NjQ4NTU1LDAsMCwxLjEwNjgxMjgsMC44MTc2MDQxNiwxLjMyNzc3MzMpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzOTk0LTMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDAwLTIiCiAgICAgICB4MT0iNjIuMDAwMDA0IgogICAgICAgeTE9IjM0IgogICAgICAgeDI9IjUyIgogICAgICAgeTI9IjM0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBzcHJlYWRNZXRob2Q9InJlZmxlY3QiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODc2NDg1NTUsMCwwLDEuMTA2ODEyOCwwLjgxNzYwNDE2LDEuMzI3NzczMykiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5OTQtMyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNhNDAwMDA7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzOTk2LTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNlZjI5Mjk7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzOTk4LTUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMy40MjUwNDg1IgogICAgIGlua3NjYXBlOmN4PSI0Ny4xMzE2ODkiCiAgICAgaW5rc2NhcGU6Y3k9IjEyLjgzNDU4OCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJnMzU2MCIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTUzNiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4MDEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOnNuYXAtZ2xvYmFsPSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpzbmFwLW5vZGVzPSJ0cnVlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDczIgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI5ODUiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bS2VpdGggU2xvYW5dPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzp0aXRsZT5PcGVuU0NBRF9IdWxsPC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDEzLTEwLTMwPC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL09wZW5TQ0FEL1Jlc291cmNlcy9pY29ucy9PcGVuU0NBRF9IdWxsLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPGcKICAgICAgIGlkPSJnMzU2MCIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuOTEyNzM2MTgsMCwwLDAuOTAzNDk1MTUsMTA3Ljg1Mzc0LC0xLjE5OTYzNjcpIj4KICAgICAgPGcKICAgICAgICAgaWQ9Imc0NjEyIgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjcxOTAxNDg3LDAsMCwwLjcxOTAxNDg3LC0yMy4zNTE1MTUsMTAuMzI1MDE5KSI+CiAgICAgICAgPGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC4xOTU0NzI5ZS02LDIuMzQyNTIwOWUtNikiCiAgICAgICAgICAgaWQ9Imc5MjkiPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiMzNDY1YTQ7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgICAgIGQ9Ik0gOSwyOCAyOSw4IDU4LDMyIDM1LDU2IFoiCiAgICAgICAgICAgICBpZD0icGF0aDQxODciCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wOTU2MDY4LDAsMCwxLjEwNjgxMjgsLTExOC4xNjUzLDEuMzI3NzczMykiIC8+CiAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgZD0ibSAzNCw1NCAxOS4wMDAwMDYsLTIwIC0yMywtMjQgTCA5LDMxIDMyLjAwMDAwNiw1MSBaIgogICAgICAgICAgICAgaWQ9InBhdGgzMDg5IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMDk1NjA2OCwwLDAsMS4xMDY4MTI4LC0xMTguMTY1MywxLjMyNzc3MzMpIgogICAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2MiIC8+CiAgICAgICAgICA8Y2lyY2xlCiAgICAgICAgICAgICByPSIxOC41NzE0MjgiCiAgICAgICAgICAgICBjeT0iMzQuNTcxNDI2IgogICAgICAgICAgICAgY3g9IjUzLjIxNDI4NyIKICAgICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMTIwOTU0MSwwLDAsMS4xMzI0MTk0LC0xMzEuODAwNTcsLTEzLjQ3MTY5OSkiCiAgICAgICAgICAgICBpZD0icGF0aDM1NTAtMyIKICAgICAgICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQzMDkwKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45NTI2NTMyOTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7bWFya2VyOm5vbmU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIgLz4KICAgICAgICAgIDxjaXJjbGUKICAgICAgICAgICAgIHI9IjE4LjU3MTQyOCIKICAgICAgICAgICAgIGN5PSIzNC41NzE0MjYiCiAgICAgICAgICAgICBjeD0iNTMuMjE0Mjg3IgogICAgICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMDI5NjU5LDAsMCwxLjAxMzIyNDEsLTEyNS41MjE5MiwtOS4zNTA5NDk0KSIKICAgICAgICAgICAgIGlkPSJwYXRoMzU1MC0zLTEiCiAgICAgICAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7ZmlsbDpub25lO3N0cm9rZTojMzQ2NWE0O3N0cm9rZS13aWR0aDoyLjE4MjM2MjMyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIiAvPgogICAgICAgICAgPGNpcmNsZQogICAgICAgICAgICAgcj0iMTguNTcxNDI4IgogICAgICAgICAgICAgY3k9IjM0LjU3MTQyNiIKICAgICAgICAgICAgIGN4PSI1My4yMTQyODciCiAgICAgICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjEyMDk1NDEsMCwwLDEuMTMyNDE5NCwtMTUzLjcxMjcyLDguNjY0NTUyNCkiCiAgICAgICAgICAgICBpZD0icGF0aDM1NTAtMy0zIgogICAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDMxNjUpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxLjk1MjY1MzI5O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIiAvPgogICAgICAgICAgPGNpcmNsZQogICAgICAgICAgICAgcj0iMTguNTcxNDI4IgogICAgICAgICAgICAgY3k9IjM0LjU3MTQyNiIKICAgICAgICAgICAgIGN4PSI1My4yMTQyODciCiAgICAgICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjAwMjk2NTksMCwwLDEuMDEzMjI0MSwtMTQ3LjQzNDA4LDEyLjc4NTMwMSkiCiAgICAgICAgICAgICBpZD0icGF0aDM1NTAtMy0xLTYiCiAgICAgICAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7ZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoyLjE4MjM2MjMyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIiAvPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgICA8ZwogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjE5NTQ1MTgsMCwwLDAuMTk3NDUwODksLTMwOC42NzcxNiwtNzguNjkzMDA1KSIKICAgICAgICAgaWQ9Imc4NDciPgogICAgICAgIDxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC45MjAwMDAwNCwwLDAsMC45OTg1MjgyNiw5Mi4zMjgwOCwxLjEwNzkzMzUpIgogICAgICAgICAgIGlkPSJnMzUyNy0zIgogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9yZWN0YW5nbGUucG5nIgogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI2LjU4OTU2NjciCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjYuNTg5NTY2NyI+CiAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjY2NjIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiNkM2Q3Y2Y7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiMyZTM0MzY7c3Ryb2tlLXdpZHRoOjE1LjI1NjgzNDAzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgICAgZD0iTSA5NzEuMzEzNDksNDE1Ljk4NDU3IFYgNzUyLjgxMDg2IEggMTMzNi44OTAyIFYgNDE1Ljk4NDU3IFogbSA0My44NjkyMSw0NC4zNjQ5MiAyNzQuMDIzNiwtMC40MzEwNSB2IDI0OC45NTg1NyBsIC0yNzQuMDIzNiwwLjA2NDYgeiIKICAgICAgICAgICAgIGlkPSJyZWN0MjIzMyIgLz4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjYyIKICAgICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDcuMzExNTM0MiwwLDAsNy4zMTE1MzQyLDkyMC4xMzI3NSwzNTAuNjc2NDgpIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzA0MCIKICAgICAgICAgICAgIGQ9Ik0gNTUuOTEzMDQ1LDEwLjkzNTE0OSBIIDkuMTczOTE2NCB2IDQzLjA2MzM3OCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjYyIKICAgICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDcuMzExNTM0MiwwLDAsNy4zMTE1MzQyLDkyMC4xMzI3NSwzNTAuNjc2NDgpIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzA0MiIKICAgICAgICAgICAgIGQ9Im0gMTMsNTEgMzkuNjg1NTc1LDAuMDMyMTkgLTAuMDY5ODcsLTM2LjEyODc1NSIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjIuMDg2NjgwMTc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8ZwogICAgICAgaWQ9ImczNzYwLTgiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjQyMTU2MjEsLTAuMDU1NTMxNjksLTAuMDc5MzU0MzgsMC41NDA4NjUyNSwyOC4wMDA0ODUsLTE1LjgxMzE3KSIgLz4KICA8L2c+Cjwvc3ZnPgo= """ shiftX_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMxMTEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0ic2hpZnRYLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczMxMTMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODQxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzA2MTljMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODQzIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzc5Y2ZiO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4NDUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODQxIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NjY1NSIKICAgICAgIHgxPSIxNTUuNDY4NzUiCiAgICAgICB5MT0iMjU0NS4yMTg4IgogICAgICAgeDI9IjcxMy4wNjI1IgogICAgICAgeTI9IjI1NDUuMjE4OCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgyLC0yKSIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMTE5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAsLTEuNDUwMDAwMSwxLjQ3MDU4ODIsMCwtMTUuMDU4ODIsOTEuNDUpIgogICAgICAgeTI9IjM2LjA3OTk5OCIKICAgICAgIHgyPSIyMS42ODk2NTMiCiAgICAgICB5MT0iMjkuMjc5OTk5IgogICAgICAgeDE9IjU2LjE3MjQwOSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzNiIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzOS41MTQxMDMiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDIwLjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjUiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiwzNS4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1LTMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzOS41MTQxMDMiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTE9IjI0LjM3OTMwOSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDIwLjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjYiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5MDAiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSIzMCIKICAgICAgIHkxPSI2IgogICAgICAgeDI9IjM0IgogICAgICAgeTI9IjU3IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTYtNCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny03LTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS01LTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCw2LjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjYtNCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDM5Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMDQxIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMwNDMiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjUtOCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NS04Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODk3LTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS00IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE1MCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNTIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTU0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0MSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjkuNjg3NSIKICAgICBpbmtzY2FwZTpjeD0iLTMuNDA2NDUxOCIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1ub2Rlcz0idHJ1ZSIKICAgICBpbmtzY2FwZTpzbmFwLWdsb2JhbD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDMwNDYiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDQ4IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIgogICAgICAgc3BhY2luZ3g9IjE2IgogICAgICAgc3BhY2luZ3k9IjE2IgogICAgICAgb3JpZ2lueD0iMCIKICAgICAgIG9yaWdpbnk9IjAiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPGcKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMDYxMjA4LDAsMCwxLjA2MTIwOCwtMS45NTg2NTYsMTIuMDQxMzQ0KSIKICAgICAgIGlkPSJnMzAxMSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzIgogICAgICAgICBkPSJtIDQ5LDE5IDAsOCAtOCwwIDAsMTAgOCwwIDAsOCAxMiwtMTMgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMwNDQpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzLTIiCiAgICAgICAgIGQ9Ik0gNTEuMDQwNzQ4LDI0LjQ4NzAyMSA1MSwyOSBsIC04LDAgMCw2IDgsMCAwLjAxNzA1LDUuMTMwNjgyIDcuMjY0MTkxLC04LjEwNjU3NiB6IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2c+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wNjEyMDgsMCwwLDEuMDYxMjA4LC0xLjk1ODY1NiwxMi4wNDEzNDQpIgogICAgICAgaWQ9ImczMDA3Ij4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMyIKICAgICAgICAgZD0ibSAxNSwxOSAwLDggOCwwIDAsMTAgLTgsMCAwLDggTCAzLDMyIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDQ2KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0yLTUiCiAgICAgICAgIGQ9Ik0gMTIuOTU5MjUyLDI0LjQ4NzAyMSAxMywyOSBsIDgsMCAwLDYgLTgsMCAtMC4wMTcwNSw1LjEzMDY4MiAtNy4yNjQxOTEsLTguMTA2NTc2IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDwvZz4KICAgIDxnCiAgICAgICBpZD0iZzMxNjAiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOS45MzUwMzksNy45ODgxODkpIj4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMTYyIgogICAgICAgICBkPSJtIDE1LjU5MDU1MSwxMS43NjM3OCA3LjkzNzAwOCwxMS42NTc0OCAtNi4xMjk5MjEsMCAtNS4zNTAzOTQsLTcuNzk1Mjc1IC01LjI3OTUyNzUsNy43OTUyNzUgLTYuMTY1MzU0MywwIDcuOTAxNTc0NywtMTEuNjU3NDggLTcuNjE4MTEwMTQsLTExLjE2MTQxNzc4IDYuMTY1MzU0MjQsMCA0Ljk5NjA2Myw3LjMzNDY0NTc4IDQuOTk2MDYzLC03LjMzNDY0NTc4IDYuMTY1MzU0LDAgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6I2Q0MDAwMDtzdHJva2Utd2lkdGg6MS4xNjkyOTEzOHB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2c+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTU3ODEiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlIC8+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9IiIgLz4KICAgICAgICA8ZGM6ZGF0ZT5Nb24gT2N0IDEwIDEzOjQ0OjUyIDIwMTEgKzAwMDA8L2RjOmRhdGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9EcmFmdC9SZXNvdXJjZXMvaWNvbnMvRHJhZnRfVHJpbWV4LnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5hcnJvdzwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmFycm93czwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmxpbmU8L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgICAgPGRjOmRlc2NyaXB0aW9uPkEgdmVydGljYWwgbGluZSB3aXRoIGFuIGFycm93IHBvaW50aW5nIGF3YXkgZnJvbSBpdCBvbiBlYWNoIHNpZGU8L2RjOmRlc2NyaXB0aW9uPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KPC9zdmc+Cg== """ shiftY_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMxMTEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0ic2hpZnRYLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczMxMTMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODQxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzA2MTljMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODQzIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzc5Y2ZiO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4NDUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODQxIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NjY1NSIKICAgICAgIHgxPSIxNTUuNDY4NzUiCiAgICAgICB5MT0iMjU0NS4yMTg4IgogICAgICAgeDI9IjcxMy4wNjI1IgogICAgICAgeTI9IjI1NDUuMjE4OCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgyLC0yKSIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMTE5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAsLTEuNDUwMDAwMSwxLjQ3MDU4ODIsMCwtMTUuMDU4ODIsOTEuNDUpIgogICAgICAgeTI9IjM2LjA3OTk5OCIKICAgICAgIHgyPSIyMS42ODk2NTMiCiAgICAgICB5MT0iMjkuMjc5OTk5IgogICAgICAgeDE9IjU2LjE3MjQwOSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzNiIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzOS41MTQxMDMiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDIwLjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjUiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiwzNS4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1LTMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzOS41MTQxMDMiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTE9IjI0LjM3OTMwOSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDIwLjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjYiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5MDAiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSIzMCIKICAgICAgIHkxPSI2IgogICAgICAgeDI9IjM0IgogICAgICAgeTI9IjU3IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTYtNCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny03LTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS01LTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCw2LjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjYtNCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDM5Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMDQxIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMwNDMiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjUtOCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NS04Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODk3LTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS00IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE1MCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNTIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTU0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0MSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjkuNjg3NSIKICAgICBpbmtzY2FwZTpjeD0iLTE2LjgyNTgwNyIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1ub2Rlcz0idHJ1ZSIKICAgICBpbmtzY2FwZTpzbmFwLWdsb2JhbD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDMwNDYiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDQ4IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIgogICAgICAgc3BhY2luZ3g9IjE2IgogICAgICAgc3BhY2luZ3k9IjE2IgogICAgICAgb3JpZ2lueD0iMCIKICAgICAgIG9yaWdpbnk9IjAiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHRleHQKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6MzEuMzUyMDM5MzRweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0OmJvbGQ7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwODAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwNTUwMDtzdHJva2Utd2lkdGg6MS4xNzAxMTc5NztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO2Rpc3BsYXk6aW5saW5lO2ZvbnQtZmFtaWx5OkRlamFWdSBTYW5zOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246RGVqYVZ1IFNhbnMgQm9sZCIKICAgICAgIHg9IjIwLjY0ODY2MyIKICAgICAgIHk9IjMxLjQyNzg3OSIKICAgICAgIGlkPSJ0ZXh0NDE5OSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQyMDEiCiAgICAgICAgIHg9IjIwLjY0ODY2MyIKICAgICAgICAgeT0iMzEuNDI3ODc5IgogICAgICAgICBzdHlsZT0ic3Ryb2tlOiMwMDU1MDA7c3Ryb2tlLXdpZHRoOjEuMTcwMTE3OTc7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiPlk8L3RzcGFuPjwvdGV4dD4KICAgIDxnCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjA2MTIwOCwwLDAsMS4wNjEyMDgsLTEuOTU4NjU2LDEyLjA0MTM0NCkiCiAgICAgICBpZD0iZzMwMTEiPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0MyIKICAgICAgICAgZD0ibSA0OSwxOSAwLDggLTgsMCAwLDEwIDgsMCAwLDggMTIsLTEzIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDQ0KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0yIgogICAgICAgICBkPSJNIDUxLjA0MDc0OCwyNC40ODcwMjEgNTEsMjkgbCAtOCwwIDAsNiA4LDAgMC4wMTcwNSw1LjEzMDY4MiA3LjI2NDE5MSwtOC4xMDY1NzYgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPC9nPgogICAgPGcKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMDYxMjA4LDAsMCwxLjA2MTIwOCwtMS45NTg2NTYsMTIuMDQxMzQ0KSIKICAgICAgIGlkPSJnMzAwNyI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzLTMiCiAgICAgICAgIGQ9Im0gMTUsMTkgMCw4IDgsMCAwLDEwIC04LDAgMCw4IEwgMywzMiB6IgogICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzA0Nik7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMi01IgogICAgICAgICBkPSJNIDEyLjk1OTI1MiwyNC40ODcwMjEgMTMsMjkgbCA4LDAgMCw2IC04LDAgLTAuMDE3MDUsNS4xMzA2ODIgLTcuMjY0MTkxLC04LjEwNjU3NiB6IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2c+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTU3ODEiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9IiIgLz4KICAgICAgICA8ZGM6ZGF0ZT5Nb24gT2N0IDEwIDEzOjQ0OjUyIDIwMTEgKzAwMDA8L2RjOmRhdGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9EcmFmdC9SZXNvdXJjZXMvaWNvbnMvRHJhZnRfVHJpbWV4LnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5hcnJvdzwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmFycm93czwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmxpbmU8L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgICAgPGRjOmRlc2NyaXB0aW9uPkEgdmVydGljYWwgbGluZSB3aXRoIGFuIGFycm93IHBvaW50aW5nIGF3YXkgZnJvbSBpdCBvbiBlYWNoIHNpZGU8L2RjOmRlc2NyaXB0aW9uPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KPC9zdmc+Cg== """ shiftZ_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMxMTEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0ic2hpZnRYLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczMxMTMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODQxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzA2MTljMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODQzIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzc5Y2ZiO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4NDUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODQxIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NjY1NSIKICAgICAgIHgxPSIxNTUuNDY4NzUiCiAgICAgICB5MT0iMjU0NS4yMTg4IgogICAgICAgeDI9IjcxMy4wNjI1IgogICAgICAgeTI9IjI1NDUuMjE4OCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgyLC0yKSIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMTE5IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAsLTEuNDUwMDAwMSwxLjQ3MDU4ODIsMCwtMTUuMDU4ODIsOTEuNDUpIgogICAgICAgeTI9IjM2LjA3OTk5OCIKICAgICAgIHgyPSIyMS42ODk2NTMiCiAgICAgICB5MT0iMjkuMjc5OTk5IgogICAgICAgeDE9IjU2LjE3MjQwOSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzNiIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzOS41MTQxMDMiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDIwLjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjUiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiwzNS4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1LTMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSIzOS41MTQxMDMiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTE9IjI0LjM3OTMwOSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDIwLjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjYiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM5MDAiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSIzMCIKICAgICAgIHkxPSI2IgogICAgICAgeDI9IjM0IgogICAgICAgeTI9IjU3IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTYtNCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny03LTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS01LTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCw2LjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjYtNCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDM5Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMDQxIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMwNDMiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMjUtOCIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NS04Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODk3LTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS00IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE1MCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNTIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTU0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0MSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjkuNjg3NSIKICAgICBpbmtzY2FwZTpjeD0iLTIxLjEwOTY3OCIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1ub2Rlcz0idHJ1ZSIKICAgICBpbmtzY2FwZTpzbmFwLWdsb2JhbD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDMwNDYiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDQ4IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIgogICAgICAgc3BhY2luZ3g9IjE2IgogICAgICAgc3BhY2luZ3k9IjE2IgogICAgICAgb3JpZ2lueD0iMCIKICAgICAgIG9yaWdpbnk9IjAiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPGcKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMDYxMjA4LDAsMCwxLjA2MTIwOCwtMS45NTg2NTYsMTIuMDQxMzQ0KSIKICAgICAgIGlkPSJnMzAxMSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzIgogICAgICAgICBkPSJtIDQ5LDE5IDAsOCAtOCwwIDAsMTAgOCwwIDAsOCAxMiwtMTMgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMwNDQpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzLTIiCiAgICAgICAgIGQ9Ik0gNTEuMDQwNzQ4LDI0LjQ4NzAyMSA1MSwyOSBsIC04LDAgMCw2IDgsMCAwLjAxNzA1LDUuMTMwNjgyIDcuMjY0MTkxLC04LjEwNjU3NiB6IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2c+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4wNjEyMDgsMCwwLDEuMDYxMjA4LC0xLjk1ODY1NiwxMi4wNDEzNDQpIgogICAgICAgaWQ9ImczMDA3Ij4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMyIKICAgICAgICAgZD0ibSAxNSwxOSAwLDggOCwwIDAsMTAgLTgsMCAwLDggTCAzLDMyIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDQ2KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0yLTUiCiAgICAgICAgIGQ9Ik0gMTIuOTU5MjUyLDI0LjQ4NzAyMSAxMywyOSBsIDgsMCAwLDYgLTgsMCAtMC4wMTcwNSw1LjEzMDY4MiAtNy4yNjQxOTEsLTguMTA2NTc2IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDwvZz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTozMi4yNzA0OTYzN3B4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6Ym9sZDtmb250LXN0cmV0Y2g6bm9ybWFsO3RleHQtYWxpZ246c3RhcnQ7bGluZS1oZWlnaHQ6NzYuOTk5OTk4MDklO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O3dyaXRpbmctbW9kZTpsci10Yjt0ZXh0LWFuY2hvcjpzdGFydDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuMjA0Mzk4MTY7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtmb250LWZhbWlseTpTYW5zOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246U2FucyBCb2xkIgogICAgICAgeD0iMjIuMjcwMDA0IgogICAgICAgeT0iMzEuNDQ5OTQ0IgogICAgICAgaWQ9InRleHQ0NDk5LTEiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iNzYuOTk5OTk4JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ0OTctNyIKICAgICAgICAgeD0iMjIuMjcwMDA0IgogICAgICAgICB5PSIzMS40NDk5NDQiCiAgICAgICAgIHN0eWxlPSJmb250LXNpemU6MzIuMjcwNDk2MzdweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0OmJvbGQ7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWFsaWduOnN0YXJ0O2xpbmUtaGVpZ2h0Ojc2Ljk5OTk5ODA5JTt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMGZmO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjIwNDM5ODE2O3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7Zm9udC1mYW1pbHk6U2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlNhbnMgQm9sZCI+WjwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE1NzgxIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICAgIDxjYzpsaWNlbnNlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSIiIC8+CiAgICAgICAgPGRjOmRhdGU+TW9uIE9jdCAxMCAxMzo0NDo1MiAyMDExICswMDAwPC9kYzpkYXRlPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W3dtYXllcl08L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnJpZ2h0cz4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQgTEdQTDIrPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpyaWdodHM+CiAgICAgICAgPGRjOnB1Ymxpc2hlcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQ8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnB1Ymxpc2hlcj4KICAgICAgICA8ZGM6aWRlbnRpZmllcj5GcmVlQ0FEL3NyYy9Nb2QvRHJhZnQvUmVzb3VyY2VzL2ljb25zL0RyYWZ0X1RyaW1leC5zdmc8L2RjOmlkZW50aWZpZXI+CiAgICAgICAgPGRjOnJlbGF0aW9uPmh0dHA6Ly93d3cuZnJlZWNhZHdlYi5vcmcvd2lraS9pbmRleC5waHA/dGl0bGU9QXJ0d29yazwvZGM6cmVsYXRpb24+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICAgIDxkYzpzdWJqZWN0PgogICAgICAgICAgPHJkZjpCYWc+CiAgICAgICAgICAgIDxyZGY6bGk+YXJyb3c8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5hcnJvd3M8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5saW5lPC9yZGY6bGk+CiAgICAgICAgICA8L3JkZjpCYWc+CiAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgIDxkYzpkZXNjcmlwdGlvbj5BIHZlcnRpY2FsIGxpbmUgd2l0aCBhbiBhcnJvdyBwb2ludGluZyBhd2F5IGZyb20gaXQgb24gZWFjaCBzaWRlPC9kYzpkZXNjcmlwdGlvbj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+Cjwvc3ZnPgo= """ rotateX_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMwMDAiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0icm90YXRlWC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzMDAyIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzM5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMzk1IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzM5NyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzOTMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzk5IgogICAgICAgeDE9IjE5NDIuNzM4MiIKICAgICAgIHkxPSIxOTM0Ljc1NiIKICAgICAgIHgyPSIxODA5LjUwMjEiCiAgICAgICB5Mj0iMTU0Ny40MTM1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjk4NjU3ODExLDAsMCwwLjk5OTIyMDc4LDM3MDYuODY1OCwtMi42ODExNjk3KSIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMDA4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzkzLTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzk5LTEiCiAgICAgICB4MT0iMTY2OS43MzE0IgogICAgICAgeTE9IjE3MjYuMDU4NSIKICAgICAgIHgyPSIyMDY3LjE3MDIiCiAgICAgICB5Mj0iMTcyNi4wNTg1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTg2NTc4MTEsMCwwLDAuOTk5MjIwNzgsMjAuMTI5MjUxLC0yLjY4MTE2OTcpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzkzLTciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAzZGRkO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDMzOTUtNCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzOWVmMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMzk3LTAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzkzLTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyOTk5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTg2NTc4MTEsMCwwLDAuOTk5MjIwNzgsMjAuMTI5MjUxLC0yLjY4MTE2OTcpIgogICAgICAgeDE9IjE5NDIuNzM4MiIKICAgICAgIHkxPSIxOTM0Ljc1NiIKICAgICAgIHgyPSIxODA5LjUwMjEiCiAgICAgICB5Mj0iMTU0Ny40MTM1IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzkzLTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzM5NS03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzM5Ny00IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjYuODUwMDk2OSIKICAgICBpbmtzY2FwZTpjeD0iLTI1LjIzNDM2OCIKICAgICBpbmtzY2FwZTpjeT0iMjkuNDY2MDgzIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczNDA1IgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9ImZhbHNlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMjk5MCIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczNDA1IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC4xMzY5MzY1LDAsMCwwLjEzNjkzNjUsLTIyMi4yMTc1NCwtMjAzLjM2NTEyKSI+CiAgICAgIDxnCiAgICAgICAgIGlkPSJnMzE2MCIKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNy4zMDI2NTQ5LDAsMCw3LjMwMjY1NDksMTc2OC4zNTY3LDE2MzEuMDcyMSkiPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgaWQ9InBhdGgzMTYyIgogICAgICAgICAgIGQ9Im0gMTUuNTkwNTUxLDExLjc2Mzc4IDcuOTM3MDA4LDExLjY1NzQ4IC02LjEyOTkyMSwwIC01LjM1MDM5NCwtNy43OTUyNzUgLTUuMjc5NTI3NSw3Ljc5NTI3NSAtNi4xNjUzNTQzLDAgNy45MDE1NzQ3LC0xMS42NTc0OCAtNy42MTgxMTAxNCwtMTEuMTYxNDE3NzggNi4xNjUzNTQyNCwwIDQuOTk2MDYzLDcuMzM0NjQ1NzggNC45OTYwNjMsLTcuMzM0NjQ1NzggNi4xNjUzNTQsMCB6IgogICAgICAgICAgIHN0eWxlPSJmaWxsOiNmZjAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiNkNDAwMDA7c3Ryb2tlLXdpZHRoOjEuMTY5MjkxMzhweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDwvZz4KICAgICAgPGcKICAgICAgICAgaWQ9ImcyOTk1IgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgtMSwwLDAsMSwzNzE5Ljk2NDksLTMuMjQyMDg1NSkiPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJzc3NjY2NjY3Nzc2NjcyIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQyOTk5KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MTQuNjA1MzEwNDQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICAgIGQ9Im0gMTY4MS4xOTIyLDE3OTIuNTc3IGMgMjkuNzUxOCwxMDEuOTIxIDEzNS41NjY5LDE2MC4xODE5IDIzNi4xOTgyLDEzMC4wNDg5IDEwMC42MzEyLC0zMC4xMzMxIDE1OC4xNTUxLC0xMzcuMzA0MSAxMjguNDAzNCwtMjM5LjIyNSAtMTUuNjc5MywtNTMuNzEyNCAtNTIuNDc5NSwtOTUuMjg5OSAtOTguNTE0NCwtMTE4LjIwMSBsIDIyLjQ5MDYsLTUxLjcwMjUgLTE3OS43NTI2LDkuMzgyNyA5NS42Njc0LDE1OS4yMjIxIDMzLjI4NywtNTEuODY2MiBjIDI4LjkxNjcsMTUuMTM1NCA1MS45NDM4LDQxLjcwNzEgNjEuODk1Myw3NS43OTc3IDE5LjM3NCw2Ni4zNjk5IC0xOC4wNzk3LDEzNi4xNDkzIC04My42MTAxLDE1NS43NzE2IC02NS41MzA0LDE5LjYyMjMgLTEzNC40NTYyLC0xOC4zMDI2IC0xNTMuODMwMywtODQuNjcyNiAtNS41NDMzLC0xOC45OTAyIC02LjQxNDcsLTM4LjI0NjkgLTMuMzIzNSwtNTYuNTQxNCBsIC02My45MzIxLC0xNS4zNzE3IGMgLTQuODQ5NiwyOC4yNTE0IC0zLjU0NSw1OC4wMTI0IDUuMDIxMSw4Ny4zNTc0IHoiCiAgICAgICAgICAgaWQ9InBhdGgyMzk2IiAvPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJzc3NjY2NjY3NzY2NjcyIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjE0LjYwNTMwOTQ5O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgICAgICBkPSJtIDE2OTcuMDczNywxNzkzLjAxNDkgYyAyMS45MDI2LDc1LjgwNjYgOTYuMTMzMiwxMjEuODQ4MiAxNzMuNDc2OSwxMjMuMjE4NyAxMTIuMTQ1NSwxLjk4NzIgMTY5LjQwMjIsLTExMy4wMDMgMTY5Ljc2OTIsLTE3My40MjA0IDAuNjM0LC0xMDQuMzc3IC03Ni4wMjI4LC0xNTUuMDQ0OCAtMTExLjMyMTQsLTE3Mi4yNDk4IGwgMTYuNTQxMiwtMzguNTkwOSAtMTI5LjYwNjUsNS4xOTAyIDY5LjgyNywxMTYuNzM2IDI3LjE2NTksLTQ0LjgyNDQgYyA2OS4yNzQ1LDQyLjUzNDcgODcuNDU3Myw4Mi4xNTc0IDg4LjIzMTQsMTM0LjgwMTggMC43OTIxLDUzLjg2OTkgLTQyLjY2MzcsMTMzLjk3OTUgLTEzMy40MzA5LDEzNy42Mzg3IC03OC4xNjQyLDMuMTUxMSAtMTIzLjAxNDMsLTUyLjkxMzEgLTEzNy44NywtMTAyLjc1MzIgLTYuNTc0LC0xNi4yNTIyIC02LjU3ODMsLTMzLjM2ODMgLTYuMTk5NywtNDcuMTA2NSBsIC0zNS42NjExLC04LjYzODggYyAtMS4wNjMxLDIxLjU0NzIgMi40MzAxLDQ2Ljk4OTMgOS4wNzgsNjkuOTk4NiB6IgogICAgICAgICAgIGlkPSJwYXRoMjM5Ni05IiAvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTUzMjQiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlIC8+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9IiIgLz4KICAgICAgICA8ZGM6ZGF0ZT5Nb24gT2N0IDEwIDEzOjQ0OjUyIDIwMTEgKzAwMDA8L2RjOmRhdGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9EcmFmdC9SZXNvdXJjZXMvaWNvbnMvRHJhZnRfUm90YXRlLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgICAgPGRjOmRlc2NyaXB0aW9uPkFuIGFycm93IGluIGEgY2lyY3VsYXIgc2hhcGUgd2l0aCB0aGUgaGVhZCBjdXJ2aW5nIHRvd2FyZHMgdGhlIHRhaWw8L2RjOmRlc2NyaXB0aW9uPgogICAgICAgIDxkYzpzdWJqZWN0PgogICAgICAgICAgPHJkZjpCYWc+CiAgICAgICAgICAgIDxyZGY6bGk+YXJyb3c8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5jdXJ2ZWQ8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5yZWZyZXNoPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+cm90YXRlPC9yZGY6bGk+CiAgICAgICAgICA8L3JkZjpCYWc+CiAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KPC9zdmc+Cg== """ rotateY_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMwMDAiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0icm90YXRlWS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzMDAyIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzM5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMzk1IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzM5NyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzOTMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzk5IgogICAgICAgeDE9IjE5NDIuNzM4MiIKICAgICAgIHkxPSIxOTM0Ljc1NiIKICAgICAgIHgyPSIxODA5LjUwMjEiCiAgICAgICB5Mj0iMTU0Ny40MTM1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjk4NjU3ODExLDAsMCwwLjk5OTIyMDc4LDM3MDYuODY1OCwtMi42ODExNjk3KSIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMDA4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzkzLTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzk5LTEiCiAgICAgICB4MT0iMTY2OS43MzE0IgogICAgICAgeTE9IjE3MjYuMDU4NSIKICAgICAgIHgyPSIyMDY3LjE3MDIiCiAgICAgICB5Mj0iMTcyNi4wNTg1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTg2NTc4MTEsMCwwLDAuOTk5MjIwNzgsMjAuMTI5MjUxLC0yLjY4MTE2OTcpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzkzLTciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAzZGRkO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDMzOTUtNCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzOWVmMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMzk3LTAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzkzLTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyOTk5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTg2NTc4MTEsMCwwLDAuOTk5MjIwNzgsMjAuMTI5MjUxLC0yLjY4MTE2OTcpIgogICAgICAgeDE9IjE5NDIuNzM4MiIKICAgICAgIHkxPSIxOTM0Ljc1NiIKICAgICAgIHgyPSIxODA5LjUwMjEiCiAgICAgICB5Mj0iMTU0Ny40MTM1IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzkzLTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzM5NS03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzM5Ny00IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjYuODUwMDk2OSIKICAgICBpbmtzY2FwZTpjeD0iLTI1LjIzNDM2OCIKICAgICBpbmtzY2FwZTpjeT0iMjkuNDY2MDgzIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczNDA1IgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9ImZhbHNlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMjk5MCIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczNDA1IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC4xMzY5MzY1LDAsMCwwLjEzNjkzNjUsLTIyMi4yMTc1NCwtMjAzLjM2NTEyKSI+CiAgICAgIDx0ZXh0CiAgICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgICBzdHlsZT0iZm9udC1zaXplOjIyOC45NTMxMjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0OmJvbGQ7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwODAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwNTUwMDtzdHJva2Utd2lkdGg6OC41NDQ5Njc2NTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO2Rpc3BsYXk6aW5saW5lO2ZvbnQtZmFtaWx5OkRlamFWdSBTYW5zOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246RGVqYVZ1IFNhbnMgQm9sZCIKICAgICAgICAgeD0iMTc3My41NjgxIgogICAgICAgICB5PSIxODAyLjI0NDEiCiAgICAgICAgIGlkPSJ0ZXh0NDE5OSI+PHRzcGFuCiAgICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgICBpZD0idHNwYW40MjAxIgogICAgICAgICAgIHg9IjE3NzMuNTY4MSIKICAgICAgICAgICB5PSIxODAyLjI0NDEiCiAgICAgICAgICAgc3R5bGU9InN0cm9rZTojMDA1NTAwO3N0cm9rZS13aWR0aDo4LjU0NDk2NzY1O3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIj5ZPC90c3Bhbj48L3RleHQ+CiAgICAgIDxnCiAgICAgICAgIGlkPSJnMjk5NSIKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoLTEsMCwwLDEsMzcxOS45NjQ5LC0zLjI0MjA4NTUpIj4KICAgICAgICA8cGF0aAogICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0ic3NzY2NjY2Nzc3NjY3MiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50Mjk5OSk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjE0LjYwNTMxMDQ0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgICAgICBkPSJtIDE2ODEuMTkyMiwxNzkyLjU3NyBjIDI5Ljc1MTgsMTAxLjkyMSAxMzUuNTY2OSwxNjAuMTgxOSAyMzYuMTk4MiwxMzAuMDQ4OSAxMDAuNjMxMiwtMzAuMTMzMSAxNTguMTU1MSwtMTM3LjMwNDEgMTI4LjQwMzQsLTIzOS4yMjUgLTE1LjY3OTMsLTUzLjcxMjQgLTUyLjQ3OTUsLTk1LjI4OTkgLTk4LjUxNDQsLTExOC4yMDEgbCAyMi40OTA2LC01MS43MDI1IC0xNzkuNzUyNiw5LjM4MjcgOTUuNjY3NCwxNTkuMjIyMSAzMy4yODcsLTUxLjg2NjIgYyAyOC45MTY3LDE1LjEzNTQgNTEuOTQzOCw0MS43MDcxIDYxLjg5NTMsNzUuNzk3NyAxOS4zNzQsNjYuMzY5OSAtMTguMDc5NywxMzYuMTQ5MyAtODMuNjEwMSwxNTUuNzcxNiAtNjUuNTMwNCwxOS42MjIzIC0xMzQuNDU2MiwtMTguMzAyNiAtMTUzLjgzMDMsLTg0LjY3MjYgLTUuNTQzMywtMTguOTkwMiAtNi40MTQ3LC0zOC4yNDY5IC0zLjMyMzUsLTU2LjU0MTQgbCAtNjMuOTMyMSwtMTUuMzcxNyBjIC00Ljg0OTYsMjguMjUxNCAtMy41NDUsNTguMDEyNCA1LjAyMTEsODcuMzU3NCB6IgogICAgICAgICAgIGlkPSJwYXRoMjM5NiIgLz4KICAgICAgICA8cGF0aAogICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0ic3NzY2NjY2Nzc2NjY3MiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxNC42MDUzMDk0OTtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICAgICAgZD0ibSAxNjk3LjA3MzcsMTc5My4wMTQ5IGMgMjEuOTAyNiw3NS44MDY2IDk2LjEzMzIsMTIxLjg0ODIgMTczLjQ3NjksMTIzLjIxODcgMTEyLjE0NTUsMS45ODcyIDE2OS40MDIyLC0xMTMuMDAzIDE2OS43NjkyLC0xNzMuNDIwNCAwLjYzNCwtMTA0LjM3NyAtNzYuMDIyOCwtMTU1LjA0NDggLTExMS4zMjE0LC0xNzIuMjQ5OCBsIDE2LjU0MTIsLTM4LjU5MDkgLTEyOS42MDY1LDUuMTkwMiA2OS44MjcsMTE2LjczNiAyNy4xNjU5LC00NC44MjQ0IGMgNjkuMjc0NSw0Mi41MzQ3IDg3LjQ1NzMsODIuMTU3NCA4OC4yMzE0LDEzNC44MDE4IDAuNzkyMSw1My44Njk5IC00Mi42NjM3LDEzMy45Nzk1IC0xMzMuNDMwOSwxMzcuNjM4NyAtNzguMTY0MiwzLjE1MTEgLTEyMy4wMTQzLC01Mi45MTMxIC0xMzcuODcsLTEwMi43NTMyIC02LjU3NCwtMTYuMjUyMiAtNi41NzgzLC0zMy4zNjgzIC02LjE5OTcsLTQ3LjEwNjUgbCAtMzUuNjYxMSwtOC42Mzg4IGMgLTEuMDYzMSwyMS41NDcyIDIuNDMwMSw0Ni45ODkzIDkuMDc4LDY5Ljk5ODYgeiIKICAgICAgICAgICBpZD0icGF0aDIzOTYtOSIgLz4KICAgICAgPC9nPgogICAgPC9nPgogIDwvZz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE1MzI0Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZSAvPgogICAgICAgIDxjYzpsaWNlbnNlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSIiIC8+CiAgICAgICAgPGRjOmRhdGU+TW9uIE9jdCAxMCAxMzo0NDo1MiAyMDExICswMDAwPC9kYzpkYXRlPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W3dtYXllcl08L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnJpZ2h0cz4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQgTEdQTDIrPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpyaWdodHM+CiAgICAgICAgPGRjOnB1Ymxpc2hlcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQ8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnB1Ymxpc2hlcj4KICAgICAgICA8ZGM6aWRlbnRpZmllcj5GcmVlQ0FEL3NyYy9Nb2QvRHJhZnQvUmVzb3VyY2VzL2ljb25zL0RyYWZ0X1JvdGF0ZS5zdmc8L2RjOmlkZW50aWZpZXI+CiAgICAgICAgPGRjOnJlbGF0aW9uPmh0dHA6Ly93d3cuZnJlZWNhZHdlYi5vcmcvd2lraS9pbmRleC5waHA/dGl0bGU9QXJ0d29yazwvZGM6cmVsYXRpb24+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICAgIDxkYzpkZXNjcmlwdGlvbj5BbiBhcnJvdyBpbiBhIGNpcmN1bGFyIHNoYXBlIHdpdGggdGhlIGhlYWQgY3VydmluZyB0b3dhcmRzIHRoZSB0YWlsPC9kYzpkZXNjcmlwdGlvbj4KICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgIDxyZGY6QmFnPgogICAgICAgICAgICA8cmRmOmxpPmFycm93PC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+Y3VydmVkPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+cmVmcmVzaDwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPnJvdGF0ZTwvcmRmOmxpPgogICAgICAgICAgPC9yZGY6QmFnPgogICAgICAgIDwvZGM6c3ViamVjdD4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+Cjwvc3ZnPgo= """ rotateZ_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMwMDAiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0icm90YXRlWi5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzMDAyIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzM5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMzk1IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzM5NyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMzOTMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzk5IgogICAgICAgeDE9IjE5NDIuNzM4MiIKICAgICAgIHkxPSIxOTM0Ljc1NiIKICAgICAgIHgyPSIxODA5LjUwMjEiCiAgICAgICB5Mj0iMTU0Ny40MTM1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjk4NjU3ODExLDAsMCwwLjk5OTIyMDc4LDM3MDYuODY1OCwtMi42ODExNjk3KSIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMDA4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzkzLTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzk5LTEiCiAgICAgICB4MT0iMTY2OS43MzE0IgogICAgICAgeTE9IjE3MjYuMDU4NSIKICAgICAgIHgyPSIyMDY3LjE3MDIiCiAgICAgICB5Mj0iMTcyNi4wNTg1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTg2NTc4MTEsMCwwLDAuOTk5MjIwNzgsMjAuMTI5MjUxLC0yLjY4MTE2OTcpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzkzLTciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAzZGRkO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDMzOTUtNCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzOWVmMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMzk3LTAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMzkzLTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyOTk5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTg2NTc4MTEsMCwwLDAuOTk5MjIwNzgsMjAuMTI5MjUxLC0yLjY4MTE2OTcpIgogICAgICAgeDE9IjE5NDIuNzM4MiIKICAgICAgIHkxPSIxOTM0Ljc1NiIKICAgICAgIHgyPSIxODA5LjUwMjEiCiAgICAgICB5Mj0iMTU0Ny40MTM1IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMzkzLTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzM5NS03IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzM5Ny00IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjYuODUwMDk2OSIKICAgICBpbmtzY2FwZTpjeD0iLTI1LjIzNDM2OCIKICAgICBpbmtzY2FwZTpjeT0iMjkuNDY2MDgzIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczNDA1IgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9ImZhbHNlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMjk5MCIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczNDA1IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC4xMzY5MzY1LDAsMCwwLjEzNjkzNjUsLTIyMi4yMTc1NCwtMjAzLjM2NTEyKSI+CiAgICAgIDx0ZXh0CiAgICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICAgIHN0eWxlPSJmb250LXNpemU6MjM1LjY2MDI5MzU4cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpib2xkO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1hbGlnbjpzdGFydDtsaW5lLWhlaWdodDo3Ni45OTk5OTgwOSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6OC43OTUzMDQzO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7Zm9udC1mYW1pbHk6U2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlNhbnMgQm9sZCIKICAgICAgICAgeD0iMTc4NS40MDgyIgogICAgICAgICB5PSIxODAzLjEzNTUiCiAgICAgICAgIGlkPSJ0ZXh0NDQ5OSIKICAgICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9Ijc2Ljk5OTk5OCUiPjx0c3BhbgogICAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgICAgaWQ9InRzcGFuNDQ5NyIKICAgICAgICAgICB4PSIxNzg1LjQwODIiCiAgICAgICAgICAgeT0iMTgwMy4xMzU1IgogICAgICAgICAgIHN0eWxlPSJmb250LXNpemU6MjM1LjY2MDI5MzU4cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpib2xkO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1hbGlnbjpzdGFydDtsaW5lLWhlaWdodDo3Ni45OTk5OTgwOSU7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDBmZjtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6OC43OTUzMDQzO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7Zm9udC1mYW1pbHk6U2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlNhbnMgQm9sZCI+WjwvdHNwYW4+PC90ZXh0PgogICAgICA8ZwogICAgICAgICBpZD0iZzI5OTUiCiAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KC0xLDAsMCwxLDM3MTkuOTY0OSwtMy4yNDIwODU1KSI+CiAgICAgICAgPHBhdGgKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9InNzc2NjY2Njc3NzY2NzIgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDI5OTkpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxNC42MDUzMTA0NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICAgICAgZD0ibSAxNjgxLjE5MjIsMTc5Mi41NzcgYyAyOS43NTE4LDEwMS45MjEgMTM1LjU2NjksMTYwLjE4MTkgMjM2LjE5ODIsMTMwLjA0ODkgMTAwLjYzMTIsLTMwLjEzMzEgMTU4LjE1NTEsLTEzNy4zMDQxIDEyOC40MDM0LC0yMzkuMjI1IC0xNS42NzkzLC01My43MTI0IC01Mi40Nzk1LC05NS4yODk5IC05OC41MTQ0LC0xMTguMjAxIGwgMjIuNDkwNiwtNTEuNzAyNSAtMTc5Ljc1MjYsOS4zODI3IDk1LjY2NzQsMTU5LjIyMjEgMzMuMjg3LC01MS44NjYyIGMgMjguOTE2NywxNS4xMzU0IDUxLjk0MzgsNDEuNzA3MSA2MS44OTUzLDc1Ljc5NzcgMTkuMzc0LDY2LjM2OTkgLTE4LjA3OTcsMTM2LjE0OTMgLTgzLjYxMDEsMTU1Ljc3MTYgLTY1LjUzMDQsMTkuNjIyMyAtMTM0LjQ1NjIsLTE4LjMwMjYgLTE1My44MzAzLC04NC42NzI2IC01LjU0MzMsLTE4Ljk5MDIgLTYuNDE0NywtMzguMjQ2OSAtMy4zMjM1LC01Ni41NDE0IGwgLTYzLjkzMjEsLTE1LjM3MTcgYyAtNC44NDk2LDI4LjI1MTQgLTMuNTQ1LDU4LjAxMjQgNS4wMjExLDg3LjM1NzQgeiIKICAgICAgICAgICBpZD0icGF0aDIzOTYiIC8+CiAgICAgICAgPHBhdGgKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9InNzc2NjY2Njc3NjY2NzIgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MTQuNjA1MzA5NDk7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICAgIGQ9Im0gMTY5Ny4wNzM3LDE3OTMuMDE0OSBjIDIxLjkwMjYsNzUuODA2NiA5Ni4xMzMyLDEyMS44NDgyIDE3My40NzY5LDEyMy4yMTg3IDExMi4xNDU1LDEuOTg3MiAxNjkuNDAyMiwtMTEzLjAwMyAxNjkuNzY5MiwtMTczLjQyMDQgMC42MzQsLTEwNC4zNzcgLTc2LjAyMjgsLTE1NS4wNDQ4IC0xMTEuMzIxNCwtMTcyLjI0OTggbCAxNi41NDEyLC0zOC41OTA5IC0xMjkuNjA2NSw1LjE5MDIgNjkuODI3LDExNi43MzYgMjcuMTY1OSwtNDQuODI0NCBjIDY5LjI3NDUsNDIuNTM0NyA4Ny40NTczLDgyLjE1NzQgODguMjMxNCwxMzQuODAxOCAwLjc5MjEsNTMuODY5OSAtNDIuNjYzNywxMzMuOTc5NSAtMTMzLjQzMDksMTM3LjYzODcgLTc4LjE2NDIsMy4xNTExIC0xMjMuMDE0MywtNTIuOTEzMSAtMTM3Ljg3LC0xMDIuNzUzMiAtNi41NzQsLTE2LjI1MjIgLTYuNTc4MywtMzMuMzY4MyAtNi4xOTk3LC00Ny4xMDY1IGwgLTM1LjY2MTEsLTguNjM4OCBjIC0xLjA2MzEsMjEuNTQ3MiAyLjQzMDEsNDYuOTg5MyA5LjA3OCw2OS45OTg2IHoiCiAgICAgICAgICAgaWQ9InBhdGgyMzk2LTkiIC8+CiAgICAgIDwvZz4KICAgIDwvZz4KICA8L2c+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNTMyNCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8Y2M6bGljZW5zZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iIiAvPgogICAgICAgIDxkYzpkYXRlPk1vbiBPY3QgMTAgMTM6NDQ6NTIgMjAxMSArMDAwMDwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlt3bWF5ZXJdPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL0RyYWZ0L1Jlc291cmNlcy9pY29ucy9EcmFmdF9Sb3RhdGUuc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgICA8ZGM6ZGVzY3JpcHRpb24+QW4gYXJyb3cgaW4gYSBjaXJjdWxhciBzaGFwZSB3aXRoIHRoZSBoZWFkIGN1cnZpbmcgdG93YXJkcyB0aGUgdGFpbDwvZGM6ZGVzY3JpcHRpb24+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5hcnJvdzwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmN1cnZlZDwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPnJlZnJlc2g8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5yb3RhdGU8L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgo8L3N2Zz4K """ putX_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI2ODIiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0icHV0WC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyNjg0Ij4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzU5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzU5NSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzN2RjYTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNTk3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzU5MyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMzNTQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIzMzAuNjM3OTEiCiAgICAgICBjeT0iMzkuOTYyNzA0IgogICAgICAgZng9IjMzMC42Mzc5MSIKICAgICAgIGZ5PSIzOS45NjI3MDQiCiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTMyNzY2MywwLDAsMC45MzI3NjYzLC0yOTguMTU2NTEsOC4xOTEzMzgxKSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NCI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2NiIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNjkwIiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MjQwMSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjk1OTMzNywwLjA1MTc5OTk0LDAsMC43MzUyMzI1LC0yOS42MTA5MDgsLTEuMjMxNDEzKSIKICAgICAgIGN4PSI1MS4xMDU0OTkiCiAgICAgICBjeT0iMjMuODA3NDA3IgogICAgICAgZng9IjUxLjEwNTQ5OSIKICAgICAgIGZ5PSIyMy44MDc0MDciCiAgICAgICByPSIxOS41NzE0MjgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4NjQiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyNDA0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuMjk5MzY3MSwtMS41NzU3MjU4ZS0yLDguNDE2MTA0NGUtMywwLjk4NTA5NzksLTk0LjM1NDIwOCwtMTAuOTk4Mzg3KSIKICAgICAgIGN4PSI0OC4yODgwNjciCiAgICAgICBjeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgZnk9IjQ2Ljc0NjE0IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNTkzIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzM3NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjkzMjc2NjMsMCwwLDAuOTMyNzY2MywtMjY3LjE2MzIzLDEyLjUxNTk4MSkiCiAgICAgICBjeD0iMzE3LjY4MTczIgogICAgICAgY3k9IjM1LjIyNzI3NiIKICAgICAgIGZ4PSIzMTcuNjgxNzMiCiAgICAgICBmeT0iMzUuMjI3Mjc2IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTciCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyNDA0LTEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4yOTkzNjcxLC0wLjAxNTc1NzI2LDAuMDA4NDE2MSwwLjk4NTA5NzksLTk0LjM1NDIwOCwtMTAuOTk4Mzg3KSIKICAgICAgIGN4PSI0OC4yODgwNjciCiAgICAgICBjeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgZnk9IjQ2Ljc0NjE0IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODY0LTciPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjYtNCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4LTAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMjc5NTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgY3k9IjQ2Ljc0NjE0IgogICAgICAgY3g9IjQ4LjI4ODA2NyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4yOTkzNjcxLC0wLjAxNTc1NzI2LDAuMDA4NDE2MSwwLjk4NTA5NzksLTk0Ljg2Nzg0NSw2Ljg5MzE4MSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwMTQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTciCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODM3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0icm90YXRlKDg5Ljg2Mjk0MiwxMi4wMzU5MjQsLTEyLjExMDE2OCkiCiAgICAgICB4MT0iNTEuOTczODI3IgogICAgICAgeTE9IjM1Ljk3ODQxNiIKICAgICAgIHgyPSIzMy45ODU4NCIKICAgICAgIHkyPSIzMC45MzUzNzciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI5LjY4NzUiCiAgICAgaW5rc2NhcGU6Y3g9IjMyIgogICAgIGlua3NjYXBlOmN5PSIzMiIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJnMzM4MSIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjU2MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzYxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDQwIgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI2ODciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8ZGM6ZGF0ZT4yMDEyLTExLTI1PC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL1BhcnQvR3VpL1Jlc291cmNlcy9pY29ucy9QYXJ0X09mZnNldC5zdmc8L2RjOmlkZW50aWZpZXI+CiAgICAgICAgPGRjOnJpZ2h0cz4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkZyZWVDQUQgTEdQTDIrPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpyaWdodHM+CiAgICAgICAgPGNjOmxpY2Vuc2U+aHR0cHM6Ly93d3cuZ251Lm9yZy9jb3B5bGVmdC9sZXNzZXIuaHRtbDwvY2M6bGljZW5zZT4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIj4KICAgIDxnCiAgICAgICBpZD0iZzMzODEiPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0iZmlsbDojODA4MDAwO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxLjQ1Mzg0NjE7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAyLjIsNDUuOTA3NjkyIDE2LjczODQ2MiwzOC42Mzg0NjIgNDAsNDAuMDkyMzA4IDI1LjQ2MTUzOCw0Ny4zNjE1MzggWiIKICAgICAgICAgaWQ9InBhdGgzMDI3IgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjIiAvPgogICAgICA8ZwogICAgICAgICBpZD0iZzM4MzMiCiAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEsLTAuMDU4ODIzNTMsMCwxLDUwLjQ1Njk5LDQuNTg5MzI4OCkiPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICAgIGlkPSJwYXRoMzM0MyIKICAgICAgICAgICBkPSJtIC0xNC45NTY5ODgsMTguOTQyNjUzIGggLTEwIGwgMTBlLTcsLTE0IGggLTE0LjAwMDAwMSB2IDE0IGggLTEwIGwgMTcsMTggeiIKICAgICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzgzNyk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICAgIGlkPSJwYXRoMzM0My0yIgogICAgICAgICAgIGQ9Im0gLTE5LjYzODI4MiwyMC45NDI2NTMgaCAtNy4zMTg3MDYgbCAtMWUtNiwtMTQgaCAtOS45OTk5OTkgdiAxNCBoIC03LjM5NTI2MSBsIDEyLjM5NTI2MSwxMyB6IgogICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8L2c+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiM3MjlmY2Y7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0ibSAyLjIsNTkgMjAsLTEwIDMyLDIgLTIwLDEwIHoiCiAgICAgICAgIGlkPSJwYXRoMzAyNy0zIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjIiAvPgogICAgICA8ZwogICAgICAgICBpZD0iZzMxNjAiCiAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM4LjIwNjE4LDIuMDc2NTc0KSI+CiAgICAgICAgPHBhdGgKICAgICAgICAgICBpZD0icGF0aDMxNjIiCiAgICAgICAgICAgZD0ibSAxNS41OTA1NTEsMTEuNzYzNzggNy45MzcwMDgsMTEuNjU3NDggLTYuMTI5OTIxLDAgLTUuMzUwMzk0LC03Ljc5NTI3NSAtNS4yNzk1Mjc1LDcuNzk1Mjc1IC02LjE2NTM1NDMsMCA3LjkwMTU3NDcsLTExLjY1NzQ4IC03LjYxODExMDE0LC0xMS4xNjE0MTc3OCA2LjE2NTM1NDI0LDAgNC45OTYwNjMsNy4zMzQ2NDU3OCA0Ljk5NjA2MywtNy4zMzQ2NDU3OCA2LjE2NTM1NCwwIHoiCiAgICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6I2Q0MDAwMDtzdHJva2Utd2lkdGg6MS4xNjkyOTEzOHB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPC9nPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg== """ putY_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI2ODIiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0icHV0WC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyNjg0Ij4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzU5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzU5NSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzN2RjYTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNTk3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzU5MyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMzNTQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIzMzAuNjM3OTEiCiAgICAgICBjeT0iMzkuOTYyNzA0IgogICAgICAgZng9IjMzMC42Mzc5MSIKICAgICAgIGZ5PSIzOS45NjI3MDQiCiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTMyNzY2MywwLDAsMC45MzI3NjYzLC0yOTguMTU2NTEsOC4xOTEzMzgxKSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NCI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2NiIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNjkwIiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MjQwMSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjk1OTMzNywwLjA1MTc5OTk0LDAsMC43MzUyMzI1LC0yOS42MTA5MDgsLTEuMjMxNDEzKSIKICAgICAgIGN4PSI1MS4xMDU0OTkiCiAgICAgICBjeT0iMjMuODA3NDA3IgogICAgICAgZng9IjUxLjEwNTQ5OSIKICAgICAgIGZ5PSIyMy44MDc0MDciCiAgICAgICByPSIxOS41NzE0MjgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4NjQiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyNDA0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuMjk5MzY3MSwtMS41NzU3MjU4ZS0yLDguNDE2MTA0NGUtMywwLjk4NTA5NzksLTk0LjM1NDIwOCwtMTAuOTk4Mzg3KSIKICAgICAgIGN4PSI0OC4yODgwNjciCiAgICAgICBjeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgZnk9IjQ2Ljc0NjE0IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNTkzIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzM3NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjkzMjc2NjMsMCwwLDAuOTMyNzY2MywtMjY3LjE2MzIzLDEyLjUxNTk4MSkiCiAgICAgICBjeD0iMzE3LjY4MTczIgogICAgICAgY3k9IjM1LjIyNzI3NiIKICAgICAgIGZ4PSIzMTcuNjgxNzMiCiAgICAgICBmeT0iMzUuMjI3Mjc2IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTciCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyNDA0LTEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4yOTkzNjcxLC0wLjAxNTc1NzI2LDAuMDA4NDE2MSwwLjk4NTA5NzksLTk0LjM1NDIwOCwtMTAuOTk4Mzg3KSIKICAgICAgIGN4PSI0OC4yODgwNjciCiAgICAgICBjeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgZnk9IjQ2Ljc0NjE0IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODY0LTciPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjYtNCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4LTAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMjc5NTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgY3k9IjQ2Ljc0NjE0IgogICAgICAgY3g9IjQ4LjI4ODA2NyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4yOTkzNjcxLC0wLjAxNTc1NzI2LDAuMDA4NDE2MSwwLjk4NTA5NzksLTk0Ljg2Nzg0NSw2Ljg5MzE4MSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwMTQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTciCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODM3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0icm90YXRlKDg5Ljg2Mjk0MiwxMi4wMzU5MjQsLTEyLjExMDE2OCkiCiAgICAgICB4MT0iNTEuOTczODI3IgogICAgICAgeTE9IjM1Ljk3ODQxNiIKICAgICAgIHgyPSIzMy45ODU4NCIKICAgICAgIHkyPSIzMC45MzUzNzciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI5LjY4NzUiCiAgICAgaW5rc2NhcGU6Y3g9IjE0LjI5Njc3NCIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0iZzMzODEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTM2MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMzA0MCIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyNjg3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W3dtYXllcl08L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmRhdGU+MjAxMi0xMS0yNTwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9QYXJ0L0d1aS9SZXNvdXJjZXMvaWNvbnMvUGFydF9PZmZzZXQuc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxjYzpsaWNlbnNlPmh0dHBzOi8vd3d3LmdudS5vcmcvY29weWxlZnQvbGVzc2VyLmh0bWw8L2NjOmxpY2Vuc2U+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczMzgxIj4KICAgICAgPHRleHQKICAgICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICAgIHN0eWxlPSJmb250LXNpemU6MzEuMzUyMDM5MzRweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0OmJvbGQ7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwODAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwNTUwMDtzdHJva2Utd2lkdGg6MS4xNzAxMTc5NztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO2Rpc3BsYXk6aW5saW5lO2ZvbnQtZmFtaWx5OkRlamFWdSBTYW5zOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246RGVqYVZ1IFNhbnMgQm9sZCIKICAgICAgICAgeD0iMzguODAwMDAzIgogICAgICAgICB5PSIyNS40OTk5OTgiCiAgICAgICAgIGlkPSJ0ZXh0NDE5OSI+PHRzcGFuCiAgICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgICBpZD0idHNwYW40MjAxIgogICAgICAgICAgIHg9IjM4LjgwMDAwMyIKICAgICAgICAgICB5PSIyNS40OTk5OTgiCiAgICAgICAgICAgc3R5bGU9InN0cm9rZTojMDA1NTAwO3N0cm9rZS13aWR0aDoxLjE3MDExNzk3O3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lIj5ZPC90c3Bhbj48L3RleHQ+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiM4MDgwMDA7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjEuNDUzODQ2MTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIDIuMiw0NS45MDc2OTIgMTYuNzM4NDYyLDM4LjYzODQ2MiA0MCw0MC4wOTIzMDggMjUuNDYxNTM4LDQ3LjM2MTUzOCBaIgogICAgICAgICBpZD0icGF0aDMwMjciCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICAgIDxnCiAgICAgICAgIGlkPSJnMzgzMyIKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMSwtMC4wNTg4MjM1MywwLDEsNTAuNDU2OTksNC41ODkzMjg4KSI+CiAgICAgICAgPHBhdGgKICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgICAgaWQ9InBhdGgzMzQzIgogICAgICAgICAgIGQ9Im0gLTE0Ljk1Njk4OCwxOC45NDI2NTMgaCAtMTAgbCAxMGUtNywtMTQgaCAtMTQuMDAwMDAxIHYgMTQgaCAtMTAgbCAxNywxOCB6IgogICAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzODM3KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgICAgPHBhdGgKICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgICAgaWQ9InBhdGgzMzQzLTIiCiAgICAgICAgICAgZD0ibSAtMTkuNjM4MjgyLDIwLjk0MjY1MyBoIC03LjMxODcwNiBsIC0xZS02LC0xNCBoIC05Ljk5OTk5OSB2IDE0IGggLTcuMzk1MjYxIGwgMTIuMzk1MjYxLDEzIHoiCiAgICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDwvZz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzcyOWZjZjtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJtIDIuMiw1OSAyMCwtMTAgMzIsMiAtMjAsMTAgeiIKICAgICAgICAgaWQ9InBhdGgzMDI3LTMiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K """ putZ_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI2ODIiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0icHV0WS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyNjg0Ij4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzU5MyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjOGUwZjk7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzU5NSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzYzN2RjYTtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzNTk3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzU5MyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMzNTQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSIzMzAuNjM3OTEiCiAgICAgICBjeT0iMzkuOTYyNzA0IgogICAgICAgZng9IjMzMC42Mzc5MSIKICAgICAgIGZ5PSIzOS45NjI3MDQiCiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTMyNzY2MywwLDAsMC45MzI3NjYzLC0yOTguMTU2NTEsOC4xOTEzMzgxKSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NCI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2NiIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNjkwIiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MjQwMSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjk1OTMzNywwLjA1MTc5OTk0LDAsMC43MzUyMzI1LC0yOS42MTA5MDgsLTEuMjMxNDEzKSIKICAgICAgIGN4PSI1MS4xMDU0OTkiCiAgICAgICBjeT0iMjMuODA3NDA3IgogICAgICAgZng9IjUxLjEwNTQ5OSIKICAgICAgIGZ5PSIyMy44MDc0MDciCiAgICAgICByPSIxOS41NzE0MjgiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4NjQiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyNDA0IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuMjk5MzY3MSwtMS41NzU3MjU4ZS0yLDguNDE2MTA0NGUtMywwLjk4NTA5NzksLTk0LjM1NDIwOCwtMTAuOTk4Mzg3KSIKICAgICAgIGN4PSI0OC4yODgwNjciCiAgICAgICBjeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgZnk9IjQ2Ljc0NjE0IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNTkzIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzM3NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjkzMjc2NjMsMCwwLDAuOTMyNzY2MywtMjY3LjE2MzIzLDEyLjUxNTk4MSkiCiAgICAgICBjeD0iMzE3LjY4MTczIgogICAgICAgY3k9IjM1LjIyNzI3NiIKICAgICAgIGZ4PSIzMTcuNjgxNzMiCiAgICAgICBmeT0iMzUuMjI3Mjc2IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTciCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyNDA0LTEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4yOTkzNjcxLC0wLjAxNTc1NzI2LDAuMDA4NDE2MSwwLjk4NTA5NzksLTk0LjM1NDIwOCwtMTAuOTk4Mzg3KSIKICAgICAgIGN4PSI0OC4yODgwNjciCiAgICAgICBjeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgZnk9IjQ2Ljc0NjE0IgogICAgICAgcj0iMTkuNTcxNDI4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODY0LTciPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDM4NjYtNCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4LTAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMjc5NTtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICByPSIxOS41NzE0MjgiCiAgICAgICBmeT0iNDYuNzQ2MTQiCiAgICAgICBmeD0iNDguMjg4MDY3IgogICAgICAgY3k9IjQ2Ljc0NjE0IgogICAgICAgY3g9IjQ4LjI4ODA2NyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4yOTkzNjcxLC0wLjAxNTc1NzI2LDAuMDA4NDE2MSwwLjk4NTA5NzksLTk0Ljg2Nzg0NSw2Ljg5MzE4MSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwMTQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODY0LTciCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODM3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0icm90YXRlKDg5Ljg2Mjk0MiwxMi4wMzU5MjQsLTEyLjExMDE2OCkiCiAgICAgICB4MT0iNTEuOTczODI3IgogICAgICAgeTE9IjM1Ljk3ODQxNiIKICAgICAgIHgyPSIzMy45ODU4NCIKICAgICAgIHkyPSIzMC45MzUzNzciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI5LjY4NzUiCiAgICAgaW5rc2NhcGU6Y3g9IjE0LjI5Njc3NCIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0iZzMzODEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTM2MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMzA0MCIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyNjg3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W3dtYXllcl08L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmRhdGU+MjAxMi0xMS0yNTwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9QYXJ0L0d1aS9SZXNvdXJjZXMvaWNvbnMvUGFydF9PZmZzZXQuc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxjYzpsaWNlbnNlPmh0dHBzOi8vd3d3LmdudS5vcmcvY29weWxlZnQvbGVzc2VyLmh0bWw8L2NjOmxpY2Vuc2U+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczMzgxIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzgwODAwMDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS40NTM4NDYxO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Ik0gMi4yLDQ1LjkwNzY5MiAxNi43Mzg0NjIsMzguNjM4NDYyIDQwLDQwLjA5MjMwOCAyNS40NjE1MzgsNDcuMzYxNTM4IFoiCiAgICAgICAgIGlkPSJwYXRoMzAyNyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgICAgPGcKICAgICAgICAgaWQ9ImczODMzIgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLC0wLjA1ODgyMzUzLDAsMSw1MC40NTY5OSw0LjU4OTMyODgpIj4KICAgICAgICA8cGF0aAogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgICBpZD0icGF0aDMzNDMiCiAgICAgICAgICAgZD0ibSAtMTQuOTU2OTg4LDE4Ljk0MjY1MyBoIC0xMCBsIDEwZS03LC0xNCBoIC0xNC4wMDAwMDEgdiAxNCBoIC0xMCBsIDE3LDE4IHoiCiAgICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM4MzcpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgICA8cGF0aAogICAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgICBpZD0icGF0aDMzNDMtMiIKICAgICAgICAgICBkPSJtIC0xOS42MzgyODIsMjAuOTQyNjUzIGggLTcuMzE4NzA2IGwgLTFlLTYsLTE0IGggLTkuOTk5OTk5IHYgMTQgaCAtNy4zOTUyNjEgbCAxMi4zOTUyNjEsMTMgeiIKICAgICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPC9nPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0iZmlsbDojNzI5ZmNmO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Im0gMi4yLDU5IDIwLC0xMCAzMiwyIC0yMCwxMCB6IgogICAgICAgICBpZD0icGF0aDMwMjctMyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgICAgPHRleHQKICAgICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgICAgc3R5bGU9ImZvbnQtc2l6ZTozMi4yNzA0OTYzN3B4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6Ym9sZDtmb250LXN0cmV0Y2g6bm9ybWFsO3RleHQtYWxpZ246c3RhcnQ7bGluZS1oZWlnaHQ6NzYuOTk5OTk4MDklO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O3dyaXRpbmctbW9kZTpsci10Yjt0ZXh0LWFuY2hvcjpzdGFydDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuMjA0Mzk4MTY7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtmb250LWZhbWlseTpTYW5zOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246U2FucyBCb2xkIgogICAgICAgICB4PSI0MSIKICAgICAgICAgeT0iMjUuNjk5OTk5IgogICAgICAgICBpZD0idGV4dDQ0OTkiCiAgICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSI3Ni45OTk5OTglIj48dHNwYW4KICAgICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICAgIGlkPSJ0c3BhbjQ0OTciCiAgICAgICAgICAgeD0iNDEiCiAgICAgICAgICAgeT0iMjUuNjk5OTk5IgogICAgICAgICAgIHN0eWxlPSJmb250LXNpemU6MzIuMjcwNDk2MzdweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0OmJvbGQ7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWFsaWduOnN0YXJ0O2xpbmUtaGVpZ2h0Ojc2Ljk5OTk5ODA5JTt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMGZmO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjIwNDM5ODE2O3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7Zm9udC1mYW1pbHk6U2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlNhbnMgQm9sZCI+WjwvdHNwYW4+PC90ZXh0PgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg== """ centerX_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMxMTEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iY2VudGVyWC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzMTEzIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg0MSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwNjE5YzA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg0MyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM3OWNmYjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg0MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDY2NTUiCiAgICAgICB4MT0iMTU1LjQ2ODc1IgogICAgICAgeTE9IjI1NDUuMjE4OCIKICAgICAgIHgyPSI3MTMuMDYyNSIKICAgICAgIHkyPSIyNTQ1LjIxODgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwtMikiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMzExOSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLC0xLjQ1MDAwMDEsMS40NzA1ODgyLDAsLTE1LjA1ODgyLDkxLjQ1KSIKICAgICAgIHkyPSIzNi4wNzk5OTgiCiAgICAgICB4Mj0iMjEuNjg5NjUzIgogICAgICAgeTE9IjI5LjI3OTk5OSIKICAgICAgIHgxPSI1Ni4xNzI0MDkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMzYiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODk3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiwyMC4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsMzUuMjA1ODgzKSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAyNS0zIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTYiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzIwNGE4NztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODk5LTUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCwyMC4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDY2IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTAwIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDIyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NS02LTQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNy0wIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNS05IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDY2LTQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzOSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA0MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzIwNGE4NztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMDQzIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1LTgiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtOCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny0yIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTUyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE1NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzOCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCw2LjIwNTg4MykiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgeTE9IjI0LjM3OTMwOSIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQwIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDYuMjA1ODgzKSIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00LTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtNi00LTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNy0wLTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS01LTktNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOC0wIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTgtMCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny0yLTkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS00LTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgtMCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwOTgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQtMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxMDAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTAyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQtMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxMTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04LTAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTEyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDYuMjA1ODgzKSIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI5LjY4NzUiCiAgICAgaW5rc2NhcGU6Y3g9Ii0zLjQwNjQ1MTgiCiAgICAgaW5rc2NhcGU6Y3k9IjMyIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjU2MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzYxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQzMDQ2IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMzA0OCIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIKICAgICAgIHNwYWNpbmd4PSIxNiIKICAgICAgIHNwYWNpbmd5PSIxNiIKICAgICAgIG9yaWdpbng9IjAiCiAgICAgICBvcmlnaW55PSIwIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIj4KICAgIDxnCiAgICAgICBpZD0iZzMxNjAiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzOC44ODc3OTUsMS45OTQwOTQzKSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzE2MiIKICAgICAgICAgZD0ibSAxNS41OTA1NTEsMTEuNzYzNzggNy45MzcwMDgsMTEuNjU3NDggLTYuMTI5OTIxLDAgLTUuMzUwMzk0LC03Ljc5NTI3NSAtNS4yNzk1Mjc1LDcuNzk1Mjc1IC02LjE2NTM1NDMsMCA3LjkwMTU3NDcsLTExLjY1NzQ4IC03LjYxODExMDE0LC0xMS4xNjE0MTc3OCA2LjE2NTM1NDI0LDAgNC45OTYwNjMsNy4zMzQ2NDU3OCA0Ljk5NjA2MywtNy4zMzQ2NDU3OCA2LjE2NTM1NCwwIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZjAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiNkNDAwMDA7c3Ryb2tlLXdpZHRoOjEuMTY5MjkxMzhweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPC9nPgogICAgPGcKICAgICAgIGlkPSJnMzAxNSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDEsMCwwLDAuNzI5LDAsMTkuNTcyKSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoMzA4NyIKICAgICAgICAgZD0iTSAzMiw1NiAzMiw4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDo4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDMwODctNiIKICAgICAgICAgZD0iTSAzMiw1NiAzMiw4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTp1cmwoI2xpbmVhckdyYWRpZW50MzEwMik7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoMzA4Ny02LTIiCiAgICAgICAgIGQ9Ik0gMzEsNTYgMzEsOCIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgIDwvZz4KICAgIDxnCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgtMS4wNjEyMDgsMCwwLDEuMDYxMjA4LDEwNi4yODQ1NiwxNC4xODQ0MzIpIgogICAgICAgaWQ9ImczMDExIj4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMiCiAgICAgICAgIGQ9Im0gNDksMTkgMCw4IC04LDAgMCwxMCA4LDAgMCw4IDEyLC0xMyB6IgogICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzExMik7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMiIKICAgICAgICAgZD0iTSA1MS4wNDA3NDgsMjQuNDg3MDIxIDUxLDI5IGwgLTgsMCAwLDYgOCwwIDAuMDE3MDUsNS4xMzA2ODIgNy4yNjQxOTEsLTguMTA2NTc2IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDwvZz4KICAgIDxnCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgtMS4wNjEyMDgsMCwwLDEuMDYxMjA4LDI1LjYzMjc1MiwxNC4xODQ0MzIpIgogICAgICAgaWQ9ImczMDA3Ij4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMyIKICAgICAgICAgZD0ibSAxNSwxOSAwLDggOCwwIDAsMTAgLTgsMCAwLDggTCAzLDMyIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMTEwKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0yLTUiCiAgICAgICAgIGQ9Ik0gMTIuOTU5MjUyLDI0LjQ4NzAyMSAxMywyOSBsIDgsMCAwLDYgLTgsMCAtMC4wMTcwNSw1LjEzMDY4MiAtNy4yNjQxOTEsLTguMTA2NTc2IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDwvZz4KICA8L2c+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNTc4MSI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8Y2M6bGljZW5zZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iIiAvPgogICAgICAgIDxkYzpkYXRlPk1vbiBPY3QgMTAgMTM6NDQ6NTIgMjAxMSArMDAwMDwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlt3bWF5ZXJdPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL0RyYWZ0L1Jlc291cmNlcy9pY29ucy9EcmFmdF9UcmltZXguc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgIDxyZGY6QmFnPgogICAgICAgICAgICA8cmRmOmxpPmFycm93PC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+YXJyb3dzPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+bGluZTwvcmRmOmxpPgogICAgICAgICAgPC9yZGY6QmFnPgogICAgICAgIDwvZGM6c3ViamVjdD4KICAgICAgICA8ZGM6ZGVzY3JpcHRpb24+QSB2ZXJ0aWNhbCBsaW5lIHdpdGggYW4gYXJyb3cgcG9pbnRpbmcgYXdheSBmcm9tIGl0IG9uIGVhY2ggc2lkZTwvZGM6ZGVzY3JpcHRpb24+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgo8L3N2Zz4K """ centerY_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMxMTEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iY2VudGVyWi5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzMTEzIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg0MSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwNjE5YzA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg0MyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM3OWNmYjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg0MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDY2NTUiCiAgICAgICB4MT0iMTU1LjQ2ODc1IgogICAgICAgeTE9IjI1NDUuMjE4OCIKICAgICAgIHgyPSI3MTMuMDYyNSIKICAgICAgIHkyPSIyNTQ1LjIxODgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwtMikiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMzExOSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLC0xLjQ1MDAwMDEsMS40NzA1ODgyLDAsLTE1LjA1ODgyLDkxLjQ1KSIKICAgICAgIHkyPSIzNi4wNzk5OTgiCiAgICAgICB4Mj0iMjEuNjg5NjUzIgogICAgICAgeTE9IjI5LjI3OTk5OSIKICAgICAgIHgxPSI1Ni4xNzI0MDkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMzYiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODk3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiwyMC4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsMzUuMjA1ODgzKSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAyNS0zIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTYiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzIwNGE4NztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODk5LTUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCwyMC4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDY2IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTAwIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDIyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NS02LTQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNy0wIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNS05IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDY2LTQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzOSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA0MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzIwNGE4NztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMDQzIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1LTgiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtOCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny0yIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTUyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE1NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzOCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCw2LjIwNTg4MykiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgeTE9IjI0LjM3OTMwOSIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQwIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDYuMjA1ODgzKSIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00LTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtNi00LTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNy0wLTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS01LTktNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOC0wIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTgtMCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny0yLTkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS00LTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgtMCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwOTgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQtMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxMDAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTAyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQtMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxMTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04LTAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTEyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDYuMjA1ODgzKSIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI5LjY4NzUiCiAgICAgaW5rc2NhcGU6Y3g9Ii0zLjQwNjQ1MTgiCiAgICAgaW5rc2NhcGU6Y3k9IjMyIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjU2MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzYxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBzaG93Z3VpZGVzPSJ0cnVlIgogICAgIGlua3NjYXBlOmd1aWRlLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMzA0NiIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDMwNDgiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiCiAgICAgICBzcGFjaW5neD0iMTYiCiAgICAgICBzcGFjaW5neT0iMTYiCiAgICAgICBvcmlnaW54PSIwIgogICAgICAgb3JpZ2lueT0iMCIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTozMS4zNTIwMzkzNHB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6Ym9sZDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDA4MDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDA1NTAwO3N0cm9rZS13aWR0aDoxLjE3MDExNzk3O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7ZGlzcGxheTppbmxpbmU7Zm9udC1mYW1pbHk6RGVqYVZ1IFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpEZWphVnUgU2FucyBCb2xkIgogICAgICAgeD0iMzkuNjA2MTEiCiAgICAgICB5PSIyNS40NDA4MTkiCiAgICAgICBpZD0idGV4dDQxOTkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MjAxIgogICAgICAgICB4PSIzOS42MDYxMSIKICAgICAgICAgeT0iMjUuNDQwODE5IgogICAgICAgICBzdHlsZT0ic3Ryb2tlOiMwMDU1MDA7c3Ryb2tlLXdpZHRoOjEuMTcwMTE3OTc7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiPlk8L3RzcGFuPjwvdGV4dD4KICAgIDxnCiAgICAgICBpZD0iZzMwMTUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwwLjcyOSwwLDE5LjU3MikiPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDMwODciCiAgICAgICAgIGQ9Ik0gMzIsNTYgMzIsOCIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6ODtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgzMDg3LTYiCiAgICAgICAgIGQ9Ik0gMzIsNTYgMzIsOCIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDMxMDIpO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBpZD0icGF0aDMwODctNi0yIgogICAgICAgICBkPSJNIDMxLDU2IDMxLDgiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICA8L2c+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoLTEuMDYxMjA4LDAsMCwxLjA2MTIwOCwxMDYuMjg0NTYsMTQuMTg0NDMyKSIKICAgICAgIGlkPSJnMzAxMSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzIgogICAgICAgICBkPSJtIDQ5LDE5IDAsOCAtOCwwIDAsMTAgOCwwIDAsOCAxMiwtMTMgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMxMTIpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzLTIiCiAgICAgICAgIGQ9Ik0gNTEuMDQwNzQ4LDI0LjQ4NzAyMSA1MSwyOSBsIC04LDAgMCw2IDgsMCAwLjAxNzA1LDUuMTMwNjgyIDcuMjY0MTkxLC04LjEwNjU3NiB6IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2c+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoLTEuMDYxMjA4LDAsMCwxLjA2MTIwOCwyNS42MzI3NTIsMTQuMTg0NDMyKSIKICAgICAgIGlkPSJnMzAwNyI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzLTMiCiAgICAgICAgIGQ9Im0gMTUsMTkgMCw4IDgsMCAwLDEwIC04LDAgMCw4IEwgMywzMiB6IgogICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzExMCk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjQuMTY4Mzg5OCIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL2NoYW5nZXByb3AucG5nIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDMzNDMtMi01IgogICAgICAgICBkPSJNIDEyLjk1OTI1MiwyNC40ODcwMjEgMTMsMjkgbCA4LDAgMCw2IC04LDAgLTAuMDE3MDUsNS4xMzA2ODIgLTcuMjY0MTkxLC04LjEwNjU3NiB6IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2c+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTU3ODEiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9IiIgLz4KICAgICAgICA8ZGM6ZGF0ZT5Nb24gT2N0IDEwIDEzOjQ0OjUyIDIwMTEgKzAwMDA8L2RjOmRhdGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9EcmFmdC9SZXNvdXJjZXMvaWNvbnMvRHJhZnRfVHJpbWV4LnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5hcnJvdzwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmFycm93czwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmxpbmU8L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgICAgPGRjOmRlc2NyaXB0aW9uPkEgdmVydGljYWwgbGluZSB3aXRoIGFuIGFycm93IHBvaW50aW5nIGF3YXkgZnJvbSBpdCBvbiBlYWNoIHNpZGU8L2RjOmRlc2NyaXB0aW9uPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KPC9zdmc+Cg== """ centerZ_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzMxMTEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iY2VudGVyWC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzMTEzIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg0MSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwNjE5YzA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg0MyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM3OWNmYjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg0MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDY2NTUiCiAgICAgICB4MT0iMTU1LjQ2ODc1IgogICAgICAgeTE9IjI1NDUuMjE4OCIKICAgICAgIHgyPSI3MTMuMDYyNSIKICAgICAgIHkyPSIyNTQ1LjIxODgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwtMikiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMzExOSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLC0xLjQ1MDAwMDEsMS40NzA1ODgyLDAsLTE1LjA1ODgyLDkxLjQ1KSIKICAgICAgIHkyPSIzNi4wNzk5OTgiCiAgICAgICB4Mj0iMjEuNjg5NjUzIgogICAgICAgeTE9IjI5LjI3OTk5OSIKICAgICAgIHgxPSI1Ni4xNzI0MDkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwMzYiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODk3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiwyMC4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NSIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsMzUuMjA1ODgzKSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAyNS0zIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTYiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzIwNGE4NztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODk5LTUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB5Mj0iMzkuNTE0MTAzIgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCwyMC4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDY2IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzOTAwIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDIyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg5NS02LTQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNy0wIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNS05IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDY2LTQiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTYtNCIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzOSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA0MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzIwNGE4NztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMDQzIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjM5LjUxNDEwMyIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDI1LTgiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtOCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny0yIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjA0YTg3O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4OTktNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxNTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTUyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzE1NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzAzOCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSw1MS40NjMxOCw2LjIwNTg4MykiCiAgICAgICB4MT0iNTAuMTIwNzgxIgogICAgICAgeTE9IjI0LjM3OTMwOSIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQwIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDYuMjA1ODgzKSIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNDEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjMwIgogICAgICAgeTE9IjYiCiAgICAgICB4Mj0iMzQiCiAgICAgICB5Mj0iNTciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtNi00LTEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDUxLjQ2MzE4LDYuMjA1ODgzKSIKICAgICAgIHgxPSI1MC4xMjA3ODEiCiAgICAgICB5MT0iMjQuMzc5MzA5IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4OTUtNi00LTEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzI5ZmNmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4OTctNy0wLTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS01LTktNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4OTUtOC0wIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0NCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjgyNzI2NTI2LDAsMCwwLjg1Mjk0MTIxLDEyLjUzNjgxNiw2LjIwNTg4MykiCiAgICAgICB4MT0iNDIuNzU4MDc2IgogICAgICAgeTE9IjIzLjUyNjY0NCIKICAgICAgIHgyPSI0NS42MTUyNDYiCiAgICAgICB5Mj0iMzkuNTE0MTAzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODk1LTgtMCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg5Ny0yLTkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODc7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg5OS00LTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODk1LTgtMCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwOTgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44MjcyNjUyNiwwLDAsMC44NTI5NDEyMSwxMi41MzY4MTYsNi4yMDU4ODMpIgogICAgICAgeDE9IjQyLjc1ODA3NiIKICAgICAgIHkxPSIyMy41MjY2NDQiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQtMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxMDAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTAyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iMzAiCiAgICAgICB5MT0iNiIKICAgICAgIHgyPSIzNCIKICAgICAgIHkyPSI1NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS02LTQtMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMxMTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsNTEuNDYzMTgsNi4yMDU4ODMpIgogICAgICAgeDE9IjUwLjEyMDc4MSIKICAgICAgIHkxPSIyNC4zNzkzMDkiCiAgICAgICB4Mj0iNDUuNjE1MjQ2IgogICAgICAgeTI9IjM5LjUxNDEwMyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg5NS04LTAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTEyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuODI3MjY1MjYsMCwwLDAuODUyOTQxMjEsMTIuNTM2ODE2LDYuMjA1ODgzKSIKICAgICAgIHgxPSI0Mi43NTgwNzYiCiAgICAgICB5MT0iMjMuNTI2NjQ0IgogICAgICAgeDI9IjQ1LjYxNTI0NiIKICAgICAgIHkyPSIzOS41MTQxMDMiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI5LjY4NzUiCiAgICAgaW5rc2NhcGU6Y3g9Ii0zLjQwNjQ1MTgiCiAgICAgaW5rc2NhcGU6Y3k9IjMyIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjU2MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzYxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBzaG93Z3VpZGVzPSJ0cnVlIgogICAgIGlua3NjYXBlOmd1aWRlLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMzA0NiIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDMwNDgiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiCiAgICAgICBzcGFjaW5neD0iMTYiCiAgICAgICBzcGFjaW5neT0iMTYiCiAgICAgICBvcmlnaW54PSIwIgogICAgICAgb3JpZ2lueT0iMCIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczMDE1IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMSwwLDAsMC43MjksMCwxOS41NzIpIj4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgzMDg3IgogICAgICAgICBkPSJNIDMyLDU2IDMyLDgiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwYjE1MjE7c3Ryb2tlLXdpZHRoOjg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoMzA4Ny02IgogICAgICAgICBkPSJNIDMyLDU2IDMyLDgiCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOnVybCgjbGluZWFyR3JhZGllbnQzMTAyKTtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgzMDg3LTYtMiIKICAgICAgICAgZD0iTSAzMSw1NiAzMSw4IgogICAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgPC9nPgogICAgPGcKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KC0xLjA2MTIwOCwwLDAsMS4wNjEyMDgsMTA2LjI4NDU2LDE0LjE4NDQzMikiCiAgICAgICBpZD0iZzMwMTEiPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0MyIKICAgICAgICAgZD0ibSA0OSwxOSAwLDggLTgsMCAwLDEwIDgsMCAwLDggMTIsLTEzIHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMTEyKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0yIgogICAgICAgICBkPSJNIDUxLjA0MDc0OCwyNC40ODcwMjEgNTEsMjkgbCAtOCwwIDAsNiA4LDAgMC4wMTcwNSw1LjEzMDY4MiA3LjI2NDE5MSwtOC4xMDY1NzYgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPC9nPgogICAgPGcKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KC0xLjA2MTIwOCwwLDAsMS4wNjEyMDgsMjUuNjMyNzUyLDE0LjE4NDQzMikiCiAgICAgICBpZD0iZzMwMDciPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpleHBvcnQteWRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNC4xNjgzODk4IgogICAgICAgICBpbmtzY2FwZTpleHBvcnQtZmlsZW5hbWU9Ii9ob21lL3lvcmlrL0RvY3VtZW50cy9MYWIvRHJhZnQvaWNvbnMvY2hhbmdlcHJvcC5wbmciCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoMzM0My0zIgogICAgICAgICBkPSJtIDE1LDE5IDAsOCA4LDAgMCwxMCAtOCwwIDAsOCBMIDMsMzIgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMxMTApO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMGIxNTIxO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC14ZHBpPSI0LjE2ODM4OTgiCiAgICAgICAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iL2hvbWUveW9yaWsvRG9jdW1lbnRzL0xhYi9EcmFmdC9pY29ucy9jaGFuZ2Vwcm9wLnBuZyIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgICAgaWQ9InBhdGgzMzQzLTItNSIKICAgICAgICAgZD0iTSAxMi45NTkyNTIsMjQuNDg3MDIxIDEzLDI5IGwgOCwwIDAsNiAtOCwwIC0wLjAxNzA1LDUuMTMwNjgyIC03LjI2NDE5MSwtOC4xMDY1NzYgeiIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPC9nPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjMyLjI3MDQ5NjM3cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpib2xkO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1hbGlnbjpzdGFydDtsaW5lLWhlaWdodDo3Ni45OTk5OTgwOSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS4yMDQzOTgxNjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO2ZvbnQtZmFtaWx5OlNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpTYW5zIEJvbGQiCiAgICAgICB4PSI0MS4yODQ0NjYiCiAgICAgICB5PSIyNS43MDIwNzYiCiAgICAgICBpZD0idGV4dDQ0OTktMSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSI3Ni45OTk5OTglIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ5Ny03IgogICAgICAgICB4PSI0MS4yODQ0NjYiCiAgICAgICAgIHk9IjI1LjcwMjA3NiIKICAgICAgICAgc3R5bGU9ImZvbnQtc2l6ZTozMi4yNzA0OTYzN3B4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6Ym9sZDtmb250LXN0cmV0Y2g6bm9ybWFsO3RleHQtYWxpZ246c3RhcnQ7bGluZS1oZWlnaHQ6NzYuOTk5OTk4MDklO3dyaXRpbmctbW9kZTpsci10Yjt0ZXh0LWFuY2hvcjpzdGFydDtmaWxsOiMwMDAwZmY7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuMjA0Mzk4MTY7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtmb250LWZhbWlseTpTYW5zOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246U2FucyBCb2xkIj5aPC90c3Bhbj48L3RleHQ+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTU3ODEiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9IiIgLz4KICAgICAgICA8ZGM6ZGF0ZT5Nb24gT2N0IDEwIDEzOjQ0OjUyIDIwMTEgKzAwMDA8L2RjOmRhdGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9EcmFmdC9SZXNvdXJjZXMvaWNvbnMvRHJhZnRfVHJpbWV4LnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bYWdyeXNvbl0gQWxleGFuZGVyIEdyeXNvbjwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y29udHJpYnV0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5hcnJvdzwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmFycm93czwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmxpbmU8L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgICAgPGRjOmRlc2NyaXB0aW9uPkEgdmVydGljYWwgbGluZSB3aXRoIGFuIGFycm93IHBvaW50aW5nIGF3YXkgZnJvbSBpdCBvbiBlYWNoIHNpZGU8L2RjOmRlc2NyaXB0aW9uPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KPC9zdmc+Cg== """ help_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0IgogICBoZWlnaHQ9IjY0IgogICBpZD0ic3ZnNjM2MSIKICAgc29kaXBvZGk6dmVyc2lvbj0iMC4zMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC41IHIxMDA0MCIKICAgc29kaXBvZGk6ZG9jbmFtZT0iaGVscC1icm93c2VyLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyNDMxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AyNDMzIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojYjhiOGI4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDI0MzUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMTY0NCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMjE2NDYiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjA7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMjE2NDgiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyMTY0NCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDIxNjUwIgogICAgICAgY3g9IjI1LjEyNSIKICAgICAgIGN5PSIzNi43NSIKICAgICAgIGZ4PSIyNS4xMjUiCiAgICAgICBmeT0iMzYuNzUiCiAgICAgICByPSIxNS43NSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSwwLDAsMC41OTUyMzgsMCwxNC44NzUpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI5MzMiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDI5MzUiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzljYmNkZTtzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyOTM3IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMyMDRhODciIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyOTMzIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MjIwNyIKICAgICAgIGN4PSIyNi41NDQzMjEiCiAgICAgICBjeT0iMjguNDU4NzI1IgogICAgICAgZng9IjI2LjU0NDMyMSIKICAgICAgIGZ5PSIyOC40NTg3MjUiCiAgICAgICByPSIyMi4zNzYxMTYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4yMzgzNDIsMC4wMDU5NTQ4NSwtMC4wMDY1MDc3NiwxLjM1MTI3MiwtNi45OTI1MTMsLTkuNzQ0ODQyKSIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MjQzMSIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDI0MzciCiAgICAgICBjeD0iLTE5LjUxNTYzOCIKICAgICAgIGN5PSIxNi44NTU2NjMiCiAgICAgICBmeD0iLTE5LjUxNTYzOCIKICAgICAgIGZ5PSIxNi44NTU2NjMiCiAgICAgICByPSI4Ljc1MzY0MyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoNC40NDU5OTEsMCwwLDYuODY2NSw2Ny4yNTA3MSwtMTA0LjY2NzkpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM3ODAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzg2IgogICAgICAgeDE9IjQxLjY0Mjg1NyIKICAgICAgIHkxPSI1OC43ODU3MTMiCiAgICAgICB4Mj0iMjIuMzU3MTQzIgogICAgICAgeTI9IjcuMzU3MTQyOSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzgwIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM3ODIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM3MjlmY2Y7c3RvcC1vcGFjaXR5OjA7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzc4NCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpbmtzY2FwZTpndWlkZS1iYm94PSJ0cnVlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iOTIuMDU4NTM5IgogICAgIGlua3NjYXBlOmN5PSI0OC4yNzgzNDQiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNjAwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgzNyIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMjciCiAgICAgaW5rc2NhcGU6c2hvd3BhZ2VzaGFkb3c9InRydWUiCiAgICAgZmlsbD0iI2RlYjg4NyIKICAgICBzdHJva2U9IiMyMDRhODciCiAgICAgYm9yZGVybGF5ZXI9InRydWUiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjNzE3MWNkIgogICAgICAgZW1wY29sb3I9IiM3YjdiYzMiCiAgICAgICBvcGFjaXR5PSIwLjEyMTU2ODYzIgogICAgICAgZW1wb3BhY2l0eT0iMC41MzcyNTQ5IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTQiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPkhlbHAgQnJvd3NlcjwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmRhdGU+MjAwNS0xMS0wNjwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlR1b21hcyBLdW9zbWFuZW48L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5oZWxwPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+YnJvd3NlcjwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmRvY3VtZW50YXRpb248L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5kb2NzPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+bWFuPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+aW5mbzwvcmRmOmxpPgogICAgICAgICAgPC9yZGY6QmFnPgogICAgICAgIDwvZGM6c3ViamVjdD4KICAgICAgICA8Y2M6bGljZW5zZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvMi4wLyIgLz4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5KYWt1YiBTdGVpbmVyLCBBbmRyZWFzIE5pbHNzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICAgIDxkYzpzb3VyY2U+aHR0cDovL3RpZ2VydC5jb208L2RjOnNvdXJjZT4KICAgICAgPC9jYzpXb3JrPgogICAgICA8Y2M6TGljZW5zZQogICAgICAgICByZGY6YWJvdXQ9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LXNhLzIuMC8iPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9SZXByb2R1Y3Rpb24iIC8+CiAgICAgICAgPGNjOnBlcm1pdHMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly93ZWIucmVzb3VyY2Uub3JnL2NjL0Rpc3RyaWJ1dGlvbiIgLz4KICAgICAgICA8Y2M6cmVxdWlyZXMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly93ZWIucmVzb3VyY2Uub3JnL2NjL05vdGljZSIgLz4KICAgICAgICA8Y2M6cmVxdWlyZXMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly93ZWIucmVzb3VyY2Uub3JnL2NjL0F0dHJpYnV0aW9uIiAvPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9EZXJpdmF0aXZlV29ya3MiIC8+CiAgICAgICAgPGNjOnJlcXVpcmVzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9TaGFyZUFsaWtlIiAvPgogICAgICA8L2NjOkxpY2Vuc2U+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDE2KSI+CiAgICA8cGF0aAogICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgc3R5bGU9ImZpbGw6IzcyOWZjZjtzdHJva2U6IzBiMTUyMTtzdHJva2Utd2lkdGg6Mi4wNjg5NjU0NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBpZD0icGF0aDI5OTIiCiAgICAgICBzb2RpcG9kaTpjeD0iMzIiCiAgICAgICBzb2RpcG9kaTpjeT0iMzIiCiAgICAgICBzb2RpcG9kaTpyeD0iMzAiCiAgICAgICBzb2RpcG9kaTpyeT0iMzAiCiAgICAgICBkPSJNIDYyLDMyIEEgMzAsMzAgMCAxIDEgMiwzMiAzMCwzMCAwIDEgMSA2MiwzMiB6IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC45NjY2NjY2NywwLDAsMC45NjY2NjY2NywxLjA2NjY2NjYsLTE0LjkzMzMzMykiIC8+CiAgICA8cGF0aAogICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM3ODYpO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojNzI5ZmNmO3N0cm9rZS13aWR0aDoyLjIyMjIyMjMzO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgIGlkPSJwYXRoMjk5Mi0zIgogICAgICAgc29kaXBvZGk6Y3g9IjMyIgogICAgICAgc29kaXBvZGk6Y3k9IjMyIgogICAgICAgc29kaXBvZGk6cng9IjMwIgogICAgICAgc29kaXBvZGk6cnk9IjMwIgogICAgICAgZD0iTSA2MiwzMiBBIDMwLDMwIDAgMSAxIDIsMzIgMzAsMzAgMCAxIDEgNjIsMzIgeiIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuOSwwLDAsMC45LDMuMTk5OTk5NiwtMTIuOCkiIC8+CiAgICA8cGF0aAogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS42ODU3NzYyLDAsMCwxLjY1NjYzODQsNjYuNTUzNzY0LC0xNC4zNTU3OSkiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjM0LjE1MzIyODc2cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpib2xkO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1hbGlnbjpzdGFydDtsaW5lLWhlaWdodDoxMjUlO3dyaXRpbmctbW9kZTpsci10Yjt0ZXh0LWFuY2hvcjpzdGFydDtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQyNDM3KTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzcyOWZjZjtzdHJva2Utd2lkdGg6MS4xOTY3ODUwOTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utb3BhY2l0eToxO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJtIC0yMC4yNSw1LjY2MTU4MzggYyAtMS4wNTkwMTksMi42M2UtNSAtMi4xNDc2MzcsMC4xMjMyMzU2IC0zLjI4MTI1LDAuMzQzNzUgLTEuMTMyOTI1LDAuMjIwNDI4MyAtMi4wMDk0MTMsMC41Mzc1MTI1IC0zLjM3OTI1MSwxIC0wLjAxMDM3LC0wLjAwMTIxIC0wLjAyMDg4LC0wLjAwMTIxIC0wLjAzMTI1LDAgLTAuMDEyMDQsMC4wMDg2MSAtMC4wMjI2NCwwLjAxOTIxNCAtMC4wMzEyNSwwLjAzMTI1IC0wLjAxMjA0LDAuMDA4NjEgLTAuMDIyNjQsMC4wMTkyMTQgLTAuMDMxMjUsMC4wMzEyNSAtMC4wMDEyLDAuMDEwMzcgLTAuMDAxMiwwLjAyMDg4IDAsMC4wMzEyNSAtMC4wMDEyLDAuMDEwMzcgLTAuMDAxMiwwLjAyMDg4IDAsMC4wMzEyNSBsIDAsNS4xNTYyNTAyIGMgLTAuMDAxMiwwLjAxMDM3IC0wLjAwMTIsMC4wMjA4OCAwLDAuMDMxMjUgLTAuMDAxMiwwLjAxMDM3IC0wLjAwMTIsMC4wMjA4OCAwLDAuMDMxMjUgMC4wMDg2LDAuMDEyMDQgMC4wMTkyMSwwLjAyMjY0IDAuMDMxMjUsMC4wMzEyNSAwLjAwODYsMC4wMTIwNCAwLjAxOTIxLDAuMDIyNjQgMC4wMzEyNSwwLjAzMTI1IDAuMDEwMzcsMC4wMDEyIDAuMDIwODgsMC4wMDEyIDAuMDMxMjUsMCAwLjAxMDM3LDAuMDAxMiAwLjAyMDg4LDAuMDAxMiAwLjAzMTI1LDAgMC4wMTAzNywwLjAwMTIgMC4wMjA4OCwwLjAwMTIgMC4wMzEyNSwwIDAuMDEwMzcsMC4wMDEyIDAuMDIwODgsMC4wMDEyIDAuMDMxMjUsMCAxLjE0MjQzOCwtMC43MTkwNDMgMS44NjI2MDMsLTEuMjY3MTM4IDIuOTEwNTAxLC0xLjYyNSAxLjA0ODE0MSwtMC4zNjg3NTYgMi4wNDMxMTYsLTAuNTYyNDc5IDIuOTM3NSwtMC41NjI1IDAuOTQ5MjE4LDIuMWUtNSAxLjY0NDkyNSwwLjIxMDU0NSAyLjE1NjI1LDAuNjI1IDAuNTA4NzIzLDAuNDAyMDkgMC43ODEyMzgsMC45ODMwNCAwLjc4MTI1LDEuNzE4NzUgLTEwZS02LDAuNDgwNjU3IC0wLjE0NDE4OCwwLjk1MTQxIC0wLjQzNzUsMS40Mzc1IC0wLjI4Mjk5MSwwLjQ4NzAxNiAtMC43NDAyNjUsMS4yNDM5MTEgLTEuMzc1LDEuODA3MTY2IEwgLTIwLjkzNzUsMTYuNzUgYyAtMS4yMDE0NTksMS4wODA0OSAtMS43Nzk1MTYsMS45OTEwMjIgLTIuMTY1MjczLDIuNzE4NzUgLTAuMzgzMTEzLDAuNzExNDQ2IC0wLjM1OTc2OSwxLjIzMTY1NiAtMC4zNTk3NjksMi4xNDk4MzIgbCAwLDAuODEyNSBjIC0wLjAwMTIsMC4wMTAzNyAtMC4wMDEyLDAuMDIwODggMCwwLjAzMTI1IC0wLjAwMTIsMC4wMTAzNyAtMC4wMDEyLDAuMDIwODggMCwwLjAzMTI1IDAuMDA4NiwwLjAxMjA0IDAuMDE5MjEsMC4wMjI2NCAwLjAzMTI1LDAuMDMxMjUgMC4wMDg2LDAuMDEyMDQgMC4wMTkyMSwwLjAyMjY0IDAuMDMxMjUsMC4wMzEyNSAwLjAxMDM3LDAuMDAxMiAwLjAyMDg4LDAuMDAxMiAwLjAzMTI1LDAgMC4wMTAzNywwLjAwMTIgMC4wMjA4OCwwLjAwMTIgMC4wMzEyNSwwIGwgNS42ODEyOTIsLTAuMDI4MyBjIDAuMDEwMzcsMC4wMDEyIDAuMDIwODgsMC4wMDEyIDAuMDMxMjUsMCAwLjAxMDM3LDAuMDAxMiAwLjAyMDg4LDAuMDAxMiAwLjAzMTI1LDAgMC4wMTIwNCwtMC4wMDg2IDAuMDIyNjQsLTAuMDE5MjEgMC4wMzEyNSwtMC4wMzEyNSAwLjAxMjA0LC0wLjAwODYgMC4wMjI2NCwtMC4wMTkyMSAwLjAzMTI1LC0wLjAzMTI1IDAuMDAxMiwtMC4wMTAzNyAwLjAwMTIsLTAuMDIwODggMCwtMC4wMzEyNSAwLjAwMTIsLTAuMDEwMzcgMC4wMDEyLC0wLjAyMDg4IDAsLTAuMDMxMjUgbCAwLC0wLjc1IGMgLTEuMWUtNSwtMC40NjgxOTYgMC4xNDgzNywtMC41NzY4ODUgMC4zNzUsLTAuOTY1Mjg2IDAuMjIyMjk1LC0wLjM5MTI4NCAwLjcwODA3MywtMC45NTAzNTkgMS40Njg3NSwtMS42MjUgbCAxLjA2MjUsLTAuOTM3NSBjIDEuMDY2NTg4LC0wLjk4MjMxIDEuODMwNjU5LC0xLjg4NDY1NCAyLjI4MTI1LC0yLjc1IDAuNDQ5MjY5LC0wLjg3NDA0NiAwLjY4NzQ4MiwtMi4wODY4MzYgMC42ODc1LC0zLjE4MjE2NiAtMS44ZS01LC0yLjEyNjI2NSAtMC43NDM3NjksLTMuNzM0MDI3OCAtMi4yMTg3NSwtNC44NDM3NTAyIC0xLjQ3NTE5NywtMS4xMjEwMjUyIC0zLjYwNjI0LC0xLjY4NzQ3MzcgLTYuMzc1LC0xLjY4NzQ5NiB6IgogICAgICAgaWQ9InBhdGgxNTU0IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY3NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7c3Ryb2tlOiM3MjlmY2Y7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gMjcsMjcgMTAsMCAwLDEwIC0xMCwwIHoiCiAgICAgICBpZD0icGF0aDM3ODgiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogIDwvZz4KPC9zdmc+Cg== """ edit_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjkwLjAwMDAwMCIKICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjkwLjAwMDAwMCIKICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS9qaW1tYWMvRGVza3RvcC93aS1maS5wbmciCiAgIHdpZHRoPSI1MS4yMDAwMDEiCiAgIGhlaWdodD0iNTEuMjAwMDAxIgogICBpZD0ic3ZnMTEzMDAiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMCByMTUyOTkiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlByZWZlcmVuY2VzLWdlbmVyYWwuc3ZnIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzIj4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjI1MCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMjI1MiIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AyMjU0IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjI2NSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMjI2NyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MDsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AyMjY5IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjI1NyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMjI1OSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AyMjYxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA4NyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzNDY1YTQ7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA4OSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMDk1IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM5ZmJjZTE7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNmI5NWNhO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDIyNDIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMjI0NCIKICAgICAgICAgb2Zmc2V0PSIwLjc1IgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojM2Q2YWE1O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM4NmViNDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMDkxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA3NyI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM5OGEwYTk7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA3OSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2MzZDBkZDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMDgxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA2MSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA2MyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzMDY1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0OSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNiNmI2YjY7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzA1MSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyMjYyIgogICAgICAgICBvZmZzZXQ9IjAuNSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2YyZjJmMjtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmYWZhZmE7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAuNjc2MTI5NTgiCiAgICAgICAgIGlkPSJzdG9wMjI2NCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AyMjY4IgogICAgICAgICBvZmZzZXQ9IjAuODQwNTE3MjIiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNkOGQ4ZDg7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDIyNjYiCiAgICAgICAgIG9mZnNldD0iMC44NzUiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmMmYyZjI7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZGJkYmRiO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMwNTMiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMDQ5IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA1NSIKICAgICAgIHgxPSIxOS42NDgzNDIiCiAgICAgICB5MT0iNDIuMjUzNjAxIgogICAgICAgeDI9IjIwLjYzMTIyNCIKICAgICAgIHkyPSI2Ljc3NTgwMzEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45MzY4MjEzMywwLDAsMC45MzY4MjEzMywyLjcwNjEyMDUsMS4yOTg4NTk3KSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzA2MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNjciCiAgICAgICB4MT0iNTAuMTUyOTMxIgogICAgICAgeTE9Ii0zLjYzMjQ0NzciCiAgICAgICB4Mj0iMjUuMjkxMDg2IgogICAgICAgeTI9Ii00LjMwMDI2NTMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45MzY4MjEzMywwLDAsMC45MzY4MjEzMywyLjgxODg4NjUsLTEuMDMxNDkzMikiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMwNzciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDgzIgogICAgICAgeDE9IjM4LjIyNzY1NCIKICAgICAgIHkxPSIxMy42MDI1MjciCiAgICAgICB4Mj0iMzcuNTM1MzciCiAgICAgICB5Mj0iNi42Mjg1ODk2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTM2ODIxMzMsMCwwLDAuOTM2ODIxMzMsMy4wMzczMzY1LDEuOTYxMjkyOCkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMwODciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDkzIgogICAgICAgeDE9IjkuNzUwMzI0MiIKICAgICAgIHkxPSIzMi4yODM3NiIKICAgICAgIHgyPSIxNi45MTUyOTciCiAgICAgICB5Mj0iMzkuNDQzMjE4IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTM2ODIxMzMsMCwwLDAuOTM2ODIxMzMsMi43MDYxMjA1LDEuMjk4ODU5NykiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIyNTciCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMjYzIgogICAgICAgeDE9IjEyLjAwNDY5NyIKICAgICAgIHkxPSIzNS42ODg0NjEiCiAgICAgICB4Mj0iMTAuNjUwODA1IgogICAgICAgeTI9IjMzLjE5NDk2NSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjA3NDQwNDMsLTAuMDI4MTIyOTQsMC4wMjgxMjI5NCwxLjA3NDQwNDMsMS42OTk2Mzg0LC0zLjkxNTUyOTYpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyMjY1IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjI3MSIKICAgICAgIHgxPSIxNC4wMTc1NDIiCiAgICAgICB5MT0iMzYuOTQyNTQzIgogICAgICAgeDI9IjE1LjQxNTc5MyIKICAgICAgIHkyPSIzOC4yNjgzNjgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45MzY2Mzg5MywtMC4wMTg0Nzg2MSwwLjAxODQ3ODYxLDAuOTM2NjM4OTMsMi4zMDc5MzI4LDAuMzM5MDkyMykiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDIyNTAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMjU2IgogICAgICAgeDE9IjMxLjE3NzQwNCIKICAgICAgIHkxPSIxOS44MjE1MTQiCiAgICAgICB4Mj0iNDAuODU5MTc3IgogICAgICAgeTI9IjkuNjU2ODUzNyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjA2NjY2NjcsMCwwLDEuMDY2NjY2NywwLC00KSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MjI1MCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIyNjMtNSIKICAgICAgIHgxPSIxMi4wMDQ2OTciCiAgICAgICB5MT0iMzUuNjg4NDYxIgogICAgICAgeDI9IjEwLjY1MDgwNSIKICAgICAgIHkyPSIzMy4xOTQ5NjUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS4wNzQ0MDQzLC0wLjAyODEyMjk0LDAuMDI4MTIyOTQsMS4wNzQ0MDQzLC05OS41MjQ4MTQsLTU0Ljg0MjM0OSkiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBzdHJva2U9IiMyMDRhODciCiAgICAgZmlsbD0iIzM0NjVhNCIKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMC4yNTQ5MDE5NiIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTIuMTA5Mzc1IgogICAgIGlua3NjYXBlOmN4PSIyNS42IgogICAgIGlua3NjYXBlOmN5PSIyNS42IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpzaG93cGFnZXNoYWRvdz0iZmFsc2UiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNTM2IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjgwMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiIC8+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkpha3ViIFN0ZWluZXI8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnNvdXJjZT5odHRwOi8vamltbWFjLm11c2ljaGFsbC5jejwvZGM6c291cmNlPgogICAgICAgIDxjYzpsaWNlbnNlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS8yLjAvIiAvPgogICAgICAgIDxkYzp0aXRsZT5QcmVmZXJlbmNlcyBTeXN0ZW08L2RjOnRpdGxlPgogICAgICAgIDxkYzpzdWJqZWN0PgogICAgICAgICAgPHJkZjpCYWc+CiAgICAgICAgICAgIDxyZGY6bGk+cHJlZmVyZW5jZXM8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5zZXR0aW5nczwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmNvbnRyb2wgcGFuZWw8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT50d2Vha3M8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5zeXN0ZW08L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvY2M6V29yaz4KICAgICAgPGNjOkxpY2Vuc2UKICAgICAgICAgcmRmOmFib3V0PSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS8yLjAvIj4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3dlYi5yZXNvdXJjZS5vcmcvY2MvUmVwcm9kdWN0aW9uIiAvPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9EaXN0cmlidXRpb24iIC8+CiAgICAgICAgPGNjOnJlcXVpcmVzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9Ob3RpY2UiIC8+CiAgICAgICAgPGNjOnJlcXVpcmVzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9BdHRyaWJ1dGlvbiIgLz4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3dlYi5yZXNvdXJjZS5vcmcvY2MvRGVyaXZhdGl2ZVdvcmtzIiAvPgogICAgICAgIDxjYzpyZXF1aXJlcwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3dlYi5yZXNvdXJjZS5vcmcvY2MvU2hhcmVBbGlrZSIgLz4KICAgICAgPC9jYzpMaWNlbnNlPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MTtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDU1KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6Izg4OGE4NTtzdHJva2Utd2lkdGg6MS4wNjY2NjYzNjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZSIKICAgICAgIGQ9Im0gMTkuMTAwNDk0LDE4LjYzMDA1NCAyMC4yNTg3NjEsMjAuNzI3MTczIGMgMC44MTk3MTksMC45MzY4MjEgMy40MTcwNzgsMS42NjA4NjUgNS4xNTI1MTgsMCAxLjY3NTg2OCwtMS42MDM4NTQgMS4yODgxMjksLTMuODY0Mzg5IC0wLjM1MTMwOCwtNS41MDM4MjYgTCAyNC43MjE0MjIsMTMuMDA5MTI2IEMgMjcuMTIxNDIyLDYuMzQyNDYgMjIuMjYzOTQsMC43NDI5MjA1IDE1Ljk5NzI3NCwxLjk0MjkyMDUgbCAtMS4zNDY2ODIsMS4yMjk1NzgxIDQuMjE1Njk2LDMuOTgxNDkwNCAwLjIzNDIwNiwzLjUxMzA4NCAtMy4xNDc1NjUsMi44NzMxNzcgLTMuNzYxNDkyLC0wLjQxNDAyMSAtMy44NjQzODc4LC0zLjYzMDE4MiBjIDAsMCAtMS4zNTQ3OTYxLDEuMzM4NTY0IC0xLjM1NDc5NjEsMS4zMzg1NjQgLTAuNjMwMTQ3Nyw2LjAxNzQwNSA1LjY2MTU3MzksMTEuMzk1NDQzIDEyLjEyODI0MDksNy43OTU0NDMgeiIKICAgICAgIGlkPSJwYXRoMjE0MCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2N6Y2NjY2NjY2Njc2MiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2N6Y2NjY2NjY2NjY2MiCiAgICAgICBpZD0icGF0aDMwNTciCiAgICAgICBkPSJtIDE5LjMyNTIxMSwxNy4yNjk0NCAyMC40ODMwNzQsMjEuMzYyNzg2IGMgMC42MzQ1NjcsMC43MjUyMTkgMi42NDUyNTUsMS4yODU3MjMgMy45ODg3MDgsMCAxLjI5NzMzNywtMS4yNDE1ODggMC45OTcxNzcsLTIuOTkxNTMxIC0wLjI3MTk1NywtNC4yNjA2NjUgTCAyMy44MDA3MzUsMTMuNDQyMzI5IGMgMS42LC02LjkzMzMzNCAtMS45ODI3MDcsLTEwLjY3MTYxMzggLTcuMzE2MDQxLC0xMC41MzgyODA1IGwgLTAuMjg4MTM5LDAuMjkxNjAyIDMuODQzMTE4LDMuNDUyMDE3NSAwLjEzODg0Myw0LjQ2MDU4MSAtMy44NTQ4NDUsMy41MTgzODEgLTQuNTI1MTI2LC0wLjQ4ODc1NiAtMy4zODgzNTQ1LC0zLjE5MDkxMyAtMC4zNzYxNDYyLDAuNDU4NzMgQyA3LjcwMDcxMDksMTcuNzcyMzU3IDE0Ljk1ODU0NCwyMC42Njk0NCAxOS4zMjUyMTEsMTcuMjY5NDQgWiIKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MC40MjYxMzYzOTtmaWxsOm5vbmU7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjEuMDY2NjY1Nzc7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmUiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHJlY3QKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MC4xNzA0NTQ1NjtmaWxsOm5vbmU7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOnVybCgjbGluZWFyR3JhZGllbnQzMDY3KTtzdHJva2Utd2lkdGg6MS4wNjY2NjM2MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZSIKICAgICAgIGlkPSJyZWN0MzA1OSIKICAgICAgIHdpZHRoPSIyNC44MTk0OTQiCiAgICAgICBoZWlnaHQ9IjIuMTkyNTI0IgogICAgICAgeD0iMjcuMTk5NzI2IgogICAgICAgeT0iLTUuNTg0NzY1IgogICAgICAgcng9IjAuOTQyODA2MTIiCiAgICAgICByeT0iMC45NDI4MDYxMiIKICAgICAgIHRyYW5zZm9ybT0icm90YXRlKDQ1LjczODE5KSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtvcGFjaXR5OjE7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzA4Myk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiM4NzhmOWQ7c3Ryb2tlLXdpZHRoOjEuMDY2NjY2MzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmUiCiAgICAgICBkPSJNIDIzLjk5ODcxNCwyOC4xMzM3MzkgQyAyNC44ODc4MjQsMjcuMzcxNjQ1IDM4LjE2ODEzNywxMy43ODg2NjIgMzguMTY4MTM3LDEzLjc4ODY2MiBMIDQxLjQ0NzAxMSwxMy41NTQ0NTcgNDYuNTk5NTI5LDYuNDExMTkgNDIuMzA2OTEzLDIuNTg2OTg1MyAzNS42MzIwNjEsOC4zMjUwMTkgdiAzLjI3ODg3NSBsIC0xMy41ODM5MSwxNC4xMTA4NzIgYyAtMC42NDQwNjQsMC42NDQwNjQgMS4xMzA4NDQsMy4xMjE1ODggMS45NTA1NjMsMi40MTg5NzMgeiIKICAgICAgIGlkPSJwYXRoMjE0NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjYyIKICAgICAgIGlkPSJwYXRoMzA4NSIKICAgICAgIGQ9Ik0gMjMuODk1NDUzLDI3LjAyNDQ4NSBDIDI0LjU4NTM0NCwyNi40MzMxNSAzNy43ODgxNDcsMTIuOTEyNjkxIDM3Ljc4ODE0NywxMi45MTI2OTEgTCA0MC45MTE5NjksMTIuNjQ4MTU5IDQ1LjQwNjgxMyw2LjYwODYyMyA0Mi4zMjQ0MzMsMy44ODk3MDA5IDM2LjQ4Mjc1LDguOTIxNjcxIDM2LjY0ODM1OCwxMS45NjI2OSAyMy4xMjcxOCwyNi4xNDExNjggYyAtMC40OTk3NTMsMC40OTk3NTMgMC4xMzIyMjUsMS40Mjg1MDEgMC43NjgyNzMsMC44ODMzMTcgeiIKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MC41Mzk3NzI3MjtmaWxsOm5vbmU7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOnVybCgjbGluZWFyR3JhZGllbnQyMjU2KTtzdHJva2Utd2lkdGg6MS4wNjY2NjY5NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7ZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzA5Myk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMyMDRhODc7c3Ryb2tlLXdpZHRoOjEuMDY2NjY2MzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7bWFya2VyOm5vbmU7bWFya2VyLXN0YXJ0Om5vbmU7bWFya2VyLW1pZDpub25lO21hcmtlci1lbmQ6bm9uZSIKICAgICAgIGQ9Im0gOS4wMjk2NjUyLDQyLjUxODk5OCBjIDEuNDA0MzU5OCwxLjU2MjkxMSA1LjMwNTI2MzgsMi4yNzAwNDMgNy4wMzUxMzk4LC0wLjc1NzI0MSAwLjc1NDI0OCwtMS4zMTk5MzIgMi4yMzM1NCwtNS4wMTYzMjIgOC44MzMyMDQsLTEwLjk1NjAxOSAxLjEwODQyMiwtMC45OTY0NDkgMi4yODI1NjksLTMuMjc1ODgxIDEuMjg3MTk2LC00LjUwNTQ1OSBMIDIzLjYwODk0NiwyMy43MjQwMiBjIC0xLjA1MzkyMywtMS4xNzEwMjYgLTMuOTgzMzc1LC0wLjYyNDc5OSAtNS4xODUyNTMsMS4wMTQyNjggLTMuNTgyNjc0LDQuOTAyNjA3IC05LjQzNTgxMTEsOC44MDUyMDUgLTEwLjc1NTc0MzksOS4yNzY2MSAtMi41MjU3ODA4LDAuOTAyMDY0IC0yLjI0MTUwNDYsNC42MjUyMjYgLTAuNTcwNDc4LDYuMzk2MjUzIHoiCiAgICAgICBpZD0icGF0aDIxNDIiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY3NjYyIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8Y2lyY2xlCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtvcGFjaXR5OjE7ZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTojYTFhMWExO3N0cm9rZS13aWR0aDoxLjA2NjY2NjM2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTttYXJrZXItc3RhcnQ6bm9uZTttYXJrZXItbWlkOm5vbmU7bWFya2VyLWVuZDpub25lIgogICAgICAgaWQ9InBhdGgyMTQ2IgogICAgICAgY3g9IjQxLjgxODQxMyIKICAgICAgIGN5PSIzNi41NDY3NjEiCiAgICAgICByPSIxLjI4ODEyOTMiIC8+CiAgICA8Y2lyY2xlCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtvcGFjaXR5OjAuNjAyMjcyNzI7ZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDowLjYwODkzNDQ7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmUiCiAgICAgICBpZD0icGF0aDMxMDEiCiAgICAgICBjeD0iMjEuMzM3MDUzIgogICAgICAgY3k9IjI1LjE3Mzc0OCIKICAgICAgIHI9IjEuMDc2NDU0MiIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtvcGFjaXR5OjE7ZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTp1cmwoI2xpbmVhckdyYWRpZW50MjI2Myk7c3Ryb2tlLXdpZHRoOjIuNDQ3NDc2Mzk7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTttYXJrZXItc3RhcnQ6bm9uZTttYXJrZXItbWlkOm5vbmU7bWFya2VyLWVuZDpub25lIgogICAgICAgZD0ibSAxOS45MjQxNjUsMjcuNTk5Nzk0IGMgMCwwIC03LjY0Nzg4Myw3Ljc4NjI1NCAtMTEuMjMwNTU3Miw5LjEwNjE4NyIKICAgICAgIGlkPSJwYXRoMzEwMyIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjY2Njc2NjIgogICAgICAgaWQ9InBhdGgyMjcwIgogICAgICAgZD0ibSA5LjM5MzA2ODEsNDEuMzE5MTM2IGMgMS41MzczNDk5LDEuODYxOTkxIDQuOTA2MTY4OSwyLjMwNTMxMSA1Ljk2MzkwMzksLTAuMzk1Mjk1IDAuNzI2NzI3LC0xLjg1NTQ3OSAzLjU1NTg2MywtNi4wNDM1MDYgOC43NDQwODksLTEwLjcxMjkxIDAuODcxMzcsLTAuNzgzMzQ0IDEuNzk0NDA5LC0yLjU3NTI4NCAxLjAxMTkxMSwtMy41NDE4OTggbCAtMi4wMjUyODgsLTIuMDI1Mjg4IGMgLTAuODI4NTI2LC0wLjkyMDU4NCAtMy4xMzE0NywtMC40OTExNzYgLTQuMDc2MzA5LDAuNzk3MzUyIC0yLjgxNjQ2NSwzLjg1NDExIC05LjA0ODkwMSw4Ljg5NTUyNCAtMTAuNTU1NDY2Miw5LjM5MjY2NyAtMi4zMzI1ODEsMC43Njk3MTggLTEuODk1NDU4NiwzLjQzNjA1MyAtMC41ODE4MDYxLDQuODI4MzE5IHoiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtvcGFjaXR5OjAuMTk4ODYzNjc7ZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTojZmZmZmZmO3N0cm9rZS13aWR0aDoxLjA2NjY2NjEzO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTttYXJrZXItc3RhcnQ6bm9uZTttYXJrZXItbWlkOm5vbmU7bWFya2VyLWVuZDpub25lIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO3Zpc2liaWxpdHk6dmlzaWJsZTtvcGFjaXR5OjAuMjc4NDA5MTE7ZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTp1cmwoI2xpbmVhckdyYWRpZW50MjI3MSk7c3Ryb2tlLXdpZHRoOjIuNDQ3NDc2Mzk7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lO21hcmtlci1zdGFydDpub25lO21hcmtlci1taWQ6bm9uZTttYXJrZXItZW5kOm5vbmUiCiAgICAgICBkPSJtIDIyLjIxMjkwOSwyOS4zNDUwOTIgYyAwLDAgLTcuODEwOTQ3LDcuMDU4NTY2IC05LjUwODAwMywxMS41ODQwNDgiCiAgICAgICBpZD0icGF0aDIyNDciCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzQ1OTgiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDUuMDQ4MDYsNTUuMTY1MDcxKSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiCiAgICAgICAgIGlkPSJwYXRoMzEwMy05IgogICAgICAgICBkPSJtIC04MS4zMDAyODcsLTIzLjMyNzAyNCBjIDAsMCAtNy42NDc4ODMsNy43ODYyNTQgLTExLjIzMDU1Nyw5LjEwNjE4NyIKICAgICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2aXNpYmlsaXR5OnZpc2libGU7b3BhY2l0eToxO2ZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6dXJsKCNsaW5lYXJHcmFkaWVudDIyNjMtNSk7c3Ryb2tlLXdpZHRoOjIuNDQ3NDc2NjM7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MTttYXJrZXI6bm9uZTttYXJrZXItc3RhcnQ6bm9uZTttYXJrZXItbWlkOm5vbmU7bWFya2VyLWVuZDpub25lIiAvPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO3N0cm9rZTojMzAyYjAwO3N0cm9rZS13aWR0aDoxLjY3NDcxODg2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Im0gLTY0Ljk2MzA0NSwtMjAuNjAzOTQgNC4xODY3OTksMTUuMDcyNDcwMSAtMTQuMjM1MTEzLC01LjAyNDE1NjEgLTEuNjc0NzE0LC0zLjM0OTQzOCA4LjM3MzU5LC04LjM3MzU5NSB6IgogICAgICAgICBpZD0icGF0aDM5NjkiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjIiAvPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0ib3BhY2l0eTowLjc7ZmlsbDojZmNlOTRmO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDowLjgzMDAzODY3IgogICAgICAgICBkPSJtIC04MS43MTAyMzUsLTM3LjM1MTEzNCAtMy4zNDk0MzcsMy4zNDk0MzggMTYuNzQ3MTg5LDE2Ljc0NzE5NCBjIDAsLTIuNTEyMDc4IDAuODM3MzYxLC0zLjM0OTQzOCAzLjM0OTQzOCwtMy4zNDk0MzggeiIKICAgICAgICAgaWQ9InBhdGgzODQzIgogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjIiAvPgogICAgICA8cGF0aAogICAgICAgICBzdHlsZT0ib3BhY2l0eTowLjc7ZmlsbDojZWRkNDAwO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDowLjgzMDAzODY3IgogICAgICAgICBkPSJtIC04NS4wNTk2NzIsLTM0LjAwMTcwMyAtMy4zNDk0MzgsMy4zNDk0NSAxNi43NDcxODksMTYuNzQ3MTg5IGMgMCwtMi41MTIwNzggMC44MzczNjIsLTMuMzQ5NDM4IDMuMzQ5NDM4LC0zLjM0OTQzOCB6IgogICAgICAgICBpZD0icGF0aDM4NDMtNyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9Im9wYWNpdHk6MC43O2ZpbGw6I2M0YTAwMDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MC44MzAwMzg2NyIKICAgICAgICAgZD0ibSAtODguNDA5MTEsLTMwLjY1MjI1MyAtMy4zNDk0MzgsMy4zNDk0MzggMTYuNzQ3MTg5LDE2Ljc0NzE4OSBjIDAsLTIuNTEyMDc5IDAuODM3MzYzLC0zLjM0OTQzOCAzLjM0OTQzOCwtMy4zNDk0MzggeiIKICAgICAgICAgaWQ9InBhdGgzODQzLTUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuNTtmaWxsOm5vbmU7c3Ryb2tlOiNlZGQ0MDA7c3Ryb2tlLXdpZHRoOjEuNjc0NzE4ODY7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Im0gLTg5LjI0NjQ2MywtMjkuMTQ1MDEzIDE1LjA3MjQ2NywxNS4wNzI0NzkiCiAgICAgICAgIGlkPSJwYXRoMzg4OCIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9Im9wYWNpdHk6MC41O2ZpbGw6bm9uZTtzdHJva2U6I2ZjZTk0ZjtzdHJva2Utd2lkdGg6MS42NzQ3MTg4NjtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0ibSAtODUuODk3MDMzLC0zMi40OTQ0NDcgMTUuMDcyNDc0LDE1LjA3MjQ3MyIKICAgICAgICAgaWQ9InBhdGgzODg4LTUiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMzMDJiMDA7c3Ryb2tlLXdpZHRoOjEuNjc0NzE4ODY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0ibSAtODEuNzEwMjM1LC0zNy4zNTExMzIgLTMuMzQ5NDM3LDMuMzQ5NDM4IDE2Ljc0NzE4OSwxNi43NDcxOTIgYyAwLC0yLjUxMjA3OCAwLjgzNzM2MSwtMy4zNDk0MzggMy4zNDk0MzgsLTMuMzQ5NDM4IHoiCiAgICAgICAgIGlkPSJwYXRoMzg0My01LTYiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMzMDJiMDA7c3Ryb2tlLXdpZHRoOjEuNjc0NzE4ODY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0ibSAtODUuMDU5NjcyLC0zNC4wMDE2OTQgLTMuMzQ5NDM4LDMuMzQ5NDQxIDE2Ljc0NzE4OSwxNi43NDcxODkgYyAwLC0yLjUxMjA3OCAwLjgzNzM2MiwtMy4zNDk0MzggMy4zNDk0MzgsLTMuMzQ5NDM4IHoiCiAgICAgICAgIGlkPSJwYXRoMzg0My01LTYtMiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzMwMmIwMDtzdHJva2U6IzMwMmIwMDtzdHJva2Utd2lkdGg6MC44MzczNTk0M3B4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Im0gLTYyLjQ1MDk2OCwtMTMuMDY3NzA1IGMgLTEuNjc0NzE2LDAgLTQuNjY0NTgxLDIuMTA2OTg5IC01LjAyNDE1NCw0LjE4Njc5ODEgbCA2LjY5ODg3NiwzLjM0OTQzNyAtMS42NzQ3MjIsLTYuNjk4ODc1MSB2IDAiCiAgICAgICAgIGlkPSJwYXRoMzk3MSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzMwMmIwMDtzdHJva2Utd2lkdGg6MS42NzQ3MTg4NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJtIC04OC40MDkxMSwtMzAuNjUyMjUzIC0zLjM0OTQzOCwzLjM0OTQzOCAxNi43NDcxODksMTYuNzQ3MTg5IGMgMCwtMi41MTIwNzkgMC44MzczNjMsLTMuMzQ5NDM4IDMuMzQ5NDM4LC0zLjM0OTQzOCB6IgogICAgICAgICBpZD0icGF0aDM4NDMtNS02LTkiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K """ #""" #PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIGhlaWdodD0iNDgiCiAgIHdpZHRoPSI0OCIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnMTM3IgogICBzb2RpcG9kaTpkb2NuYW1lPSJFZGl0LnN2ZyIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4wIHIxNTI5OSI+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhMTQxIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgb2JqZWN0dG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBndWlkZXRvbGVyYW5jZT0iMTAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE1MzYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODAxIgogICAgIGlkPSJuYW1lZHZpZXcxMzkiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjQuOTE2NjY2NyIKICAgICBpbmtzY2FwZTpjeD0iMjQiCiAgICAgaW5rc2NhcGU6Y3k9IjI0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmcxMzciIC8+CiAgPGRlZnMKICAgICBpZD0iZGVmczc1Ij4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImEiPgogICAgICA8c3RvcAogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMiIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1vcGFjaXR5PSIwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlkPSJ4IgogICAgICAgeGxpbms6aHJlZj0iI2EiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN5PSI0My41IgogICAgICAgY3g9IjQuOTkzIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjAwMzgsMCwwLDEuNCwyNy45ODgsLTE3LjQpIgogICAgICAgcj0iMi41IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpZD0ieSIKICAgICAgIHhsaW5rOmhyZWY9IiNhIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBjeT0iNDMuNSIKICAgICAgIGN4PSI0Ljk5MyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi4wMDM4LDAsMCwxLjQsLTIwLjAxMiwtMTA0LjQpIgogICAgICAgcj0iMi41IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0icyIKICAgICAgIHkyPSIzOS45OTkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgyPSIyNS4wNTgiCiAgICAgICB5MT0iNDcuMDI4IgogICAgICAgeDE9IjI1LjA1OCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iMCIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIG9mZnNldD0iLjUiCiAgICAgICAgIGlkPSJzdG9wMTEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDEzIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaWQ9InoiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN5PSIzNS40NzkiCiAgICAgICBjeD0iNy45MzU4IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMS4xNzM0Nzg3LDAsMCwxLjQwMTU0OTIsNDUuMjYxNzI4LC0xMy43OTQ1NjgpIgogICAgICAgcj0iODYuNzA4Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2ZhZmFmYSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDE2IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjYmJiIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMTgiIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpZD0iYWEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN5PSIzLjc1NjEiCiAgICAgICBjeD0iOC44MjQ0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjE4Mjk5MDMsMCwwLC0xLjEzMTgzNTYsLTEuMDM0MTk2NCw0OC42ODg3NDYpIgogICAgICAgcj0iMzcuNzUyIj4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2EzYTNhMyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDIxIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjOGE4YThhIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMjMiIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpZD0iYWIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN5PSI3LjI2NzkiCiAgICAgICBjeD0iOC4xNDM2IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjIwNjk1NzUsMCwwLDIuMTA2MDM4LC05LjM3NjU1NjgsLTkuNTk0Mzk5MykiCiAgICAgICByPSIzOC4xNTkiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZmIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMjYiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNmOGY4ZjgiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AyOCIgLz4KICAgIDwvcmFkaWFsR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlkPSJhYyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3k9IjE3IgogICAgICAgY3g9IjgiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsMCwwLDAuNjI1LDAsNi4zNzUpIgogICAgICAgcj0iOCI+CiAgICAgIDxzdG9wCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMSIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1vcGFjaXR5PSIwIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzMiIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpZD0icCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3k9IjIyLjEzNyIKICAgICAgIGN4PSIyNCIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC42MDQ1MiwwLDAsMC42MjYwNywxLjQ4MjgsMC45ODMwNikiCiAgICAgICByPSIxOS41Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2VlZWVlYyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM2IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZWVlZWVjIgogICAgICAgICBvZmZzZXQ9Ii41IgogICAgICAgICBpZD0ic3RvcDM4IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZmIgogICAgICAgICBvZmZzZXQ9Ii43ODMxMyIKICAgICAgICAgaWQ9InN0b3A0MCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2QzZDdjZiIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDQyIiAvPgogICAgPC9yYWRpYWxHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9InQiCiAgICAgICB5Mj0iNC43Mzk3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4Mj0iNC40ODMyIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjIwOSwwLDAsMS4yNTMzLDIuNjkxOSwxLjcxODkpIgogICAgICAgeTE9IjYuMzExNyIKICAgICAgIHgxPSI2LjA1NTIiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZWYyOTI5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNlZjI5MjkiCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDQ3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9InUiCiAgICAgICB5Mj0iMTQuMjc1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4Mj0iOC4xMjI0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjIwOSwwLDAsMS4yNTMzLDIuNjkxOSwxLjcxODkpIgogICAgICAgeTE9IjE1Ljk2NyIKICAgICAgIHgxPSI2LjQzMDMiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZWYyOTI5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNlZjI5MjkiCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDUyIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9InYiCiAgICAgICB5Mj0iNC4zMTgyIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4Mj0iMTcuNDE3IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLjIwOSwwLDAsMS4yNTMzLDIuNjkxOSwxLjcxODkpIgogICAgICAgeTE9IjYuNDQzNyIKICAgICAgIHgxPSIxNS4yOTEiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZWYyOTI5IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNTUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNlZjI5MjkiCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDU3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9InciCiAgICAgICB5Mj0iMTQuNTQiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgyPSIxNC4zNzIiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMjA5LDAsMCwxLjI1MzMsMi42OTE5LDEuNzE4OSkiCiAgICAgICB5MT0iMTUuOTkiCiAgICAgICB4MT0iMTUuODIyIj4KICAgICAgPHN0b3AKICAgICAgICAgc3RvcC1jb2xvcj0iI2VmMjkyOSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDYwIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZWYyOTI5IgogICAgICAgICBzdG9wLW9wYWNpdHk9IjAiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3A2MiIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlkPSJxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBjeT0iMy4xNTQ5IgogICAgICAgY3g9IjcuNTkyNyIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi43MjQsMCwwLDIuNjMzNCwtMTMuNTYsLTQuODMxMikiCiAgICAgICByPSI4LjQ2MjIiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZmIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNjUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNmZmYiCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iLjMzOTI5IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNjciIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpZD0iciIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgY3k9IjE2LjE0OSIKICAgICAgIGN4PSIxMC44NjQiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuMjEwMiwwLDAsMi4xMzg5LC0xMi4xMDIsLTE4LjgyMSkiCiAgICAgICByPSI4LjcxNDQiPgogICAgICA8c3RvcAogICAgICAgICBzdG9wLWNvbG9yPSIjZmZmIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNzAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0b3AtY29sb3I9IiNmZmYiCiAgICAgICAgIHN0b3Atb3BhY2l0eT0iMCIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDcyIiAvPgogICAgPC9yYWRpYWxHcmFkaWVudD4KICA8L2RlZnM+CiAgPGcKICAgICBzdHlsZT0ib3BhY2l0eTowLjQiCiAgICAgaWQ9Imc4MyIKICAgICB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NTIyNDYsMCwwLDEuNDA0NDk4NywtNi42Njg4NjcyLC0xNy44NjM1NCkiPgogICAgPHJlY3QKICAgICAgIHN0eWxlPSJmaWxsOnVybCgjeCkiCiAgICAgICBpZD0icmVjdDc3IgogICAgICAgeD0iMzgiCiAgICAgICB5PSI0MCIKICAgICAgIHdpZHRoPSI1IgogICAgICAgaGVpZ2h0PSI3IiAvPgogICAgPHJlY3QKICAgICAgIHN0eWxlPSJmaWxsOnVybCgjeSkiCiAgICAgICBpZD0icmVjdDc5IgogICAgICAgeD0iLTEwIgogICAgICAgeT0iLTQ3IgogICAgICAgd2lkdGg9IjUiCiAgICAgICBoZWlnaHQ9IjciCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKC0xKSIgLz4KICAgIDxyZWN0CiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI3MpIgogICAgICAgaWQ9InJlY3Q4MSIKICAgICAgIHg9IjEwIgogICAgICAgeT0iNDAiCiAgICAgICB3aWR0aD0iMjgiCiAgICAgICBoZWlnaHQ9IjciIC8+CiAgPC9nPgogIDxwYXRoCiAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICBpZD0icGF0aDg1IgogICAgIGRpc3BsYXk9ImJsb2NrIgogICAgIGQ9Ik0gMzEuMjczOTUyLDAuNjI5NyA0LjMzNjI1LDAuNjM5NzUwMDggYyAtMC43Nzc3MjgyLDAgLTEuNTE5NjU2NiwwLjU1NTQ3NDUyIC0xLjUxOTY1NjYsMS4yNTMwOTIwMiBMIDIuOTI0NTE1MSw0NC4yMjMzNDIgYyAwLDAuNjk3NjE4IDAuNjM0MDUzMSwxLjI2MzAzMyAxLjQxMTcyMDIsMS4yNjMwMzMgSCA0NC4xMzM1MDkgYyAwLjc3NzcyOSwwIDEuNDExODQzLC0wLjU2NTQ3IDEuNDExNzIxLC0xLjI2MzAzMyBWIDEuODYwMDcwMSBjIDAsLTAuNjk2NTE0MiAtMC42MzM2OTksLTEuMjI3ODU3NTggLTEuNDExNDc2LC0xLjIyNzg1NzU4IEggMzEuMjc1NDAzIFoiCiAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZGlzcGxheTpibG9jaztmaWxsOnVybCgjeik7c3Ryb2tlOnVybCgjYWEpO3N0cm9rZS13aWR0aDoxLjE1NDkxMTQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kIiAvPgogIDxwYXRoCiAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICBpZD0icGF0aDg3IgogICAgIGRpc3BsYXk9ImJsb2NrIgogICAgIGQ9Ik0gNC4yMTM0MTc0LDEuNzI5MzA5OCBIIDQ0LjAxMTkxMiBjIDAuMTM2NDU5LC0wLjAwNTU4IDAuMzUzMTAxLDAuMDcxNiAwLjMzNjIxNSwwLjIxMDE2NjkgTCA0NC4yNTQxMSw0NC4xOTU2OTMgYyAwLDAuMDkwMiAtMC4wODExNywwLjE2MjgyMyAtMC4xODE5OSwwLjE2MjgyMyBIIDQuNDE2NDgxOSBjIC0wLjEwMDgyMTYsMCAtMC4xODE5OSwtMC4wNzI2MiAtMC4xODE5OSwtMC4xNjI4MjMgTCA0LjAzMjE3MjIsMS44OTI1MDM1IGMgMCwtMC4wOTAyMDMgMC4wODExNjcsLTAuMTYyODIyMiAwLjE4MTk5LC0wLjE2MjgyMjIgeiIKICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtkaXNwbGF5OmJsb2NrO2ZpbGw6bm9uZTtzdHJva2U6dXJsKCNhYik7c3Ryb2tlLXdpZHRoOjEuMTU0OTExNDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQiIC8+CiAgPHBhdGgKICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojYmFiZGI2O3N0cm9rZS13aWR0aDoxLjE1NDkxMTRweDtzdHJva2UtbGluZWNhcDpyb3VuZCIKICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgIGlkPSJwYXRoMTMxIgogICAgIGQ9Ik0gMTAuMTM2LDcuMTg0MSBIIDM4LjIxOSIgLz4KICA8cGF0aAogICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNiYWJkYjY7c3Ryb2tlLXdpZHRoOjEuMTU0OTExNHB4O3N0cm9rZS1saW5lY2FwOnJvdW5kIgogICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgaWQ9InBhdGgxMzMiCiAgICAgZD0iTSAxMC4xMzYsMTAuNDYxMyBIIDM4LjIxOSIgLz4KICA8ZwogICAgIGlkPSJnMzA5MCIKICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjgyMjc4MTgzLDAsMCwwLjgzNzM1OTQ3LC00NDEuODIyMjYsLTUwLjk3NzgyMykiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7c3Ryb2tlOiMzMDJiMDA7c3Ryb2tlLXdpZHRoOjIuMDE3NjM5NjQ7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNTgwLjE3MzczLDkxLjgzNjU2MSA1LjA4ODU5LDE3Ljk5OTk5OSAtMTcuMzAxMiwtNiAtMi4wMzU0MywtMy45OTk5OTkgMTAuMTc3MTcsLTEwIHoiCiAgICAgICBpZD0icGF0aDM5NjkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6I2ZjZTk0ZjtzdHJva2U6bm9uZSIKICAgICAgIGQ9Im0gNTU5LjgxOTM4LDcxLjgzNjU1NSAtNC4wNzA4Nyw0IDIwLjM1NDM1LDIwLjAwMDAwNiBjIDAsLTMgMS4wMTc3MiwtNCA0LjA3MDg3LC00IHoiCiAgICAgICBpZD0icGF0aDM4NDMiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDojZWRkNDAwO3N0cm9rZTpub25lIgogICAgICAgZD0ibSA1NTUuNzQ4NTEsNzUuODM2NTQ4IC00LjA3MDg3LDQuMDAwMDEzIDIwLjM1NDM1LDIwIGMgMCwtMyAxLjAxNzcyLC00IDQuMDcwODcsLTQgeiIKICAgICAgIGlkPSJwYXRoMzg0My03IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6I2M0YTAwMDtzdHJva2U6bm9uZSIKICAgICAgIGQ9Im0gNTUxLjY3NzY0LDc5LjgzNjU2MSAtNC4wNzA4Nyw0IDIwLjM1NDM1LDE5Ljk5OTk5OSBjIDAsLTMgMS4wMTc3MiwtMy45OTk5OTkgNC4wNzA4NywtMy45OTk5OTkgeiIKICAgICAgIGlkPSJwYXRoMzg0My01IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6I2VkZDQwMDtzdHJva2Utd2lkdGg6Mi4wMTc2Mzk2NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNTUwLjY1OTkzLDgxLjYzNjU1MyAxOC4zMTg5MSwxOC4wMDAwMSIKICAgICAgIGlkPSJwYXRoMzg4OCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmY2U5NGY7c3Ryb2tlLXdpZHRoOjIuMDE3NjM5NjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDU1NC43MzA3OSw3Ny42MzY1NTcgNTczLjA0OTcxLDk1LjYzNjU2IgogICAgICAgaWQ9InBhdGgzODg4LTUiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzAyYjAwO3N0cm9rZS13aWR0aDoyLjAxNzYzOTY0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDU1OS44MTkzOCw3MS44MzY1NTggLTQuMDcwODcsNCAyMC4zNTQzNSwyMC4wMDAwMDMgYyAwLC0zIDEuMDE3NzIsLTQgNC4wNzA4NywtNCB6IgogICAgICAgaWQ9InBhdGgzODQzLTUtNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMzMDJiMDA7c3Ryb2tlLXdpZHRoOjIuMDE3NjM5NjQ7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNTU1Ljc0ODUxLDc1LjgzNjU1OCAtNC4wNzA4Nyw0LjAwMDAwMyAyMC4zNTQzNSwyMCBjIDAsLTMgMS4wMTc3MiwtNCA0LjA3MDg3LC00IHoiCiAgICAgICBpZD0icGF0aDM4NDMtNS02LTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzAyYjAwO3N0cm9rZS13aWR0aDoyLjAxNzYzOTY0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDU1MS42Nzc2NCw3OS44MzY1NjEgLTQuMDcwODcsNCAyMC4zNTQzNSwxOS45OTk5OTkgYyAwLC0zIDEuMDE3NzIsLTMuOTk5OTk5IDQuMDcwODcsLTMuOTk5OTk5IHoiCiAgICAgICBpZD0icGF0aDM4NDMtNS02LTkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDojMzAyYjAwO3N0cm9rZTojMzAyYjAwO3N0cm9rZS13aWR0aDoxLjAwODgxOTgycHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNTgzLjIyNjg4LDEwMC44MzY1NiBjIC0yLjAzNTQzLDAgLTUuNjY5MjgsMi41MTYyMyAtNi4xMDYzLDUgbCA4LjE0MTc0LDQgLTIuMDM1NDQsLTggdiAwIgogICAgICAgaWQ9InBhdGgzOTcxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2MiIC8+CiAgPC9nPgo8L3N2Zz4K #""" closeW_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0IgogICBoZWlnaHQ9IjY0IgogICBpZD0ic3ZnMiIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ4LjUgcjEwMDQwIgogICBzb2RpcG9kaTpkb2NuYW1lPSJlZGl0X0NhbmNlbC5zdmciCiAgIHZpZXdCb3g9IjAgMCA2NCA2NCI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODc5IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIj4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODgxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNhNDAwMDA7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg4MyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZWYyOTI5O3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2OSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNhNDAwMDA7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODcxIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZWYyOTI5O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzg3MyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4NjkiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODc1IgogICAgICAgeDE9Ii00NSIKICAgICAgIHkxPSIxMDQ0LjM2MjIiCiAgICAgICB4Mj0iLTU1IgogICAgICAgeTI9Ijk5NC4zNjIxOCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjg2NjQ3NzI3LDAsMCwwLjg2NjQ3NzM5LDczLjY1MzQwOSwxMzYuMzAzOTEpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODc5IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg3NyIKICAgICAgIHgxPSItNDUiCiAgICAgICB5MT0iMTA0NC4zNjIyIgogICAgICAgeDI9Ii01NSIKICAgICAgIHkyPSI5OTQuMzYyMTgiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44NjY0NzcyNywwLDAsMC44NjY0NzczOSw3My42NTM0MDksMTM2LjMwMzkxKSIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjYuNTU3NzM4IgogICAgIGlua3NjYXBlOmN4PSI1MC4yNzE5NTgiCiAgICAgaW5rc2NhcGU6Y3k9IjMyLjkwMDkyNCIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTU5OCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4MzYiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjAiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjI3IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMTE1MjEiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgZG90dGVkPSJmYWxzZSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsLTk4OC4zNjIxOCkiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMyODAwMDA7c3Ryb2tlLXdpZHRoOjE2O3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gMTMsMTAwMS4zNjIyIDM4LjEyNSwzOC4xMjUiCiAgICAgICBpZD0icGF0aDMwMDIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMjgwMDAwO3N0cm9rZS13aWR0aDoxNjtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDUxLjEyNSwxMDAxLjM2MjIgMTMsMTAzOS40ODcyIgogICAgICAgaWQ9InBhdGgzMDAyLTYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZWYyOTI5O3N0cm9rZS13aWR0aDoxMjtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDEzLDEwMDEuMzYyMiAzOC4xMjUsMzguMTI1IgogICAgICAgaWQ9InBhdGgzMDAyLTciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZWYyOTI5O3N0cm9rZS13aWR0aDoxMjtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJNIDUxLjEyNSwxMDAxLjM2MjIgMTMsMTAzOS40ODcyIgogICAgICAgaWQ9InBhdGgzMDAyLTYtNSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOnVybCgjbGluZWFyR3JhZGllbnQzODc3KTtzdHJva2Utd2lkdGg6ODtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDEzLDEwMDEuMzYyMiAzOC4xMjUsMzguMTI1IgogICAgICAgaWQ9InBhdGgzMDAyLTctNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOnVybCgjbGluZWFyR3JhZGllbnQzODc1KTtzdHJva2Utd2lkdGg6ODtzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJNIDUxLjEyNSwxMDAxLjM2MjIgMTMsMTAzOS40ODcyIgogICAgICAgaWQ9InBhdGgzMDAyLTYtNS0yIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgPC9nPgo8L3N2Zz4K """ un_dock_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpvc2I9Imh0dHA6Ly93d3cub3BlbnN3YXRjaGJvb2sub3JnL3VyaS8yMDA5L29zYiIKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMCIKICAgeD0iMC4wMDAwMDAwIgogICB5PSIwLjAwMDAwMDAiCiAgIHdpZHRoPSI2NCIKICAgaGVpZ2h0PSI2NCIKICAgaWQ9InN2ZzExMzAwIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ4LjQgcjk5MzkiCiAgIHNvZGlwb2RpOmRvY25hbWU9InVuLWRvY2suc3ZnIj4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE2NiI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5Db3JleSBXb29kd29ydGg8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICAgIDxjYzpsaWNlbnNlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS8yLjAvIiAvPgogICAgICAgIDxkYzpzb3VyY2U+aHR0cDovL2ppbW1hYy5tdXNpY2hhbGwuY3o8L2RjOnNvdXJjZT4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkpha3ViIFN0ZWluZXI8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5uZXc8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5pbnNlcnQ8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT53aW5kb3c8L3JkZjpsaT4KICAgICAgICAgIDwvcmRmOkJhZz4KICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvY2M6V29yaz4KICAgICAgPGNjOkxpY2Vuc2UKICAgICAgICAgcmRmOmFib3V0PSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS8yLjAvIj4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3dlYi5yZXNvdXJjZS5vcmcvY2MvUmVwcm9kdWN0aW9uIiAvPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9EaXN0cmlidXRpb24iIC8+CiAgICAgICAgPGNjOnJlcXVpcmVzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9Ob3RpY2UiIC8+CiAgICAgICAgPGNjOnJlcXVpcmVzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vd2ViLnJlc291cmNlLm9yZy9jYy9BdHRyaWJ1dGlvbiIgLz4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3dlYi5yZXNvdXJjZS5vcmcvY2MvRGVyaXZhdGl2ZVdvcmtzIiAvPgogICAgICAgIDxjYzpyZXF1aXJlcwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3dlYi5yZXNvdXJjZS5vcmcvY2MvU2hhcmVBbGlrZSIgLz4KICAgICAgPC9jYzpMaWNlbnNlPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGlkPSJiYXNlIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iLTIuMzI3NTAwNSIKICAgICBpbmtzY2FwZTpjeT0iMTkuMzY5NDQxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnMTEzMDAiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMzAxNiIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8ZGVmcwogICAgIGlkPSJkZWZzMyI+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQ2MzMiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzMzMzMzO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQ2MjkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzMzMzMzM7c3RvcC1vcGFjaXR5OjA7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDYzMSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MzIiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQ2MjQiCiAgICAgICAgIG9mZnNldD0iMC4wMDAwMDAwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxIiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDQ2MjYiCiAgICAgICAgIG9mZnNldD0iMS4wMDAwMDAwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZDNkN2NmO3N0b3Atb3BhY2l0eToxIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTA2MCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDUwMzEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTIuNzc0Mzg5LDAsMCwxLjk2OTcwNiwxMTIuNzYyMywtODcyLjg4NTQpIgogICAgICAgY3g9IjYwNS43MTQyOSIKICAgICAgIGN5PSI0ODYuNjQ3ODkiCiAgICAgICBmeD0iNjA1LjcxNDI5IgogICAgICAgZnk9IjQ4Ni42NDc4OSIKICAgICAgIHI9IjExNy4xNDI4NiIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NTA2MCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOmJsYWNrO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDUwNjIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOmJsYWNrO3N0b3Atb3BhY2l0eTowOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDUwNjQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ1MDYwIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50NTAyOSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjc3NDM4OSwwLDAsMS45Njk3MDYsLTE4OTEuNjMzLC04NzIuODg1NCkiCiAgICAgICBjeD0iNjA1LjcxNDI5IgogICAgICAgY3k9IjQ4Ni42NDc4OSIKICAgICAgIGZ4PSI2MDUuNzE0MjkiCiAgICAgICBmeT0iNDg2LjY0Nzg5IgogICAgICAgcj0iMTE3LjE0Mjg2IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ1MDQ4Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6YmxhY2s7c3RvcC1vcGFjaXR5OjA7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNTA1MCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3A1MDU2IgogICAgICAgICBvZmZzZXQ9IjAuNSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6YmxhY2s7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjpibGFjaztzdG9wLW9wYWNpdHk6MDsiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3A1MDUyIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTA0OCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDUwMjciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi43NzQzODksMCwwLDEuOTY5NzA2LC0xODkyLjE3OSwtODcyLjg4NTQpIgogICAgICAgeDE9IjMwMi44NTcxNSIKICAgICAgIHkxPSIzNjYuNjQ3ODkiCiAgICAgICB4Mj0iMzAyLjg1NzE1IgogICAgICAgeTI9IjYwOS41MDUwNyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MTI1MTIiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxLjAwMDAwMDAiCiAgICAgICAgIG9mZnNldD0iMC4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDEyNTEzIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmNTIwO3N0b3Atb3BhY2l0eTowLjg5MTA4OTA4IgogICAgICAgICBvZmZzZXQ9IjAuNTAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wMTI1MTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmYzMDA7c3RvcC1vcGFjaXR5OjAuMDAwMDAwMCIKICAgICAgICAgb2Zmc2V0PSIxLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wMTI1MTQiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0ODE2Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzM0NjVhNDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wNDgxOCIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzcyOWZjZjtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIxLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wNDgyMCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQ4MDgiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMDAwMDAwO3N0b3Atb3BhY2l0eToxLjAwMDAwMDAiCiAgICAgICAgIG9mZnNldD0iMC4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDQ4MTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjAuMDAwMDAwMCIKICAgICAgICAgb2Zmc2V0PSIxLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wNDgxMiIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MzItNSIKICAgICAgIG9zYjpwYWludD0iZ3JhZGllbnQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AzODM0IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZDNkN2NmO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AzODM2IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDgwOCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDEzNzIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMS45NDU3MzQsMCwwLDAuNjI3Mjc0LC05LjYyODQ1NSwyNC4yNTkyMSkiCiAgICAgICBjeD0iMTcuMzY4MzExIgogICAgICAgY3k9IjI1LjY4MTk0MiIKICAgICAgIGZ4PSIxNy4zNjgzMTEiCiAgICAgICBmeT0iMjUuNjgxOTQyIgogICAgICAgcj0iMTEuNzk5ODQ1IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODMyLTUiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMjY3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDMuMjUzOTM1LDAsMCwzLjI5NTQ1MTcsLTE2LjM2NDA3NCwtMjQuNTQ2OTQzKSIKICAgICAgIHgxPSIxMy44MTczOTkiCiAgICAgICB5MT0iOC42NjQ3ODI1IgogICAgICAgeDI9IjE3Ljg4MDY4IgogICAgICAgeTI9IjIxLjc2NzU3OCIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTA2MCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDUwMjktOCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjc3NDM4OSwwLDAsMS45Njk3MDYsLTE4OTEuNjMzLC04NzIuODg1NCkiCiAgICAgICBjeD0iNjA1LjcxNDI5IgogICAgICAgY3k9IjQ4Ni42NDc4OSIKICAgICAgIGZ4PSI2MDUuNzE0MjkiCiAgICAgICBmeT0iNDg2LjY0Nzg5IgogICAgICAgcj0iMTE3LjE0Mjg2IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ1MDYwIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50NTAzMS0yIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0yLjc3NDM4OSwwLDAsMS45Njk3MDYsMTEyLjc2MjMsLTg3Mi44ODU0KSIKICAgICAgIGN4PSI2MDUuNzE0MjkiCiAgICAgICBjeT0iNDg2LjY0Nzg5IgogICAgICAgZng9IjYwNS43MTQyOSIKICAgICAgIGZ5PSI0ODYuNjQ3ODkiCiAgICAgICByPSIxMTcuMTQyODYiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNDgiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQxMTIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi43NzQzODksMCwwLDEuOTY5NzA2LC0xODkyLjE3OSwtODcyLjg4NTQpIgogICAgICAgeDE9IjMwMi44NTcxNSIKICAgICAgIHkxPSIzNjYuNjQ3ODkiCiAgICAgICB4Mj0iMzAyLjg1NzE1IgogICAgICAgeTI9IjYwOS41MDUwNyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDYzMyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQ2MzUiCiAgICAgICB4MT0iNTguNDQ2OTY4IgogICAgICAgeTE9IjU4Ljc3NjU0NiIKICAgICAgIHgyPSIzMy43NTQ1NTkiCiAgICAgICB5Mj0iMTIuNDYxMDUxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNDgiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDQ5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNzc0Mzg5LDAsMCwxLjk2OTcwNiwtMTg5Mi4xNzksLTg3Mi44ODU0KSIKICAgICAgIHgxPSIzMDIuODU3MTUiCiAgICAgICB5MT0iMzY2LjY0Nzg5IgogICAgICAgeDI9IjMwMi44NTcxNSIKICAgICAgIHkyPSI2MDkuNTA1MDciIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNjAiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDUxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNzc0Mzg5LDAsMCwxLjk2OTcwNiwtMTg5MS42MzMsLTg3Mi44ODU0KSIKICAgICAgIGN4PSI2MDUuNzE0MjkiCiAgICAgICBjeT0iNDg2LjY0Nzg5IgogICAgICAgZng9IjYwNS43MTQyOSIKICAgICAgIGZ5PSI0ODYuNjQ3ODkiCiAgICAgICByPSIxMTcuMTQyODYiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNjAiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDUzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0yLjc3NDM4OSwwLDAsMS45Njk3MDYsMTEyLjc2MjMsLTg3Mi44ODU0KSIKICAgICAgIGN4PSI2MDUuNzE0MjkiCiAgICAgICBjeT0iNDg2LjY0Nzg5IgogICAgICAgZng9IjYwNS43MTQyOSIKICAgICAgIGZ5PSI0ODYuNjQ3ODkiCiAgICAgICByPSIxMTcuMTQyODYiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4MzItNSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwNTUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMy4yNTM5MzUsMCwwLDMuMjk1NDUxNywtMTYuMzY0MDc0LC0yNC41NDY5NDMpIgogICAgICAgeDE9IjEzLjgxNzM5OSIKICAgICAgIHkxPSI4LjY2NDc4MjUiCiAgICAgICB4Mj0iMTcuODgwNjgiCiAgICAgICB5Mj0iMjEuNzY3NTc4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ1MDQ4IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA1NyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjc3NDM4OSwwLDAsMS45Njk3MDYsLTE4OTIuMTc5LC04NzIuODg1NCkiCiAgICAgICB4MT0iMzAyLjg1NzE1IgogICAgICAgeTE9IjM2Ni42NDc4OSIKICAgICAgIHgyPSIzMDIuODU3MTUiCiAgICAgICB5Mj0iNjA5LjUwNTA3IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ1MDYwIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzA1OSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjc3NDM4OSwwLDAsMS45Njk3MDYsLTE4OTEuNjMzLC04NzIuODg1NCkiCiAgICAgICBjeD0iNjA1LjcxNDI5IgogICAgICAgY3k9IjQ4Ni42NDc4OSIKICAgICAgIGZ4PSI2MDUuNzE0MjkiCiAgICAgICBmeT0iNDg2LjY0Nzg5IgogICAgICAgcj0iMTE3LjE0Mjg2IiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ1MDYwIgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzA2MSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMi43NzQzODksMCwwLDEuOTY5NzA2LDExMi43NjIzLC04NzIuODg1NCkiCiAgICAgICBjeD0iNjA1LjcxNDI5IgogICAgICAgY3k9IjQ4Ni42NDc4OSIKICAgICAgIGZ4PSI2MDUuNzE0MjkiCiAgICAgICBmeT0iNDg2LjY0Nzg5IgogICAgICAgcj0iMTE3LjE0Mjg2IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0NjMzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA2MyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjU4LjQ0Njk2OCIKICAgICAgIHkxPSI1OC43NzY1NDYiCiAgICAgICB4Mj0iMzMuNzU0NTU5IgogICAgICAgeTI9IjEyLjQ2MTA1MSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTA0OCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwODMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi43NzQzODksMCwwLDEuOTY5NzA2LC0xODkyLjE3OSwtODcyLjg4NTQpIgogICAgICAgeDE9IjMwMi44NTcxNSIKICAgICAgIHkxPSIzNjYuNjQ3ODkiCiAgICAgICB4Mj0iMzAyLjg1NzE1IgogICAgICAgeTI9IjYwOS41MDUwNyIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTA2MCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwODUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi43NzQzODksMCwwLDEuOTY5NzA2LC0xODkxLjYzMywtODcyLjg4NTQpIgogICAgICAgY3g9IjYwNS43MTQyOSIKICAgICAgIGN5PSI0ODYuNjQ3ODkiCiAgICAgICBmeD0iNjA1LjcxNDI5IgogICAgICAgZnk9IjQ4Ni42NDc4OSIKICAgICAgIHI9IjExNy4xNDI4NiIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTA2MCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwODciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTIuNzc0Mzg5LDAsMCwxLjk2OTcwNiwxMTIuNzYyMywtODcyLjg4NTQpIgogICAgICAgY3g9IjYwNS43MTQyOSIKICAgICAgIGN5PSI0ODYuNjQ3ODkiCiAgICAgICBmeD0iNjA1LjcxNDI5IgogICAgICAgZnk9IjQ4Ni42NDc4OSIKICAgICAgIHI9IjExNy4xNDI4NiIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDYzMyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwODkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSI1OC40NDY5NjgiCiAgICAgICB5MT0iNTguNzc2NTQ2IgogICAgICAgeDI9IjMzLjc1NDU1OSIKICAgICAgIHkyPSIxMi40NjEwNTEiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNDgiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMDkxIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNzc0Mzg5LDAsMCwxLjk2OTcwNiwtMTg5Mi4xNzksLTg3Mi44ODU0KSIKICAgICAgIHgxPSIzMDIuODU3MTUiCiAgICAgICB5MT0iMzY2LjY0Nzg5IgogICAgICAgeDI9IjMwMi44NTcxNSIKICAgICAgIHkyPSI2MDkuNTA1MDciIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNjAiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDkzIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNzc0Mzg5LDAsMCwxLjk2OTcwNiwtMTg5MS42MzMsLTg3Mi44ODU0KSIKICAgICAgIGN4PSI2MDUuNzE0MjkiCiAgICAgICBjeT0iNDg2LjY0Nzg5IgogICAgICAgZng9IjYwNS43MTQyOSIKICAgICAgIGZ5PSI0ODYuNjQ3ODkiCiAgICAgICByPSIxMTcuMTQyODYiIC8+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDUwNjAiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQzMDk1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0yLjc3NDM4OSwwLDAsMS45Njk3MDYsMTEyLjc2MjMsLTg3Mi44ODU0KSIKICAgICAgIGN4PSI2MDUuNzE0MjkiCiAgICAgICBjeT0iNDg2LjY0Nzg5IgogICAgICAgZng9IjYwNS43MTQyOSIKICAgICAgIGZ5PSI0ODYuNjQ3ODkiCiAgICAgICByPSIxMTcuMTQyODYiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4MzItNSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDMwOTciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMy4yNTM5MzUsMCwwLDMuMjk1NDUxNywtMTYuMzY0MDc0LC0yNC41NDY5NDMpIgogICAgICAgeDE9IjEzLjgxNzM5OSIKICAgICAgIHkxPSI4LjY2NDc4MjUiCiAgICAgICB4Mj0iMTcuODgwNjgiCiAgICAgICB5Mj0iMjEuNzY3NTc4IiAvPgogIDwvZGVmcz4KICA8ZwogICAgIGlkPSJnMzA2NSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMS4wMjA0MzMzLC0xLjgwNzcwODgpIj4KICAgIDxnCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjg1NzM3NSwwLDAsMC44NTczNzUsNi41OTQ2MDc4LDYuNjgwMDQxMikiCiAgICAgICBpZD0iZzMwNDAiPgogICAgICA8ZwogICAgICAgICBpZD0iZzQ2NDMiPgogICAgICAgIDxnCiAgICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC4wMjg0NDcxMiwwLDAsMC4wMTg4NjY2NSw2MS44NTY3MDYsNTkuMjAwODI2KSIKICAgICAgICAgICBpZD0iZzUwMjItMiIKICAgICAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmUiPgogICAgICAgICAgPHJlY3QKICAgICAgICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuNDAyMDYxODU7Y29sb3I6IzAwMDAwMDtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDgzKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MTttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZSIKICAgICAgICAgICAgIGlkPSJyZWN0NDE3My0xIgogICAgICAgICAgICAgd2lkdGg9IjEzMzkuNjMzNSIKICAgICAgICAgICAgIGhlaWdodD0iNDc4LjM1NzE4IgogICAgICAgICAgICAgeD0iLTE1NTkuMjUyMyIKICAgICAgICAgICAgIHk9Ii0xNTAuNjk2ODUiIC8+CiAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuNDAyMDYxODU7Y29sb3I6IzAwMDAwMDtmaWxsOnVybCgjcmFkaWFsR3JhZGllbnQzMDg1KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MTttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZSIKICAgICAgICAgICAgIGQ9Im0gLTIxOS42MTg3NiwtMTUwLjY4MDM4IGMgMCwwIDAsNDc4LjMzMDc5IDAsNDc4LjMzMDc5IDE0Mi44NzQxNjYsMC45MDA0NSAzNDUuNDAwMjIsLTEwNy4xNjk2NiAzNDUuNDAwMTQsLTIzOS4xOTYxNzUgMCwtMTMyLjAyNjUzNyAtMTU5LjQzNjgxNiwtMjM5LjEzNDU5NSAtMzQ1LjQwMDE0LC0yMzkuMTM0NjE1IHoiCiAgICAgICAgICAgICBpZD0icGF0aDUwNTgtMCIKICAgICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjYyIgLz4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjIgogICAgICAgICAgICAgaWQ9InBhdGg1MDE4LTUiCiAgICAgICAgICAgICBkPSJtIC0xNTU5LjI1MjMsLTE1MC42ODAzOCBjIDAsMCAwLDQ3OC4zMzA3OSAwLDQ3OC4zMzA3OSAtMTQyLjg3NDIsMC45MDA0NSAtMzQ1LjQwMDIsLTEwNy4xNjk2NiAtMzQ1LjQwMDIsLTIzOS4xOTYxNzUgMCwtMTMyLjAyNjUzNyAxNTkuNDM2OCwtMjM5LjEzNDU5NSAzNDUuNDAwMiwtMjM5LjEzNDYxNSB6IgogICAgICAgICAgICAgc3R5bGU9Im9wYWNpdHk6MC40MDIwNjE4NTtjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDMwODcpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlIiAvPgogICAgICAgIDwvZz4KICAgICAgICA8cmVjdAogICAgICAgICAgIGlkPSJyZWN0MzgxOC04IgogICAgICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMwODkpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojOGQ4ZDhkO3N0cm9rZS13aWR0aDoxLjgwNDk5OTk1O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZSIKICAgICAgICAgICB5PSIxMC4zMzAxOCIKICAgICAgICAgICB4PSIxMC4zODIzMjEiCiAgICAgICAgICAgcnk9IjEuNDExOTg0OSIKICAgICAgICAgICByeD0iMS4yODY1ODAzIgogICAgICAgICAgIGhlaWdodD0iNTIuMzQ1MDAxIgogICAgICAgICAgIHdpZHRoPSI1Mi4zNDUwMDEiIC8+CiAgICAgICAgPHJlY3QKICAgICAgICAgICBpZD0icmVjdDQ5NjItNyIKICAgICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjEuODA0OTk5OTU7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlIgogICAgICAgICAgIHk9IjEyLjEzNTE4IgogICAgICAgICAgIHg9IjEyLjE4NzMyMiIKICAgICAgICAgICByeT0iMCIKICAgICAgICAgICByeD0iMCIKICAgICAgICAgICBoZWlnaHQ9IjQ4LjczNTAwMSIKICAgICAgICAgICB3aWR0aD0iNDguNzM1MDAxIiAvPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8ZwogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC44NTczNzUsMCwwLDAuODU3Mzc1LDQuMDA4NDg0OSw0LjExNjg4NjQpIgogICAgICAgaWQ9ImczMDMxIj4KICAgICAgPGcKICAgICAgICAgaWQ9ImcxNDYiCiAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuNCwwKSI+CiAgICAgICAgPGcKICAgICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjAyODQ0NzEyLDAsMCwwLjAxODg2NjY1LDUzLjAwNjk0NSw1MC42NjA3NTcpIgogICAgICAgICAgIGlkPSJnNTAyMiIKICAgICAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmUiPgogICAgICAgICAgPHJlY3QKICAgICAgICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuNDAyMDYxODU7Y29sb3I6IzAwMDAwMDtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDkxKTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MTttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZSIKICAgICAgICAgICAgIGlkPSJyZWN0NDE3MyIKICAgICAgICAgICAgIHdpZHRoPSIxMzM5LjYzMzUiCiAgICAgICAgICAgICBoZWlnaHQ9IjQ3OC4zNTcxOCIKICAgICAgICAgICAgIHg9Ii0xNTU5LjI1MjMiCiAgICAgICAgICAgICB5PSItMTUwLjY5Njg1IiAvPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBzdHlsZT0ib3BhY2l0eTowLjQwMjA2MTg1O2NvbG9yOiMwMDAwMDA7ZmlsbDp1cmwoI3JhZGlhbEdyYWRpZW50MzA5Myk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjE7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGUiCiAgICAgICAgICAgICBkPSJtIC0yMTkuNjE4NzYsLTE1MC42ODAzOCBjIDAsMCAwLDQ3OC4zMzA3OSAwLDQ3OC4zMzA3OSAxNDIuODc0MTY2LDAuOTAwNDUgMzQ1LjQwMDIyLC0xMDcuMTY5NjYgMzQ1LjQwMDE0LC0yMzkuMTk2MTc1IDAsLTEzMi4wMjY1MzcgLTE1OS40MzY4MTYsLTIzOS4xMzQ1OTUgLTM0NS40MDAxNCwtMjM5LjEzNDYxNSB6IgogICAgICAgICAgICAgaWQ9InBhdGg1MDU4IgogICAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjIiAvPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2MiCiAgICAgICAgICAgICBpZD0icGF0aDUwMTgiCiAgICAgICAgICAgICBkPSJtIC0xNTU5LjI1MjMsLTE1MC42ODAzOCBjIDAsMCAwLDQ3OC4zMzA3OSAwLDQ3OC4zMzA3OSAtMTQyLjg3NDIsMC45MDA0NSAtMzQ1LjQwMDIsLTEwNy4xNjk2NiAtMzQ1LjQwMDIsLTIzOS4xOTYxNzUgMCwtMTMyLjAyNjUzNyAxNTkuNDM2OCwtMjM5LjEzNDU5NSAzNDUuNDAwMiwtMjM5LjEzNDYxNSB6IgogICAgICAgICAgICAgc3R5bGU9Im9wYWNpdHk6MC40MDIwNjE4NTtjb2xvcjojMDAwMDAwO2ZpbGw6dXJsKCNyYWRpYWxHcmFkaWVudDMwOTUpO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDoxO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlIiAvPgogICAgICAgIDwvZz4KICAgICAgICA8cmVjdAogICAgICAgICAgIGlkPSJyZWN0MzgxOCIKICAgICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOnVybCgjbGluZWFyR3JhZGllbnQzMDk3KTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzhkOGQ4ZDtzdHJva2Utd2lkdGg6MS44MDQ5OTk5NTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGUiCiAgICAgICAgICAgeT0iMS43OTAxMTE0IgogICAgICAgICAgIHg9IjEuNTMyNTYzMSIKICAgICAgICAgICByeT0iMS40MTE5ODQ5IgogICAgICAgICAgIHJ4PSIxLjI4NjU4MDMiCiAgICAgICAgICAgaGVpZ2h0PSI1Mi4zNDUwMDEiCiAgICAgICAgICAgd2lkdGg9IjUyLjM0NTAwMSIgLz4KICAgICAgICA8cmVjdAogICAgICAgICAgIGlkPSJyZWN0NDk2MiIKICAgICAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjEuODA0OTk5OTU7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlIgogICAgICAgICAgIHk9IjMuNTk1MTExNCIKICAgICAgICAgICB4PSIzLjMzNzU2MyIKICAgICAgICAgICByeT0iMCIKICAgICAgICAgICByeD0iMCIKICAgICAgICAgICBoZWlnaHQ9IjQ4LjczNTAwMSIKICAgICAgICAgICB3aWR0aD0iNDguNzM1MDAxIiAvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K """ dock_right_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNjRweCIKICAgaGVpZ2h0PSI2NHB4IgogICBpZD0ic3ZnMjk4NSIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ4LjQgcjk5MzkiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImRvY2stbGVmdC5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyOTg3IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI1LjA5NjgzMTIiCiAgICAgaW5rc2NhcGU6Y3g9Ii01OS45NzI4ODUiCiAgICAgaW5rc2NhcGU6Y3k9IjE2LjE5MzQxMyIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTM2MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9ImZhbHNlIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDI5ODciCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhMjk5MCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlt5b3Jpa3ZhbmhhdnJlXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6dGl0bGU+QXJjaF9TZWN0aW9uUGxhbmVfVHJlZTwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmRhdGU+MjAxMS0xMi0wNjwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9BcmNoL1Jlc291cmNlcy9pY29ucy9BcmNoX1NlY3Rpb25QbGFuZV9UcmVlLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHBhdGgKICAgICAgIHNvZGlwb2RpOnR5cGU9InN0YXIiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiM4MDgwODA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiM0ZDRkNGQ7c3Ryb2tlLXdpZHRoOjEuNTc0ODgyMjc7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI5OTciCiAgICAgICBzb2RpcG9kaTpzaWRlcz0iMyIKICAgICAgIHNvZGlwb2RpOmN4PSIyMiIKICAgICAgIHNvZGlwb2RpOmN5PSIxNy4wOTA5MDgiCiAgICAgICBzb2RpcG9kaTpyMT0iMjAuNDMyNTEyIgogICAgICAgc29kaXBvZGk6cjI9IjEwLjIxNjI1NyIKICAgICAgIHNvZGlwb2RpOmFyZzE9IjIuMDk0Mzk1MSIKICAgICAgIHNvZGlwb2RpOmFyZzI9IjMuMTQxNTkyNyIKICAgICAgIGlua3NjYXBlOmZsYXRzaWRlZD0idHJ1ZSIKICAgICAgIGlua3NjYXBlOnJvdW5kZWQ9IjAiCiAgICAgICBpbmtzY2FwZTpyYW5kb21pemVkPSIwIgogICAgICAgZD0ibSAxMS43ODM3NDQsMzQuNzg1OTgzIDAsLTM1LjM5MDE0OTYzIDMwLjY0ODc2OCwxNy42OTUwNzQ2MyB6IgogICAgICAgaW5rc2NhcGU6dHJhbnNmb3JtLWNlbnRlci14PSItNi42Njg0MTU5IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4zMDU0NTI5LDAsMCwxLjEzMDA0NzYsLTMuMjAyMzc3OCwxMi42ODY0NTkpIiAvPgogIDwvZz4KPC9zdmc+Cg== """ dock_left_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNjRweCIKICAgaGVpZ2h0PSI2NHB4IgogICBpZD0ic3ZnMjk4NSIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ4LjQgcjk5MzkiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImRvY2stbGVmdC5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMyOTg3IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI1LjA5NjgzMTIiCiAgICAgaW5rc2NhcGU6Y3g9Ii01OS45NzI4ODUiCiAgICAgaW5rc2NhcGU6Y3k9IjE2LjE5MzQxMyIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTM2MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOnNuYXAtbm9kZXM9ImZhbHNlIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDI5ODciCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhMjk5MCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlt5b3Jpa3ZhbmhhdnJlXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6dGl0bGU+QXJjaF9TZWN0aW9uUGxhbmVfVHJlZTwvZGM6dGl0bGU+CiAgICAgICAgPGRjOmRhdGU+MjAxMS0xMi0wNjwvZGM6ZGF0ZT4KICAgICAgICA8ZGM6cmVsYXRpb24+aHR0cDovL3d3dy5mcmVlY2Fkd2ViLm9yZy93aWtpL2luZGV4LnBocD90aXRsZT1BcnR3b3JrPC9kYzpyZWxhdGlvbj4KICAgICAgICA8ZGM6cHVibGlzaGVyPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICAgIDxkYzppZGVudGlmaWVyPkZyZWVDQUQvc3JjL01vZC9BcmNoL1Jlc291cmNlcy9pY29ucy9BcmNoX1NlY3Rpb25QbGFuZV9UcmVlLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHBhdGgKICAgICAgIHNvZGlwb2RpOnR5cGU9InN0YXIiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiM4MDgwODA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiM0ZDRkNGQ7c3Ryb2tlLXdpZHRoOjEuNTc0ODgyMjcwMDAwMDAwMDA7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI5OTciCiAgICAgICBzb2RpcG9kaTpzaWRlcz0iMyIKICAgICAgIHNvZGlwb2RpOmN4PSIyMiIKICAgICAgIHNvZGlwb2RpOmN5PSIxNy4wOTA5MDgiCiAgICAgICBzb2RpcG9kaTpyMT0iMjAuNDMyNTEyIgogICAgICAgc29kaXBvZGk6cjI9IjEwLjIxNjI1NyIKICAgICAgIHNvZGlwb2RpOmFyZzE9IjIuMDk0Mzk1MSIKICAgICAgIHNvZGlwb2RpOmFyZzI9IjMuMTQxNTkyNyIKICAgICAgIGlua3NjYXBlOmZsYXRzaWRlZD0idHJ1ZSIKICAgICAgIGlua3NjYXBlOnJvdW5kZWQ9IjAiCiAgICAgICBpbmtzY2FwZTpyYW5kb21pemVkPSIwIgogICAgICAgZD0ibSAxMS43ODM3NDQsMzQuNzg1OTgzIDAsLTM1LjM5MDE0OTYzIDMwLjY0ODc2OCwxNy42OTUwNzQ2MyB6IgogICAgICAgaW5rc2NhcGU6dHJhbnNmb3JtLWNlbnRlci14PSI2LjY2ODQxNTkiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgtMS4zMDU0NTI5LDAsMCwxLjEzMDA0NzYsNjcuNTc0MzkxLDEyLjY4NjQ1OSkiIC8+CiAgPC9nPgo8L3N2Zz4K """ stop_grey_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjQ4LjAwMDAwMHB4IgogICBoZWlnaHQ9IjQ4LjAwMDAwMHB4IgogICBpZD0ic3ZnNjM2MSIKICAgc29kaXBvZGk6dmVyc2lvbj0iMC4zMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC40IHI5OTM5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJzdG9wLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzNzk4Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzMzMzMzMztzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODAwIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMzMzMzMzO3N0b3Atb3BhY2l0eTowOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4MDIiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI0IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjQ4IDogMjQgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI0IDogMTYgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlNTIiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIyNTYiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmYwMjAyO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDIyNTgiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZjliOWI7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMjI2MCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIyNDgiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDIyNTAiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjA7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMjI1MiIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDk2NDciPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDk2NDkiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNkYmRiZGI7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wOTY1MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDc4OTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDc4OTciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjA7IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNzg5OSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQ5ODEiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojY2MwMDAwO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDQ5ODMiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNiMzAwMDA7c3RvcC1vcGFjaXR5OjEuMDAwMDAwMDsiCiAgICAgICAgIG9mZnNldD0iMS4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDQ5ODUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQxNTc2MiIKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMTU3NjQiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMTU3NjYiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MDsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQxNDIzNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMTQyMzgiCiAgICAgICAgIG9mZnNldD0iMC4wMDAwMDAwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZWQ0MDQwO3N0b3Atb3BhY2l0eToxLjAwMDAwMDA7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDE0MjQwIgogICAgICAgICBvZmZzZXQ9IjEuMDAwMDAwMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2E0MDAwMDtzdG9wLW9wYWNpdHk6MS4wMDAwMDAwOyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDExNzgwIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmOGI4YjtzdG9wLW9wYWNpdHk6MS4wMDAwMDAwOyIKICAgICAgICAgb2Zmc2V0PSIwLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wMTE3ODIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNlYzFiMWI7c3RvcC1vcGFjaXR5OjEuMDAwMDAwMDsiCiAgICAgICAgIG9mZnNldD0iMS4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDExNzg0IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MTEwMTQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojYTgwMDAwO3N0b3Atb3BhY2l0eToxLjAwMDAwMDA7IgogICAgICAgICBvZmZzZXQ9IjAuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AxMTAxNiIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2M2MDAwMDtzdG9wLW9wYWNpdHk6MS4wMDAwMDAwOyIKICAgICAgICAgb2Zmc2V0PSIwLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wMTMyNDUiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNlNTAwMDA7c3RvcC1vcGFjaXR5OjEuMDAwMDAwMDsiCiAgICAgICAgIG9mZnNldD0iMS4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDExMDE4IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjkuNjUwNzUzMCIKICAgICAgIHgyPSI5Ljg5NDAyMjkiCiAgICAgICB5MT0iNS4zODU1NDI0IgogICAgICAgeDE9IjUuNzM2NTI3MCIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTEuMDAwMDAwLDAuMDAwMDAwLDAuMDAwMDAwLC0xLjAwMDAwMCwzMS43MjE3MCwzMS4yOTA3OSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDE1NzcyIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MTU3NjIiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDc4OTUiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ3OTAxIgogICAgICAgeDE9IjE1LjU3ODg3NSIKICAgICAgIHkxPSIxNi4yODUwODgiCiAgICAgICB4Mj0iMzIuMTY2NDA1IgogICAgICAgeTI9IjI4LjM5NDI5MSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0OTgxIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjI0MyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjIzLjk5NTk4NSIKICAgICAgIHkxPSIyMC4xMDUzMzciCiAgICAgICB4Mj0iNDEuMDQ3ODM2IgogICAgICAgeTI9IjM3Ljk1OTc4NSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45ODgzNzMsMC4wMDAwMDAsMC4wMDAwMDAsMC45ODgzNzMsMC4yNzkwMDIsMC4yNzg5ODQpIiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyMjQ4IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MzAyMiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCg0LjE1NDk1NywwLDAsMy4xOTg3MjMsLTUyLjg0NTUzLC0xNi42MTg2OCkiCiAgICAgICBjeD0iMTYuNzUiCiAgICAgICBjeT0iMTAuNjY2MzQ0IgogICAgICAgZng9IjE2Ljc1IgogICAgICAgZnk9IjEwLjY2NjM0NCIKICAgICAgIHI9IjIxLjI1IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzNzk4IgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzgwNCIKICAgICAgIHgxPSIxLjk5ODQwMDMiCiAgICAgICB5MT0iMjQiCiAgICAgICB4Mj0iNDYuMDAxNiIKICAgICAgIHkyPSIyNCIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBzaG93Z3VpZGVzPSJ0cnVlIgogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIwLjE1Mjk0MTE4IgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIxNiIKICAgICBpbmtzY2FwZTpjeD0iMi4zNDgwNjI2IgogICAgIGlua3NjYXBlOmN5PSI5Ljk5NjEwOSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIyNTYwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzNjEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTpzaG93cGFnZXNoYWRvdz0iZmFsc2UiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE0Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICAgIDxkYzpkYXRlPjIwMDUtMTAtMTY8L2RjOmRhdGU+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5BbmRyZWFzIE5pbHNzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICA8cmRmOkJhZz4KICAgICAgICAgICAgPHJkZjpsaT5zdG9wPC9yZGY6bGk+CiAgICAgICAgICAgIDxyZGY6bGk+aGFsdDwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmVycm9yPC9yZGY6bGk+CiAgICAgICAgICA8L3JkZjpCYWc+CiAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgIDxjYzpsaWNlbnNlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9wdWJsaWNkb21haW4vIiAvPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPkpha3ViIFN0ZWluZXI8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICA8L2NjOldvcms+CiAgICAgIDxjYzpMaWNlbnNlCiAgICAgICAgIHJkZjphYm91dD0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluLyI+CiAgICAgICAgPGNjOnBlcm1pdHMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zI1JlcHJvZHVjdGlvbiIgLz4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjRGlzdHJpYnV0aW9uIiAvPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyNEZXJpdmF0aXZlV29ya3MiIC8+CiAgICAgIDwvY2M6TGljZW5zZT4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzgwNCk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMzMzMzMzM7c3Ryb2tlLXdpZHRoOjE7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgIGQ9Ik0gMTUuNTk1MDIxLDIuNDk3NDk2MiBIIDMyLjY4MDMyNiBMIDQ1LjUwMTYsMTUuNTkxOTYgViAzMy40ODU2MDUgTCAzMi44NTMwMDEsNDUuNTAyNTA0IEggMTUuNDIyNjY0IEwgMi40OTg0MDAzLDMyLjY2MzgzOSBWIDE1LjQ2OTY1MyB6IgogICAgICAgaWQ9InBhdGg5NDgwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjY2MiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuODEzMTg2ODMwMDAwMDAwMDM7ZmlsbDojMzMzMzMzO3N0cm9rZTojMWExYTFhO3N0cm9rZS13aWR0aDoxLjAwMDAwMDI0MDAwMDAwMDEwO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMTYuMDE3ODg3LDMuNTA2MjU2MiBIIDMyLjI0NTc5NSBMIDQ0LjQ5MzY4OCwxNS45Mjg2MzEgViAzMy4wNDI5MTUgTCAzMi42MzU3MDQsNDQuNDkzNzQ0IEggMTUuODY3NDg1IEwgMy41MDYzMTE2LDMyLjIxNDYzMiBWIDE1Ljg1MTUyNCB6IgogICAgICAgaWQ9InBhdGg5NDgyIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjY2MiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuMjg5NzcyNzI7ZmlsbDp1cmwoI3JhZGlhbEdyYWRpZW50MzAyMik7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmUiCiAgICAgICBkPSJNIDE1LjY4NzUsNy42NDA1MzAzIDIuNzUsMjAuNDUzMDMgdiAxNyBsIDIuOTM3NSwyLjkwNjI1IEMgMjIuNDUwMDQxLDQwLjQxNjgyOSAyMi4xNjQ2NjUsMjcuMzQwNTk3IDQ1LjI1LDI4LjQ4NDI4IFYgMjAuNTc4MDMgTCAzMi41NjI1LDcuNjQwNTMwMyB6IgogICAgICAgaWQ9InBhdGgyMjQxIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjYyIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgPC9nPgo8L3N2Zz4K """ stop_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjQ4LjAwMDAwMHB4IgogICBoZWlnaHQ9IjQ4LjAwMDAwMHB4IgogICBpZD0ic3ZnNjM2MSIKICAgc29kaXBvZGk6dmVyc2lvbj0iMC4zMiIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4xIHIxNTM3MSIKICAgc29kaXBvZGk6ZG9jbmFtZT0ic3RvcC5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIgogICB2ZXJzaW9uPSIxLjEiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnMzIj4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI0OCA6IDI0IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNCA6IDE2IDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTUyIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMjU2Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmMDIwMjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AyMjU4IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmY5YjliO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDIyNjAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQyMjQ4Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AyMjUwIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eTowOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDIyNTIiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ5NjQ3Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A5NjQ5IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZGJkYmRiO3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDk2NTEiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ3ODk1Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A3ODk3IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eTowOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDc4OTkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0OTgxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2NjMDAwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0OTgzIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojYjMwMDAwO3N0b3Atb3BhY2l0eToxLjAwMDAwMDA7IgogICAgICAgICBvZmZzZXQ9IjEuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3A0OTg1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MTU3NjIiCiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDE1NzY0IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDE1NzY2IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjA7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MTQyMzYiPgogICAgICA8c3RvcAogICAgICAgICBpZD0ic3RvcDE0MjM4IgogICAgICAgICBvZmZzZXQ9IjAuMDAwMDAwMCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2VkNDA0MDtzdG9wLW9wYWNpdHk6MS4wMDAwMDAwOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AxNDI0MCIKICAgICAgICAgb2Zmc2V0PSIxLjAwMDAwMDAiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNhNDAwMDA7c3RvcC1vcGFjaXR5OjEuMDAwMDAwMDsiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQxMTc4MCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZjhiOGI7c3RvcC1vcGFjaXR5OjEuMDAwMDAwMDsiCiAgICAgICAgIG9mZnNldD0iMC4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDExNzgyIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZWMxYjFiO3N0b3Atb3BhY2l0eToxLjAwMDAwMDA7IgogICAgICAgICBvZmZzZXQ9IjEuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AxMTc4NCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDExMDE0Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2E4MDAwMDtzdG9wLW9wYWNpdHk6MS4wMDAwMDAwOyIKICAgICAgICAgb2Zmc2V0PSIwLjAwMDAwMDAiCiAgICAgICAgIGlkPSJzdG9wMTEwMTYiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjNjAwMDA7c3RvcC1vcGFjaXR5OjEuMDAwMDAwMDsiCiAgICAgICAgIG9mZnNldD0iMC4wMDAwMDAwIgogICAgICAgICBpZD0ic3RvcDEzMjQ1IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZTUwMDAwO3N0b3Atb3BhY2l0eToxLjAwMDAwMDA7IgogICAgICAgICBvZmZzZXQ9IjEuMDAwMDAwMCIKICAgICAgICAgaWQ9InN0b3AxMTAxOCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHkyPSI5LjY1MDc1MzAiCiAgICAgICB4Mj0iOS44OTQwMjI5IgogICAgICAgeTE9IjUuMzg1NTQyNCIKICAgICAgIHgxPSI1LjczNjUyNzAiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0xLjAwMDAwMCwwLjAwMDAwMCwwLjAwMDAwMCwtMS4wMDAwMDAsMzEuNzIxNzAsMzEuMjkwNzkpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQxNTc3MiIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDE1NzYyIgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQxMTc4MCIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDIwNTciCiAgICAgICB4MT0iMTUuNzM3MDAxIgogICAgICAgeTE9IjEyLjUwMzYwMCIKICAgICAgIHgyPSI1My41NzAxMjYiCiAgICAgICB5Mj0iNDcuMzc0MzE3IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC0wLjAwMjc2NzYsMC4wMDU5MTM4KSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDk4MSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQ5ODciCiAgICAgICB4MT0iMjMuOTk1OTg1IgogICAgICAgeTE9IjIwLjEwNTMzNyIKICAgICAgIHgyPSI0MS4wNDc4MzYiCiAgICAgICB5Mj0iMzcuOTU5Nzg1IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMDA0MDE0NjUsMC4wMDU1NzQ4NSkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDc4OTUiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ3OTAxIgogICAgICAgeDE9IjE1LjU3ODg3NSIKICAgICAgIHkxPSIxNi4yODUwODgiCiAgICAgICB4Mj0iMzIuMTY2NDA1IgogICAgICAgeTI9IjI4LjM5NDI5MSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQ0OTgxIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjI0MyIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgeDE9IjIzLjk5NTk4NSIKICAgICAgIHkxPSIyMC4xMDUzMzciCiAgICAgICB4Mj0iNDEuMDQ3ODM2IgogICAgICAgeTI9IjM3Ljk1OTc4NSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC45ODgzNzMsMC4wMDAwMDAsMC4wMDAwMDAsMC45ODgzNzMsMC4yNzkwMDIsMC4yNzg5ODQpIiAvPgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQyMjQ4IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MjI1NCIKICAgICAgIGN4PSIxNi43NSIKICAgICAgIGN5PSIxMC42NjYzNDQiCiAgICAgICBmeD0iMTYuNzUiCiAgICAgICBmeT0iMTAuNjY2MzQ0IgogICAgICAgcj0iMjEuMjUiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDQuMTU0OTU3LDAsMCwzLjE5ODcyMywtNTIuODQ1NTMsLTE2LjYxODY4KSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBzaG93Z3VpZGVzPSJ0cnVlIgogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIwLjE1Mjk0MTE4IgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSItMzEuNDkyNzI0IgogICAgIGlua3NjYXBlOmN5PSI3LjA1NDQ1NzYiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDE3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6c2hvd3BhZ2VzaGFkb3c9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiIC8+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8ZGM6ZGF0ZT4yMDA1LTEwLTE2PC9kYzpkYXRlPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+QW5kcmVhcyBOaWxzc29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzpzdWJqZWN0PgogICAgICAgICAgPHJkZjpCYWc+CiAgICAgICAgICAgIDxyZGY6bGk+c3RvcDwvcmRmOmxpPgogICAgICAgICAgICA8cmRmOmxpPmhhbHQ8L3JkZjpsaT4KICAgICAgICAgICAgPHJkZjpsaT5lcnJvcjwvcmRmOmxpPgogICAgICAgICAgPC9yZGY6QmFnPgogICAgICAgIDwvZGM6c3ViamVjdD4KICAgICAgICA8Y2M6bGljZW5zZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluLyIgLz4KICAgICAgICA8ZGM6Y29udHJpYnV0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5KYWt1YiBTdGVpbmVyPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgICA8Y2M6TGljZW5zZQogICAgICAgICByZGY6YWJvdXQ9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL3B1YmxpY2RvbWFpbi8iPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyNSZXByb2R1Y3Rpb24iIC8+CiAgICAgICAgPGNjOnBlcm1pdHMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zI0Rpc3RyaWJ1dGlvbiIgLz4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjRGVyaXZhdGl2ZVdvcmtzIiAvPgogICAgICA8L2NjOkxpY2Vuc2U+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9Imc1MyI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDk0ODAiCiAgICAgICAgIGQ9Ik0gMTUuNTk1MDIxLDIuNDk3NDk2MiBIIDMyLjY4MDMyNiBMIDQ1LjUwMTYsMTUuNTkxOTYgViAzMy40ODU2MDUgTCAzMi44NTMwMDEsNDUuNTAyNTA0IEggMTUuNDIyNjY0IEwgMi40OTg0MDAzLDMyLjY2MzgzOSBWIDE1LjQ2OTY1MyBaIgogICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50NDk4Nyk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiM4NjAwMDA7c3Ryb2tlLXdpZHRoOjE7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NjY2MiCiAgICAgICAgIGlkPSJwYXRoOTQ4MiIKICAgICAgICAgZD0iTSAxNi4wMTc4ODcsMy41MDYyNTYyIEggMzIuMjQ1Nzk1IEwgNDQuNDkzNjg4LDE1LjkyODYzMSBWIDMzLjA0MjkxNSBMIDMyLjYzNTcwNCw0NC40OTM3NDQgSCAxNS44Njc0ODUgTCAzLjUwNjMxMTYsMzIuMjE0NjMyIFYgMTUuODUxNTI0IFoiCiAgICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuODEzMTg2ODM7ZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTp1cmwoI2xpbmVhckdyYWRpZW50MjA1Nyk7c3Ryb2tlLXdpZHRoOjEuMDAwMDAwMjQ7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIgogICAgICAgICBpZD0icGF0aDIyNDEiCiAgICAgICAgIGQ9Ik0gMTUuNjg3NSw3LjY0MDUzMDMgMi43NSwyMC40NTMwMyB2IDE3IGwgMi45Mzc1LDIuOTA2MjUgQyAyMi40NTAwNDEsNDAuNDE2ODI5IDIyLjE2NDY2NSwyNy4zNDA1OTcgNDUuMjUsMjguNDg0MjggViAyMC41NzgwMyBMIDMyLjU2MjUsNy42NDA1MzAzIFoiCiAgICAgICAgIHN0eWxlPSJvcGFjaXR5OjAuMjg5NzcyNzI7ZmlsbDp1cmwoI3JhZGlhbEdyYWRpZW50MjI1NCk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjE7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ ok_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI5ODAiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMSByMTUzNzEiCiAgIHNvZGlwb2RpOmRvY25hbWU9Im9rLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczI5ODIiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODA1Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzRlOWEwNjtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4MDciIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YWUyMzQ7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODA5IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg2NCI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzg2NiIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNzFiMmY4O3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzODY4IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDI3OTU7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg2NCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM4NTAiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC42MDI4NDU5LDEuMDQ3MTYzOSwtMS45Nzk0MDIxLDEuMTM5NTI5NSwxMjcuOTU4OCwtNzQuNDU2OTA3KSIKICAgICAgIGN4PSI1MS4zMjg4OTIiCiAgICAgICBjeT0iMzEuMDc0MTQ2IgogICAgICAgZng9IjUxLjMyODg5MiIKICAgICAgIGZ5PSIzMS4wNzQxNDYiCiAgICAgICByPSIxOS41NzE0MjgiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjk4OCIgLz4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50Mzg2NCIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDMwNzYiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC41ODAxOTQyMSwxLjAwNzgxNzEsLTEuOTA1MDI2OSwxLjA5NjcxMjEsNTkuMjg2NTEyLC0xOTcuODE3NDcpIgogICAgICAgY3g9IjUxLjMyODg5MiIKICAgICAgIGN5PSIzMS4wNzQxNDYiCiAgICAgICBmeD0iNTEuMzI4ODkyIgogICAgICAgZnk9IjMxLjA3NDE0NiIKICAgICAgIHI9IjE5LjU3MTQyOCIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzgwNSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MTEiCiAgICAgICB4MT0iNDkuMDU4ODIzIgogICAgICAgeTE9IjYwLjgyMzUyOCIKICAgICAgIHgyPSIzNC45NDExNzciCiAgICAgICB5Mj0iMjMuMTc2NDciCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC44NSwwLDAsMC44NSw2Ni4zLDYuMzAwMDAwMSkiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI1Ljc3MDYwMzEiCiAgICAgaW5rc2NhcGU6Y3g9Ii0xMy44NjMyNjQiCiAgICAgaW5rc2NhcGU6Y3k9IjIyLjAzNzA4NSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE5MjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTAxNyIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1iYm94PSJ0cnVlIj4KICAgIDxpbmtzY2FwZTpncmlkCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBpZD0iZ3JpZDI5OTEiCiAgICAgICBlbXBzcGFjaW5nPSIyIgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhMjk4NSI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGUgLz4KICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPltZb3JpayB2YW4gSGF2cmVdPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzp0aXRsZT5BcmNoX0NoZWNrPC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDEyLTA3LTIyPC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL0FyY2gvUmVzb3VyY2VzL2ljb25zL0FyY2hfQ2hlY2suc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxjYzpsaWNlbnNlPmh0dHBzOi8vd3d3LmdudS5vcmcvY29weWxlZnQvbGVzc2VyLmh0bWw8L2NjOmxpY2Vuc2U+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8ZwogICAgICAgaWQ9ImczNzA4IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS40MTAxNTYyLDAsMCwxLjQxMDE1NjIsLTExMS44MzU5NCwtMjcuMjI2NTYyKSI+CiAgICAgIDxjaXJjbGUKICAgICAgICAgcj0iMTkiCiAgICAgICAgIGN5PSI0MiIKICAgICAgICAgY3g9IjEwMiIKICAgICAgICAgaWQ9InBhdGgzNzg1IgogICAgICAgICBzdHlsZT0iZmlsbDojNzNkMjE2O2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMTcyYTA0O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnNxdWFyZTtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MC42MDAwMDAwMjtzdHJva2Utb3BhY2l0eToxIiAvPgogICAgICA8Y2lyY2xlCiAgICAgICAgIHI9IjE3IgogICAgICAgICBjeT0iNDIiCiAgICAgICAgIGN4PSIxMDIiCiAgICAgICAgIGlkPSJwYXRoMzc4NS0zIgogICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzgxMSk7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiM4YWUyMzQ7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowLjYwMDAwMDAyO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjYyIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgzODEzIgogICAgICAgICBkPSJtIDkxLDQ1IDQsLTQgNCw0IDEyLC0xMiA0LDQgLTE2LDE2IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7c3Ryb2tlOiMxNzJhMDQ7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo= """ add_block_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI4MjEiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTIuMSByMTUzNzEiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImFkZC1ibG9jay15LnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHZlcnNpb249IjEuMSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczI4MjMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODAxIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2M0YTAwMDtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4MDMiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmY2U5NGY7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODA1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzM3NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM3MDEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSI4NC44ODMzMjQiCiAgICAgICBjeT0iNzcuMDQyODQ3IgogICAgICAgZng9Ijg0Ljg4MzMyNCIKICAgICAgIGZ5PSI3Ny4wNDI4NDciCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuODQ5MjQyMSwxLjI1ODUxMTksLTAuNDA0MDQxNSwwLjkxNDc0MDcsLTEyNS44NDEzMSwtMTAwLjI1ODA1KSIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzM3NyI+CiAgICAgIDxzdG9wCiAgICAgICAgIGlkPSJzdG9wMzM3OSIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmFmZjJiO3N0b3Atb3BhY2l0eToxOyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgaWQ9InN0b3AzMzgxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmFhMDA7c3RvcC1vcGFjaXR5OjE7IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50MzM3NyIKICAgICAgIGlkPSJyYWRpYWxHcmFkaWVudDM2OTkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGN4PSI3Ni4zODMzMzEiCiAgICAgICBjeT0iOTQuMzY5NTY4IgogICAgICAgZng9Ijc2LjM4MzMzMSIKICAgICAgIGZ5PSI5NC4zNjk1NjgiCiAgICAgICByPSIxOS40Njc0MzYiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuOTgxODk0MywwLjE4OTQyOTUsLTAuNDEwOTQyNywyLjEzMDA5MjQsNDAuMTYzNDUzLC0xMjEuMTE1NTkpIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MjkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4MDEiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODA3IgogICAgICAgeDE9IjExMCIKICAgICAgIHkxPSIzNSIKICAgICAgIHgyPSI4NSIKICAgICAgIHkyPSIzNSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgc3ByZWFkTWV0aG9kPSJyZWZsZWN0IgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtNjIsMCkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDM4MDEtNSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MDctNyIKICAgICAgIHgxPSIxMTAiCiAgICAgICB5MT0iMzUiCiAgICAgICB4Mj0iODUiCiAgICAgICB5Mj0iMzUiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHNwcmVhZE1ldGhvZD0icmVmbGVjdCIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTYyLC0xNikiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MDEtNSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNjNGEwMDA7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODAzLTMiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNmY2U5NGY7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODA1LTUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHBhdHRlcm4KICAgICAgIHk9IjAiCiAgICAgICB4PSIwIgogICAgICAgaGVpZ2h0PSI2IgogICAgICAgd2lkdGg9IjYiCiAgICAgICBwYXR0ZXJuVW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9IkVNRmhiYXNlcGF0dGVybiIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjMuNjA0MDAzOSIKICAgICBpbmtzY2FwZTpjeD0iLTczLjg3OTM5OSIKICAgICBpbmtzY2FwZTpjeT0iMjYuMDE4MDQyIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDE3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQyOTkyIgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI4MjYiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDExLTEwLTEwPC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL1BhcnQvR3VpL1Jlc291cmNlcy9pY29ucy9QYXJ0X0N5bGluZGVyLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOiNmY2U5NGY7c3Ryb2tlOiMzMDJiMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQuNTtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNob2Zmc2V0OjIwLjQiCiAgICAgICBkPSJtIDU1LjAwMDAwMSw1MyBjIDAsNC40MTgyNzggLTEwLjc0NTE2Niw4IC0yNCw4IEMgMTcuNzQ1MTY4LDYxIDcuMDAwMDAyLDU3LjQxODI3OCA3LjAwMDAwMiw1MyBMIDcsMTEgNTQuOTk5OTk5LDExIHoiCiAgICAgICBpZD0icGF0aDI5OTQtMyIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9InNzY2NjcyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzgwNyk7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOiNmY2U5NGY7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQuNTtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNob2Zmc2V0OjIwLjQiCiAgICAgICBkPSJNIDUzLDUxLjcyNzI3MyBDIDUzLDU1Ljc0Mzg4OSA0My4xNTAyNyw1OSAzMSw1OSAxOC44NDk3MzYsNTkgOS4wMDAwMDEsNTUuNzQzODg5IDkuMDAwMDAxLDUxLjcyNzI3MyBsIC0yZS02LC0zOCA0NC4wMDAwMDEsMCB6IgogICAgICAgaWQ9InBhdGgyOTk0LTMtNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9InNzY2NjcyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZmNlOTRmO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0LjU7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaG9mZnNldDoyMC40IgogICAgICAgZD0iTSA1MywxNS43MjcyNzMgQyA1MSwxOSA0My4xNTAyNywyMSAzMSwyMSAxOC44NDk3MzYsMjEgMTIsMTkgOS4wMDAwMDEsMTUuNzI3MjczIgogICAgICAgaWQ9InBhdGgyOTk0LTMtNi05IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjIiAvPgogICAgPHBhdGgKICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgIHN0eWxlPSJmaWxsOiNmY2U5NGY7c3Ryb2tlOiMzMDJiMDA7c3Ryb2tlLXdpZHRoOjEuNzU7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjQuNTtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDoyMC40IgogICAgICAgaWQ9InBhdGgyOTk0IgogICAgICAgc29kaXBvZGk6Y3g9Ii0zNSIKICAgICAgIHNvZGlwb2RpOmN5PSIyNSIKICAgICAgIHNvZGlwb2RpOnJ4PSIyMSIKICAgICAgIHNvZGlwb2RpOnJ5PSI3IgogICAgICAgZD0ibSAtMTQsMjUgYSAyMSw3IDAgMSAxIC00MiwwIDIxLDcgMCAxIDEgNDIsMCB6IgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMS4xNDI4NTcxLDAsMCwxLjE0Mjg1NzEsNzEsLTE3LjU3MTQyOCkiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgyOSIKICAgICAgIGQ9Ik0gNDAuOTYwMTExLDcuMDQwMTExIFYgMzcuMDM5ODg5IgogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzJlMzQzNjtzdHJva2Utd2lkdGg6Ny45OTk5NDA4N3B4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDMxIgogICAgICAgZD0iTSA1NiwyMiBIIDI2LjAwMDIyMiIKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMyZTM0MzY7c3Ryb2tlLXdpZHRoOjcuOTk5OTQwODdweDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzMyIKICAgICAgIGQ9Ik0gNDAuOTYwMTExLDcuMDQwMTExIFYgMzcuMDM5ODg5IgogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6I2QzZDdjZjtzdHJva2Utd2lkdGg6My45OTk5NzA0NHB4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM1IgogICAgICAgZD0iTSA1NiwyMiBIIDI2LjAwMDIyMiIKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNkM2Q3Y2Y7c3Ryb2tlLXdpZHRoOjMuOTk5OTcwNDRweDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzNyIKICAgICAgIGQ9Ik0gNTYsMjEuMDQwMDA3IEggMjYuMDAwMjIyIgogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MS45OTk5ODUyMnB4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM5IgogICAgICAgZD0iTSA0MC4wMDAxMTgsNy4wNDAxMTEgViAzNy4wMzk4ODkiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZmZmZmZmO3N0cm9rZS13aWR0aDoxLjk5OTk4NTIycHg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogIDwvZz4KPC9zdmc+Cg== """ minimize_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNjRweCIKICAgaGVpZ2h0PSI2NHB4IgogICBpZD0ic3ZnMjk4NSIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ4LjQgcjk5MzkiCiAgIHNvZGlwb2RpOmRvY25hbWU9Im1pbmltaXplLnN2ZyI+CiAgPGRlZnMKICAgICBpZD0iZGVmczI5ODciIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuMDk2ODMxMiIKICAgICBpbmtzY2FwZTpjeD0iLTU5Ljk3Mjg4NSIKICAgICBpbmtzY2FwZTpjeT0iMTYuMTkzNDEzIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0idHJ1ZSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMjU2MCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMzYxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1ub2Rlcz0iZmFsc2UiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIHR5cGU9Inh5Z3JpZCIKICAgICAgIGlkPSJncmlkMjk4NyIKICAgICAgIGVtcHNwYWNpbmc9IjIiCiAgICAgICB2aXNpYmxlPSJ0cnVlIgogICAgICAgZW5hYmxlZD0idHJ1ZSIKICAgICAgIHNuYXB2aXNpYmxlZ3JpZGxpbmVzb25seT0idHJ1ZSIgLz4KICA8L3NvZGlwb2RpOm5hbWVkdmlldz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyOTkwIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZSAvPgogICAgICAgIDxkYzpjcmVhdG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W3lvcmlrdmFuaGF2cmVdPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjcmVhdG9yPgogICAgICAgIDxkYzp0aXRsZT5BcmNoX1NlY3Rpb25QbGFuZV9UcmVlPC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDExLTEyLTA2PC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL0FyY2gvUmVzb3VyY2VzL2ljb25zL0FyY2hfU2VjdGlvblBsYW5lX1RyZWUuc3ZnPC9kYzppZGVudGlmaWVyPgogICAgICAgIDxkYzpyaWdodHM+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEIExHUEwyKzwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cmlnaHRzPgogICAgICAgIDxjYzpsaWNlbnNlPmh0dHBzOi8vd3d3LmdudS5vcmcvY29weWxlZnQvbGVzc2VyLmh0bWw8L2NjOmxpY2Vuc2U+CiAgICAgICAgPGRjOmNvbnRyaWJ1dG9yPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+W2Fncnlzb25dIEFsZXhhbmRlciBHcnlzb248L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOmNvbnRyaWJ1dG9yPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlkPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciI+CiAgICA8cGF0aAogICAgICAgc29kaXBvZGk6dHlwZT0ic3RhciIKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6Izk5OTk5OTtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6IzRkNGQ0ZDtzdHJva2Utd2lkdGg6MS41NzQ4ODIyNztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGlkPSJwYXRoMjk5NyIKICAgICAgIHNvZGlwb2RpOnNpZGVzPSIzIgogICAgICAgc29kaXBvZGk6Y3g9IjIyIgogICAgICAgc29kaXBvZGk6Y3k9IjE3LjA5MDkwOCIKICAgICAgIHNvZGlwb2RpOnIxPSIyMC40MzI1MTIiCiAgICAgICBzb2RpcG9kaTpyMj0iMTAuMjE2MjU3IgogICAgICAgc29kaXBvZGk6YXJnMT0iMi4wOTQzOTUxIgogICAgICAgc29kaXBvZGk6YXJnMj0iMy4xNDE1OTI3IgogICAgICAgaW5rc2NhcGU6ZmxhdHNpZGVkPSJ0cnVlIgogICAgICAgaW5rc2NhcGU6cm91bmRlZD0iMCIKICAgICAgIGlua3NjYXBlOnJhbmRvbWl6ZWQ9IjAiCiAgICAgICBkPSJtIDExLjc4Mzc0NCwzNC43ODU5ODMgMCwtMzUuMzkwMTQ5NjMgMzAuNjQ4NzY4LDE3LjY5NTA3NDYzIHoiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDAuNTk3OTI3MDMsMS4xNDcyNDA3LDAsMTIuMzkyNjE0LDIxLjYwNjM2NCkiCiAgICAgICBpbmtzY2FwZTp0cmFuc2Zvcm0tY2VudGVyLXk9IjMuMDU0Mjg1NyIgLz4KICA8L2c+Cjwvc3ZnPgo= """ export3DStep_b64=\ """ iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAPdEVYdEF1dGhvcgBbd21heWVyXauF7RsAAAAYdEVYdENyZWF0aW9uIFRpbWUAMjAxMS0xMC0xMEUb11UAAA6XSURBVHic7Zt5cF3Vfcc/59x7375I8pNsSV7kBSNb8oqNCSRmSWywCdCUEsjANEydzLSdTlvaP9KmbdImbZJ2piGdtlAIIQ1pQ6GUgoEJNCm7iV0Wy3gR2LIsL9re0/b25S6nf9ynDSNrsRx5Jv7O3LnvvHuW3/me3++c37nnd+ESfrUhZrOypiY8XkGjo2gGFipFPYJ6AbXA/HJ7leXsw/fBMXcF9AJdStElBJ3AGSk4JLy0vvsu5mzKC+dJwLp11GOyHcH1AtYBqwBjdkQ7CybQquAAilekzUv7P6TrfCudNgFr17JU2uxCcBvuSI9ASsGi+iCXLY9QNz9AdbWf6iof1TEfVZVepCYIB11+ImH3nkq7g5rOmji2YmCwSDyRp2+wSDyepzue49jxFKc7sziO+qjwBxXsxuaRlg/ouKAErG9mm1D8sYJtgAQIBnSuWB9jyxU1rFldybKGMD6vNhM5JkWhaNPekebgkUH2vRvnnf195PLW8GNHwf9Ih7/f38rPp1PvpARsuJw6ZfA9FHcAeD0a266v47M3LmZ9cxW6Lqffm1mAZTm0HBrg+ZdO8bNXuiiWbPeB4klhc99UzeOcBGxYxVYleRaoCAR0dt2zks/d3DCivhcLUmmTp5/v4NF/P0ouZwEMOorb3j/CG5OVnZCAdY2sFBpvA5Gtn1jAn9y3jpqYbxbFnn30JvJ85/73eWNvD0BSwqb3DtN2rjITGmxtDQ8g2HjjDQv5ztc3EQpeXKP+cQgFDbZfX0/HqQztHWmfAzW9Cf7rXGUmNGAh+HWAP/jtJqScVXfhgkJKwR/+ThMAAn5j0vwTPVCgAyilJspy0WKMyPpkeSedwr/34OGz1t+LGY6j+O4/H5xy/kkZ+tmrneQLFn963zrmV/vPS7gLjZ54nm/df4C39vVOucyExr2+CQUQDXtIpksE/Dr33n0Zt3+2gWjEMwvizh6GkiWefqGDH/7bUfIFe0RmgJbD517qJyXgxf+8kfsfPMxLL58BwOORfGZrHbfsWML6NVUYc+QImZZDy8EBnvvpSX7+ehelkgPATZ9eyH2/28yNt78IzAIB77x8GwBvv9fHY4+3sm//AI7bFn6f5rrCm2pobqxg+dIIAf+kVjUj5PKW6wq3DrL3nTjvtfSRL7jenxCwcbWf3/riGrZsqgVg0w3PApMTMGVpN2+IsXnjp+jqyvDU7jbe3JfgxKkcb+7t5c29rs1JKaivDXDZ8ih18/3U1PiJVfmoifmoqvQhNQgHDYQQhEMGQozfDFmmw8BgkUR/gb6BAvFEgc6uLMfaU3R2Z8fO7ggB9dWCLetD3Hn7KpavrJ0GnWPqmejBRzWg93QHVdULMHzD3qAiEc/yi7d7eLtlgOMnMpw4lcW0nBkJMhl0DepqNJbUedjQHOaaLfU0LK9FM1wHzTItek60sXBlI3ABNMAsFkl0ncEbCBKKRPH4/VTXhLj15hXcenM5j2XTcTJDe0eKeL87gom+PPG+AkPJIpapyOQslFO+KwgFXWc0FNCQEioiOlUVBlUVBrEqDwtq/CxfGmHZskqCoQBSjnde8+kU8c7TDPb2AGqEgKli2gZrFgukBkw0Xcfj9+Px+tB1A6lpGLrGZcujXLY8Ot1qp4xiIUculSY92MdATw/FfA6paWiahtSmvxWf8YyllMIsFrEtC03TXSEMA03X0aQGUri6p5RrS8OaqBh9BigUylblOh0c28KxbSzLwrJKmPkCpUKeQj5HPpPGLBZxbBvbtnFse6bij2BWp2zlODi2jQCk0FAwTtjhyy53cjRd/u3YIwRMVGa2MTeL+EWESwTMtQBzjUsEzLUAc41LBMy1AHONSwTMtQBzjUsEzLUAc41LBMy1AHONX3kCZrwdth1FfKhEV79Fd8KkZAuyeQclBFIINE1jXqVBZUgnGtKpjRnMi1yY2IHzwbQJ+OBkga9/v5OT3SVMe3onRlVRg8aGII1LfKxq8HPl6iDR4NyeO06bgKdfHaTtTHFGjQ0kTd46MMRbB9y0lLC6wc81a0N8ojlA4+Jf/gn0tAkIeEenjcu3RqltDhCq1DECGoZHInV3RJUDhYxNfsgkEzfp7Shw+r0spfzoWx3HgUPteQ6153noGVhUY7DzqhA7tvioCk9PMxwH5AwsbNoEVEVHizTvrKRhfcXUCysoFWxS8SJdR3K0PNdPoj0/8vh03OSh3YM88jxsutzLrVf7uapxchH/4Tl47ZDF3dfB+mun05sZEFBdMVokMzjNsD0BHr9GbEmA2JIAa3fEKOZtej7I8t6zCdr3pgGwHdjXWmRfa5El8zXuvNbHtWvkxy5Z77TBKwfduegnr1nc256mcVl4yiJNm4DYWAISowTks0UyyTwI99RGSonU3NVA0zV0XaIbOlIbr9pev8aSDRGWbIhQyFq07Rliz496yfS7dZ/stfm7J7M8+qLk9mt0dlwhGK7CsuFfXx6tz3bga/90mCe/e9WFI2DeGBNI9pRGfluWTWooixACId2lUEgxkhZCIAXoHg3DY+D1G3i8Bpo2Oq6+oE7z9hhN2+aROFHg1YfPcLolC0BfyuGhn5Z4+i3BPdcKPtUEL7wj6BocT+ie/f288Hr3hSNgrAkMdY+uBpqU2JbjdtYROFIgHcYR4EiBU1CYJZt8toiUAsOj4w948Aa8I6E4Qghqlvm549srSPYUef3Rbo69kQQgkVTcv1vxzF7oTY52fuuuOl7/gRsZ980HW1HK1cRZJyAc0PAYkpLpMNQ5agKaLrFtG+GMjroa7rwzelfjCAHbdigVTUQyh8/vwR/w4vEZI0RU1Pq45c+Wkukv8sYj3bS+MgTAifioTJvvrGHzHTX0tuX48LUhTvfkCQlJmMnPKWfkCldXuutNts9Clc/KdY9OtDJEpCJIpDJIKOzHG/BgeHSEENiWjW3ZWLZ7t20b23KwbQfLcrBNh1ymyEAiTV9Pkny2OBLsI4DwPC87v9LArh82smhtaEQWwyu5+p4FAHzm9xaOjHpWySl0f4aucCxq0Bk3UQ6UijZev0TTJHUNsQnLOI7CLFmYRYtisUQxb1EqmuAohBAoyYimOI6DWbLIJDWCYR/+oBdRNo+KWh+f/9sV9BzPsudH3Wy5az664Y6jL6yz9ct1vPZwFwpIogHnPk2aMQHDKGQtvP7JPTgpBV6fgddnEMKNNVJKUSyY5LIFCpkiZskeN3E6jsIazJJO5QmG/QTDPneEBSxYEeT2b6w4q52Nt1Xzf0/EySctCkqwoFm/rueQ9epEcs2MgDET4dtP9RCt82D4JLrHHSVNl3iDGt6gjjco8Yc9+ILjZ3xwbdzn9+DzeyAGpmmRTxdJp3JYpl3WDIFjO6QGM+TSecIVAfwBr2sXYyY5VU5KTXDzVxfz1Ffa3f8d7gc2MYEqzIiAsSvBgWeSUyojdKha5KGuOcCi5iDVK/xU1PjRjVH/1TB0jCqdSFWQfK5IZihPLltwNcJxNWIgkcbjyROpDOD1l4O1lCKTyhOKBhDA4rURalcF6G7NAayvWW18OX7E/JePlWsigT8aIRLvPIVVLCE1jZajBb707XbON4ZSSFi6OcTqbRUsWhMhED07+sy2bVKDOdKDORRqnJ/hC3iIVAYpFiyy6TzRqhC+sjkO9RT4wb0fDFfTZwjr8jOHGZgOAf1A1WvP7SQYNLAtk/6ebhzbRtM0jnWWON1rIqSkZELJFggpkVKSzloUSor+ZIlEf5Ge/gInOnMMpc7tOseW+djyhRjLr6zE+Mh3B7blMNSfJp109w5yhAjcCVIIdEOjurYCUV4K/veB07Ts7i/XoP6x94j9+1MmYF0Tjwu466YbFvLXf36FW4VSZFNJCpmM23g5KmM4QGI0PRqtMfasf3CowLHTWQ4eTfH+0QzvH0tzqrd0VttSFzTfVMHGz1VTVR8YJ6Rl2gwm0qTTeSSMTpplQqIVQcKVAQBKeZsHP38Iy1QAFkpu6G0tHRrb1oQbyPoaDij4YtuJlO/Y8SSfvKoWjyHx+Hz4Q2GkLlGOAhRSSsSYazg9TNrw5TUEC6oM1qwI8OnNUb6wPcbOq6PUxwws26G338JR7la692iBlt39dH2YZv5KP4GIq9pSkwTDPoJhP8W8iVlyY41Uua1S0SQQ9iGlRDMk4WqNtl+kASRCNWYTzmNTIqA7wcCCGt4Afq3jVMb/H//dzrx5PlYsDaNpEt3w4PUH8Pj8SE1zO0/Z55+AgNHLGfkdCUialvnYeVWYWz4ZIuwXdPeZpPPuBJPsMmnZ3U//mSyL1wcxPO4ErOmSoYEMZtF0w2yUQilwlMKxHIJhN5ptXkOAI68MUszYAEuDNeJQNqFaJyUAoCfB6foYP1aCOsty1ry+p4fHnmgjn7NYsjhCMKiX3//p6LqBbrjBUlJKEKLslQl3iSpPmO5oOSjHJUGUF3YpJeGgzuamCHfvmM+qpUFOdBboT7rfBfV3lFi1PUIw4gUgPZQj0T0EqlzniBaAWbLwBz3u7lMKFjR6OfSi+3WegCsrverhVAqrnJ4a1jZxg4Q/AnZQdqEDfo1bblrC9uvrWdYQJhQ0prQBmSr+5uEPeODx4wCs2Bri1q+uGBHYsRWO45TN8OyeSCnR9FEtfOYvj9O+L+OmBV+LH7a++THFJseGVSxxJLsE3AqsHVuHlIKmxgqu3jKflcsiVMd8VES9RMIGXq+GEKBJd8SHiXKXUoXtuCpcLDikMiUOtyX50l/tx7IUQsKuxy4nGpt5tHp6oMj373Z3iQjy0rFWdbdy8rzGa3MTCyzBNqW4AffDySZgVkLJB5VGocztxjsq+NRvLkIztPP60nPPTzrZ+1jCTQjxRO9h865ZfSd93XXoA700apJmJViIoh5FHYJ6oBrX84zimtDwy8QhXDc1BVhAvICwBpV23WzK9jFQjmDrRfkxUE2T/hdC8Y1fQlPfujCx7ecJx7Ee0YR+BYKF58yo0EFN/Q3ouLJiQNPlj/8fNekbya6hBWUAAAAASUVORK5CYII= """ pcb_edge_b64=\ """ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0cHgiCiAgIGhlaWdodD0iNjRweCIKICAgaWQ9InN2ZzI4MjUiCiAgIHNvZGlwb2RpOnZlcnNpb249IjAuMzIiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTEgcjEzNzI1IgogICBzb2RpcG9kaTpkb2NuYW1lPSJTa2V0Y2hlcl9SZWN0YW5nbGUuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjgyNyI+CiAgICA8cmFkaWFsR3JhZGllbnQKICAgICAgIGlua3NjYXBlOmNvbGxlY3Q9ImFsd2F5cyIKICAgICAgIHhsaW5rOmhyZWY9IiNsaW5lYXJHcmFkaWVudDMxNDQiCiAgICAgICBpZD0icmFkaWFsR3JhZGllbnQyMjI5IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsMCwwLDAuNjk4NTI5NCwwLDIwMi44Mjg2MykiCiAgICAgICBjeD0iMjI1LjI2NDAyIgogICAgICAgY3k9IjY3Mi43OTczNiIKICAgICAgIGZ4PSIyMjUuMjY0MDIiCiAgICAgICBmeT0iNjcyLjc5NzM2IgogICAgICAgcj0iMzQuMzQ1MTg4IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzMTQ0Ij4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTQ2IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eTowOyIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDMxNDgiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzMTQ0IgogICAgICAgaWQ9InJhZGlhbEdyYWRpZW50MjIxNSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwwLjY5ODUyOTQsMCwyMDIuODI4NjMpIgogICAgICAgY3g9IjIyNS4yNjQwMiIKICAgICAgIGN5PSI2NzIuNzk3MzYiCiAgICAgICBmeD0iMjI1LjI2NDAyIgogICAgICAgZnk9IjY3Mi43OTczNiIKICAgICAgIHI9IjM0LjM0NTE4OCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODMzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODM2LTAiCiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODAxLTEiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIHgxPSItMTgiCiAgICAgICB5MT0iMTgiCiAgICAgICB4Mj0iLTIyIgogICAgICAgeTI9IjUiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MzYtMCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNhNDAwMDA7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODM4LTIiIC8+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNlZjI5Mjk7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMSIKICAgICAgICAgaWQ9InN0b3AzODQwLTUiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpbmtzY2FwZTpjb2xsZWN0PSJhbHdheXMiCiAgICAgICB4bGluazpocmVmPSIjbGluZWFyR3JhZGllbnQzODM2LTAtNiIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4MDEtMS0zIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICB4MT0iLTE4IgogICAgICAgeTE9IjE4IgogICAgICAgeDI9Ii0yMiIKICAgICAgIHkyPSI1IiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODM2LTAtNiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiNhNDAwMDA7c3RvcC1vcGFjaXR5OjEiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODM4LTItNyIgLz4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2VmMjkyOTtzdG9wLW9wYWNpdHk6MSIKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDM4NDAtNS01IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeTI9IjYwIgogICAgICAgeDI9IjEyLjAwMDAwMSIKICAgICAgIHkxPSI0OCIKICAgICAgIHgxPSIxNi4wMDAwMDIiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEuMTEwMDAyNiwwLDAsLTEuNjQ4NTIyNyw3Mi45MTk3OTksMTAxLjQ0ODQ2KSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MzA0OS05IgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDA4MS01IgogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ0MDgxLTUiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojZWYyOTI5O3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNDA4My02IiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojYTQwMDAwO3N0b3Atb3BhY2l0eToxIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDA4NS0yIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjkuOTUzMTI1IgogICAgIGlua3NjYXBlOmN4PSIzMiIKICAgICBpbmtzY2FwZTpjeT0iMzIiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0iZzM1MjciCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE2MDAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODI4IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgaWQ9ImdyaWQyOTk3IgogICAgICAgZW1wc3BhY2luZz0iMiIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIgogICAgICAgc25hcHZpc2libGVncmlkbGluZXNvbmx5PSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTI4MzAiPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOmNyZWF0b3I+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5bd21heWVyXTwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6Y3JlYXRvcj4KICAgICAgICA8ZGM6dGl0bGU+U2tldGNoZXJfQ3JlYXRlUmVjdGFuZ2xlPC9kYzp0aXRsZT4KICAgICAgICA8ZGM6ZGF0ZT4yMDExLTEwLTEwPC9kYzpkYXRlPgogICAgICAgIDxkYzpyZWxhdGlvbj5odHRwOi8vd3d3LmZyZWVjYWR3ZWIub3JnL3dpa2kvaW5kZXgucGhwP3RpdGxlPUFydHdvcms8L2RjOnJlbGF0aW9uPgogICAgICAgIDxkYzpwdWJsaXNoZXI+CiAgICAgICAgICA8Y2M6QWdlbnQ+CiAgICAgICAgICAgIDxkYzp0aXRsZT5GcmVlQ0FEPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpwdWJsaXNoZXI+CiAgICAgICAgPGRjOmlkZW50aWZpZXI+RnJlZUNBRC9zcmMvTW9kL1NrZXRjaGVyL0d1aS9SZXNvdXJjZXMvaWNvbnMvU2tldGNoZXJfQ3JlYXRlUmVjdGFuZ2xlLnN2ZzwvZGM6aWRlbnRpZmllcj4KICAgICAgICA8ZGM6cmlnaHRzPgogICAgICAgICAgPGNjOkFnZW50PgogICAgICAgICAgICA8ZGM6dGl0bGU+RnJlZUNBRCBMR1BMMis8L2RjOnRpdGxlPgogICAgICAgICAgPC9jYzpBZ2VudD4KICAgICAgICA8L2RjOnJpZ2h0cz4KICAgICAgICA8Y2M6bGljZW5zZT5odHRwczovL3d3dy5nbnUub3JnL2NvcHlsZWZ0L2xlc3Nlci5odG1sPC9jYzpsaWNlbnNlPgogICAgICAgIDxkYzpjb250cmlidXRvcj4KICAgICAgICAgIDxjYzpBZ2VudD4KICAgICAgICAgICAgPGRjOnRpdGxlPlthZ3J5c29uXSBBbGV4YW5kZXIgR3J5c29uPC9kYzp0aXRsZT4KICAgICAgICAgIDwvY2M6QWdlbnQ+CiAgICAgICAgPC9kYzpjb250cmlidXRvcj4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpZD0ibGF5ZXIxIgogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiPgogICAgPGcKICAgICAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI2LjU4OTU2NjciCiAgICAgICBpbmtzY2FwZTpleHBvcnQteGRwaT0iNi41ODk1NjY3IgogICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL3JlY3RhbmdsZS5wbmciCiAgICAgICBpZD0iZzM1MjciCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjEzNjc3MDIsMCwwLDAuMTM2NzcwMiwtMTI1Ljg0Njc0LC00Ny45NjIwOTIpIj4KICAgICAgPGcKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXlkcGk9IjYuNTg5NTY2NyIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjYuNTg5NTY2NyIKICAgICAgICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS95b3Jpay9Eb2N1bWVudHMvTGFiL0RyYWZ0L2ljb25zL3JlY3RhbmdsZS5wbmciCiAgICAgICAgIGlkPSJnMzUyNy0zIgogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjkyMDAwMDA0LDAsMCwwLjk5ODUyODI2LDkyLjMyODA4LDEuMTA3OTMzNSkiPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgaWQ9InJlY3QyMjMzIgogICAgICAgICAgIGQ9Im0gOTcxLjMxMzQ5LDQxNS45ODQ1NyAwLDMzNi44MjYyOSAzNjUuNTc2NzEsMCAwLC0zMzYuODI2MjkgeiBtIDQzLjg2OTIxLDQ0LjM2NDkyIDI3NC4wMjM2LC0wLjQzMTA1IDAsMjQ4Ljk1ODU3IC0yNzQuMDIzNiwwLjA2NDYgeiIKICAgICAgICAgICBzdHlsZT0iZmlsbDojZDNkN2NmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMmUzNDM2O3N0cm9rZS13aWR0aDoxNS4yNTY4MzQwMztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjYyIgLz4KICAgICAgICA8cGF0aAogICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgICBkPSJtIDU1LjkxMzA0NSwxMC45MzUxNDkgLTQ2LjczOTEyODYsMCAwLDQzLjA2MzM3OCIKICAgICAgICAgICBpZD0icGF0aDMwNDAiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCg3LjMxMTUzNDIsMCwwLDcuMzExNTM0Miw5MjAuMTMyNzUsMzUwLjY3NjQ4KSIKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjYyIgLz4KICAgICAgICA8cGF0aAogICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjIuMDg2NjgwMTc7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgICAgZD0ibSAxMyw1MSAzOS42ODU1NzUsMC4wMzIxOSAtMC4wNjk4NywtMzYuMTI4NzU1IgogICAgICAgICAgIGlkPSJwYXRoMzA0MiIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDcuMzExNTM0MiwwLDAsNy4zMTE1MzQyLDkyMC4xMzI3NSwzNTAuNjc2NDgpIgogICAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjIiAvPgogICAgICA8L2c+CiAgICAgIDxnCiAgICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDcuMzExNTM0MiwwLDAsNy4zMTE1MzQyLDEyMTIuNjM2MSw1OC4xNzUyOTkpIgogICAgICAgICBpZD0iZzM4MjctMSI+CiAgICAgICAgPGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMS4zMjIxMzEsNDAuNTcwMjg5KSIKICAgICAgICAgICBpZD0iZzM3OTctOSI+CiAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMyODAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODgwMDAwMDAwMDY7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZSIKICAgICAgICAgICAgIGlkPSJwYXRoNDI1MC03MSIKICAgICAgICAgICAgIGQ9Ik0gLTI2LjE1NjIwNCw1LjU4MjYyNiBBIDguOTkzODE4LDguOTkzNDA3NyAwLjAyMDQyMjgzIDEgMSAtMTIuNDkzNzkzLDE3LjI4MjI0MSA4Ljk5MzgxOCw4Ljk5MzQwNzcgMC4wMjA0MjI4MyAxIDEgLTI2LjE1NjIwNCw1LjU4MjYyNiB6IiAvPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MzgwMS0xKTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6I2VmMjkyOTtzdHJva2Utd2lkdGg6MS45OTk5OTk1MjAwMDAwMDAwMztzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICAgICAgaWQ9InBhdGg0MjUwLTctMyIKICAgICAgICAgICAgIGQ9Ik0gLTI0LjYzMzU4OCw2Ljg5MzU4OCBBIDYuOTk5OTk5Nyw3LjAwMDAwMDEgMCAxIDEgLTE0LDE2IDYuOTk5OTk5Nyw3LjAwMDAwMDEgMCAwIDEgLTI0LjYzMzU4OCw2Ljg5MzU4OCB6IiAvPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgICA8ZwogICAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCg3LjMxMTUzNDIsMCwwLDcuMzExNTM0Miw5MjAuMTMyNzUsMzUwLjY3NjQ4KSIKICAgICAgICAgaWQ9ImczODI3LTEtMyI+CiAgICAgICAgPGcKICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMS4zMjIxMzEsNDAuNTcwMjg5KSIKICAgICAgICAgICBpZD0iZzM3OTctOS01Ij4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzI4MDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODAwMDAwMDAwNjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICAgICAgaWQ9InBhdGg0MjUwLTcxLTYiCiAgICAgICAgICAgICBkPSJNIC0yNi4xNTYyMDQsNS41ODI2MjYgQSA4Ljk5MzgxOCw4Ljk5MzQwNzcgMC4wMjA0MjI4MyAxIDEgLTEyLjQ5Mzc5MywxNy4yODIyNDEgOC45OTM4MTgsOC45OTM0MDc3IDAuMDIwNDIyODMgMSAxIC0yNi4xNTYyMDQsNS41ODI2MjYgeiIgLz4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDM4MDEtMS0zKTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6I2VmMjkyOTtzdHJva2Utd2lkdGg6MS45OTk5OTk1MjAwMDAwMDAwMztzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIgogICAgICAgICAgICAgaWQ9InBhdGg0MjUwLTctMy0yIgogICAgICAgICAgICAgZD0iTSAtMjQuNjMzNTg4LDYuODkzNTg4IEEgNi45OTk5OTk3LDcuMDAwMDAwMSAwIDEgMSAtMTQsMTYgNi45OTk5OTk3LDcuMDAwMDAwMSAwIDAgMSAtMjQuNjMzNTg4LDYuODkzNTg4IHoiIC8+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICAgIDxnCiAgICAgICAgIGlkPSJnNTA4MiIKICAgICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNy4wNzM2Njc5LDAsMCw5LjEyMTMxODEsNjM4LjYxMDMxLDUzOC45ODMwNCkiPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDMwNDktOSk7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjIuMjAwMDAwMDU7bWFya2VyOm5vbmU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgICAgICBkPSJtIDkwLjY3OTg0MiwyLjUzNzA5NyAxMy4zMjAwMjgsMTMuMTg4MTgxIC0xMy4zMjAwMjgsMTMuMTg4MTgyIDAsLTYuNTk0MDkxIC0xMy4zMjAwMzIsMCAwLC0xMy4xODgxODEgMTMuMzIwMDMyLDAgeiIKICAgICAgICAgICBpZD0icmVjdDMxNjUiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIiAvPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6bm9uZTtzdHJva2U6I2VmMjkyOTtzdHJva2Utd2lkdGg6Mi43MDU0NDk1ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgIGQ9Im0gOTIuODk5ODQ4LDguNzI5MzAzNyAwLDMuMjk3MDQ1MyAtMTMuMzIwMDMzLDAgMTBlLTcsOS44OTExMzQiCiAgICAgICAgICAgaWQ9InBhdGg0MDg3IgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjIiAvPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO2ZpbGw6bm9uZTtzdHJva2U6I2VmMjkyOTtzdHJva2Utd2lkdGg6Mi43MDU0NDk1ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgIGQ9Ik0gOTEuMTgxMzIxLDYuNDA4MDY2OSAxMDIuMzMxOCwxNy40OTgxMjgiCiAgICAgICAgICAgaWQ9InBhdGg0MDg5IgogICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgICAgICA8cGF0aAogICAgICAgICAgIHN0eWxlPSJkaXNwbGF5OmlubGluZTtmaWxsOm5vbmU7c3Ryb2tlOiNlZjI5Mjk7c3Ryb2tlLXdpZHRoOjEuMzUyNzI0NzlweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICAgIGQ9Ik0gOTAuNjc5ODQyLDEyLjQyODIzMyA5Mi44OTk4NDgsOS4xMzExODc1IgogICAgICAgICAgIGlkPSJwYXRoNDA5MSIKICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgICAgIDxwYXRoCiAgICAgICAgICAgc3R5bGU9ImRpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO2ZpbGw6bm9uZTtzdHJva2U6IzM0MDQwNDtzdHJva2Utd2lkdGg6Mi45NzU5OTQ1OTtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDtzdHJva2Utb3BhY2l0eToxO21hcmtlcjpub25lO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICAgICAgZD0ibSA5MC42Nzk4NDMsMi41MzcwOTUxIDEzLjMyMDAzNywxMy4xODgxODE5IC0xMy4zMjAwMzcsMTMuMTg4MTgxIDAsLTYuNTk0MDkxIC0xMy4zMjAwMzIsMCAwLC0xMy4xODgxODExIDEzLjMyMDAzMiwwIHoiCiAgICAgICAgICAgaWQ9InJlY3QzMTY1LTEiCiAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjY2NjY2NjIiAvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K """ ############################################### def find_name(n): n=n.lower() return { 'prefix3d_1' : 1, 'prefix3d_2' : 2, 'pcb_color' : 3, 'bklist' : 4, 'bbox' : 5, 'placement' : 6, 'virt' : 7, 'exportfusing' : 8, 'min_drill_size': 9, 'last_pcb_path' :10, 'last_fp_path' :11, 'export_to_step':12, 'mat' :13, 'spin' :14, 'compound' :15, 'dkmode' :16, 'font_size' :17, 'exporting_mode':18, 'importing_mode':19, }.get(n, 0) # 0 is default if x not found # #import ConfigParser #import configobj def insert(filename, other): if os.path.exists(filename): open(filename,True) else: FreeCAD.Console.PrintError("File does not exist.\n") reply = QtGui.QMessageBox.information(None,"info", "File does not exist.\n") def reload_lib(lib): if (sys.version_info > (3, 0)): import importlib importlib.reload(lib) else: reload (lib) def open(filename,insert=None): #reply = QtGui.QMessageBox.information(None,"info", filename) #onLoadBoard_cmd(filename) global original_filename ext = os.path.splitext(os.path.basename(filename))[1] sayw("kicad StepUp version "+str(___ver___)) #say("tolerance on vertex = "+str(edge_tolerance)) say("tolerance on vertex applied") if ext==".kicad_pcb": original_filename=filename onLoadBoard(filename,None,insert) # zf= Timer (1.0,ZoomFitThread) # zf.start() #elif ext==".emn": # onLoadBoard_idf(filename) elif ext==".kicad_mod": import kicadStepUptools reload_lib( kicadStepUptools ) KSUWidget.activateWindow() KSUWidget.show() KSUWidget.raise_() import fps fps.addfootprint(filename) # onLoadFootprint(filename) def make_unicode(input): if (sys.version_info > (3, 0)): #py3 if isinstance(input, str): return input else: input = input.decode('utf-8') return input else: #py2 if type(input) != unicode: input = input.decode('utf-8') return input else: return input def make_string(input): if (sys.version_info > (3, 0)): #py3 if isinstance(input, str): return input else: input = input.encode('utf-8') return input else: #py2 if type(input) == unicode: input = input.encode('utf-8') return input else: return input def PLine(prm1,prm2): if hasattr(Part,"LineSegment"): return Part.LineSegment(prm1, prm2) else: return Part.Line(prm1, prm2) def light1(x,y,z): light = SoDirectionalLight() light.on = True # 40W Tungsten 2600 255, 197, 143 #light.color = (1,197/255,143/255) light.color = (1,0.839,0.66667) light.intensity = 0.7 light.direction = (x,y,z) return light def light2(x,y,z): light = SoDirectionalLight() light.on = True #Overcast Sky 7000 201, 226, 255 #light.color = (201/255,226/255,1) light.color = (0.2509,0.6117,1) light.intensity = 0.5 light.direction = (x,y,z) return light def simple_copy_link(obj): #simple copy with incremental placement if obj.ViewObject.Visibility != False: __shape = Part.getShape(obj,'',needSubElement=False,refine=False) FreeCAD.ActiveDocument.addObject('Part::Feature','LinkGroup').Shape=__shape nobj = FreeCAD.ActiveDocument.ActiveObject nobjV = FreeCADGui.ActiveDocument.ActiveObject nobj.Label=obj.Label if obj.TypeId == 'App::Part': for subobj in obj.OutList: if hasattr(FreeCADGui.ActiveDocument.getObject(subobj.Name),'ShapeColor'): nobjV.ShapeColor=FreeCADGui.ActiveDocument.getObject(subobj.Name).ShapeColor if hasattr(FreeCADGui.ActiveDocument.getObject(subobj.Name),'LineColor'): #FreeCAD.Console.PrintMessage(subobj.Label);FreeCAD.Console.PrintMessage(' LineColor ' +str(FreeCADGui.ActiveDocument.getObject(subobj.Name).LineColor)+ '\n') nobjV.LineColor=FreeCADGui.ActiveDocument.getObject(subobj.Name).LineColor nobjV.PointColor=FreeCADGui.ActiveDocument.getObject(subobj.Name).PointColor if hasattr(FreeCADGui.ActiveDocument.getObject(subobj.Name),'DiffuseColor'): #FreeCAD.Console.PrintMessage(subobj.Label);FreeCAD.Console.PrintMessage(' DiffuseColor ' +str(FreeCADGui.ActiveDocument.getObject(subobj.Name).DiffuseColor)+ '\n') nobjV.DiffuseColor=FreeCADGui.ActiveDocument.getObject(subobj.Name).DiffuseColor if hasattr(FreeCADGui.ActiveDocument.getObject(subobj.Name),'Transparency'): #FreeCAD.Console.PrintMessage(subobj.Label);FreeCAD.Console.PrintMessage(' Transparency ' +str(FreeCADGui.ActiveDocument.getObject(subobj.Name).Transparency)+ '\n') nobjV.Transparency=FreeCADGui.ActiveDocument.getObject(subobj.Name).Transparency else: nobj.ViewObject.ShapeColor=getattr(obj.getLinkedObject(True).ViewObject,'ShapeColor',nobj.ViewObject.ShapeColor) nobj.ViewObject.LineColor= getattr(obj.getLinkedObject(True).ViewObject,'LineColor' ,nobj.ViewObject.LineColor) nobj.ViewObject.PointColor=getattr(obj.getLinkedObject(True).ViewObject,'PointColor',nobj.ViewObject.PointColor) FreeCAD.ActiveDocument.recompute() def simple_cpy_plc(obj,proot): #simple copy with incremental placement if '::CoordinateSystem' not in obj.TypeId and '::DocumentObjectGroup' not in obj.TypeId \ and '::FeaturePython' not in obj.TypeId and 'GeoFeature' not in obj.TypeId and 'Origin' not in obj.TypeId and obj.ViewObject.Visibility != False: s=obj.Shape t=s.copy() r=s.copy() r.Placement=FreeCAD.Placement(proot) t.Placement=r.Placement.multiply(t.Placement) #incremental Placement FreeCAD.ActiveDocument.addObject('Part::Feature',obj.Name+"_cp").Shape=t if hasattr(FreeCADGui.ActiveDocument.getObject(obj.Name),'ShapeColor'): FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor if hasattr(FreeCADGui.ActiveDocument.getObject(obj.Name),'LineColor'): FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor if hasattr(FreeCADGui.ActiveDocument.getObject(obj.Name),'PointColor'): FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor if hasattr(FreeCADGui.ActiveDocument.getObject(obj.Name),'DiffuseColor'): FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor if hasattr(FreeCADGui.ActiveDocument.getObject(obj.Name),'Transparency'): FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency new_label=make_string(obj.Label)+"_cp" FreeCAD.ActiveDocument.ActiveObject.Label=new_label #stop def get_node_plc(o,obj): # get node placement in App::Part child = FreeCAD.ActiveDocument.addObject("Part::Box","BoxC") node = FreeCAD.ActiveDocument.addObject("Part::Box","BoxN") child.Placement=o.Placement node.Placement=obj.Placement new_Placement=node.Placement.multiply(child.Placement) FreeCAD.ActiveDocument.removeObject("BoxC") FreeCAD.ActiveDocument.removeObject("BoxN") FreeCAD.ActiveDocument.recompute() return new_Placement def recurse_node(obj,plcm,scl): # recursive function to make a simple copy of App::Part hierarchy if "App::Part" in obj.TypeId or 'Body' in obj.TypeId or 'App::LinkGroup' in obj.TypeId: #sayerr(obj.Label) if 'LinkGroup' in obj.TypeId: group = obj.OutList else: group = obj.Group #for o in obj.Group: #sayw(str(group)) for o in group: #sayerr(o.Name);sayw(o.TypeId) if "App::Part" in o.TypeId or 'Body' in o.TypeId or 'App::LinkGroup' in o.TypeId: #sayerr(o.Label)#+" * "+obj.Name) if "App::Part" in o.TypeId and o.Visibility==False: # avoiding objects in invisible Part containers print('avoiding objects in invisible Part containers') pass else: new_plcm=get_node_plc(o,obj) recurse_node(o,new_plcm,scl) else: if "Sketcher" not in o.TypeId and "DocumentObjectGroup" not in o.TypeId: if FreeCADGui.ActiveDocument.getObject(o.Name).Visibility: if 'Compound2' in o.TypeId: simple_copy_link(o) #simple_cpy_plc(o,plcm) else: simple_cpy_plc(o,plcm) scl.append(FreeCAD.ActiveDocument.ActiveObject) class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(400, 164) self.buttonBox = QtGui.QDialogButtonBox(Dialog) self.buttonBox.setGeometry(QtCore.QRect(30, 110, 341, 32)) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.comboBox = QtGui.QComboBox(Dialog) self.comboBox.setGeometry(QtCore.QRect(180, 40, 191, 22)) self.comboBox.setMaxVisibleItems(33) #25 self.comboBox.setObjectName("comboBox") self.label = QtGui.QLabel(Dialog) self.label.setGeometry(QtCore.QRect(180, 20, 53, 16)) self.label.setObjectName("label") self.label_2 = QtGui.QLabel(Dialog) self.label_2.setGeometry(QtCore.QRect(20, 20, 53, 16)) self.label_2.setObjectName("label_2") self.plainTextEdit = QtGui.QTextEdit(Dialog) self.plainTextEdit.setEnabled(False) self.plainTextEdit.setGeometry(QtCore.QRect(20, 40, 31, 31)) #self.plainTextEdit.setBackgroundVisible(True) self.plainTextEdit.setObjectName("plainTextEdit") self.plainTextEdit_2 = QtGui.QTextEdit(Dialog) self.plainTextEdit_2.setEnabled(False) self.plainTextEdit_2.setGeometry(QtCore.QRect(120, 40, 31, 31)) #self.plainTextEdit_2.setBackgroundVisible(True) self.plainTextEdit_2.setObjectName("plainTextEdit_2") self.label_3 = QtGui.QLabel(Dialog) self.label_3.setGeometry(QtCore.QRect(120, 20, 41, 16)) self.label_3.setObjectName("label_3") self.label_4 = QtGui.QLabel(Dialog) self.label_4.setGeometry(QtCore.QRect(20, 80, 351, 16)) self.label_4.setObjectName("label_4") QtCore.QObject.connect(self.comboBox, QtCore.SIGNAL("currentIndexChanged(QString)"), self.SIGNAL_comboBox_Changed) self.retranslateUi(Dialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) QtCore.QMetaObject.connectSlotsByName(Dialog) def SIGNAL_comboBox_Changed(self,text): #say("combo changed "+text) comboBox_Changed(text) def retranslateUi(self, Dialog): Dialog.setWindowTitle(translate("Ui_Dialog", "Material Properties")) self.label.setText(translate("Ui_Dialog", "Materials")) self.label_2.setText(translate("Ui_Dialog", "Original")) self.plainTextEdit.setToolTip(translate("Ui_Dialog", "Shape Color")) self.plainTextEdit_2.setToolTip(translate("Ui_Dialog", "Diffuse Color")) self.label_3.setText(translate("Ui_Dialog", "New")) self.label_4.setText(translate("Ui_Dialog", "NB: set Material will unmatch colors between wrl and STEP")) ### def isWritable(path): try: testfile = tempfile.TemporaryFile(dir = path) testfile.close() #sayw('ok') return True except: #except OSError as e: #sayw('ko') sayw('folder not writable!') pass return False #if e.errno == errno.EACCES: # 13 # return False #e.filename = path #return False #raise sayw('folder not writable!') return False ### def comboBox_Changed(text_combo): global ui, shape_col #say(text_combo) material_index=material_properties_names.index(text_combo) #say(material_index) mat_prop = material_properties[material_index].split('\n') if len(mat_prop)>1: # say(mat_prop[2]) color_rgb=mat_prop[2].split(' ') # say (color_rgb) # say(color_rgb[9]+" "+color_rgb[10]+" "+color_rgb[11]) ## pal = QtGui.QPalette() ## bgc = QtGui.QColor(float(color_rgb[9])*255,float(color_rgb[10])*255, float(color_rgb[11])*255) ## pal.setColor(QtGui.QPalette.Base, bgc) ## ui.plainTextEdit_2.viewport().setPalette(pal) #say(material_index) ui.plainTextEdit_2.setStyleSheet("#plainTextEdit_2 {background-color:rgb("+str(float(color_rgb[9])*255)+","+str(float(color_rgb[10])*255)+","+str(float(color_rgb[11])*255)+");}") else: #say(str(material_index)+" here") #ui.plainTextEdit_2.setStyleSheet("#plainTextEdit_2 {background-color:rgb("+str(0*255)+","+str(1*255)+","+str(0*255)+");}") ui.plainTextEdit_2.setStyleSheet("#plainTextEdit_2 {background-color:rgb("+str(shape_col[0]*255)+","+str(shape_col[1]*255)+","+str(shape_col[2]*255)+");}") ### # https://forum.freecad.org/viewtopic.php?t=71391 https://github.com/easyw/kicadStepUpMod/issues/187 def shapeToMesh(shape, color, transp, mesh_deviation, scale=None): #mesh_deviation=0.1 #the smaller the best quality, 1 coarse #say(mesh_deviation) mesh_data = shape.tessellate(mesh_deviation, True) # forcing new mesh each time points = mesh_data[0] if scale is not None: points = map(lambda p: p*scale, points) newMesh= Mesh(points = points, faces = mesh_data[1], color = color, transp=transp) return newMesh def exportVRMLmaterials(objects, filepath): """Export given list of Mesh objects to a VRML file. with material properties `Mesh` structure is defined at root.""" global ui, creaseAngle, shape_col global color_list, color_list_mat #material_list=["as is","metal pins","gold pins","black body","dark brown body","brown body","grey body","green body","white body","black label","white label"] #material_properties_names=["as is","metal grey pins","gold pins","black body","resistor black body",\ # "grey body","dark grey body","brown body","light brown body","blue body",\ # "green body","orange body","pink body","yellow body","white body","light brown label",\ # "led red","led green","led blue"] #global color_list_mat, col_index #with __builtin__.open(filepath, 'w') as f: # with builtin.open(filepath, 'wb') as f: #py2 if filepath.endswith('wrz'): fname=os.path.splitext(os.path.basename(filepath))[0] tempdir = tempfile.gettempdir() # get the current temporary directory tempfilepath = os.path.join(tempdir,fname + u'.wrl') fpath=tempfilepath else: fpath=filepath with builtin.open(fpath, 'w') as f: #py3 # write the standard VRML header f.write("#VRML V2.0 utf8\n#kicad StepUp wrl exported\n\n") f.write(material_definitions) color_list=[] color_list_mat=[] index_color=-1 Dialog = QtGui.QDialog() ui = Ui_Dialog() ui.setupUi(Dialog) ui.comboBox.addItems(material_properties_names) material="as is" for obj in objects: if creaseAngle==0: f.write("Shape { geometry IndexedFaceSet \n{ coordIndex [") else: f.write("Shape { geometry IndexedFaceSet \n{ creaseAngle %.2f coordIndex [" % creaseAngle) # write coordinate indexes for each face f.write(','.join("%d,%d,%d,-1" % f for f in obj.faces)) f.write("]\n") # closes coordIndex f.write("coord Coordinate { point [") # write coordinate points for each vertex #f.write(','.join('%.3f %.3f %.3f' % (p.x, p.y, p.z) for p in obj.points)) f.write(','.join('%g %g %g' % (p.x, p.y, p.z) for p in obj.points)) f.write("]\n}") # closes Coordinate #shape_col=(1.0, 0.0, 0.0)#, 0.0) f.write("}\n") # closes points #say(obj.color) ##shape_col=obj.color[:-1] #remove last item shape_col=obj.color #say(shape_col) if shape_col not in color_list: #sayw(shape_col);say('not found') idc=0;material_index=0 found_mat=False for mat_diff_col in material_properties_diffuse: #say(mat_diff_col) delta_col=0.01 if ((abs(shape_col[0]-mat_diff_col[0]):"/\|?*,;:\\'), None) exp_name=exp_name.translate(translation_table) path, fname = os.path.split(fullfilePathName) fname=os.path.splitext(fname)[0] save_wrz=False prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if prefs.GetBool('wrz_export_enabled'): #'stpz' save_wrz=True #print('stpZ',fullFilePathNameStep) if scale is not None: if save_wrz: filename=path+os.sep+exp_name+'.wrz' else: filename=path+os.sep+exp_name+'.wrl' else: if save_wrz: filename=path+os.sep+exp_name+'_1_1.wrz' else: filename=path+os.sep+exp_name+'_1_1.wrl' say(filename) exportV=True mesh_deviation_default=0.9 # 0.03 or 0.1 0.9 smaller files mesh_dev=mesh_deviation_default #the smaller the best quality, 1 coarse if os.path.exists(filename): say('file exists') QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.question(None, "Info", filename+"\nwrl file exists, overwrite?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: # this is where the code relevant to a 'Yes' answer goes exportV=True #pass if reply == QtGui.QMessageBox.No: # this is where the code relevant to a 'No' answer goes exportV=False #pass if exportV: reply = QtGui.QInputDialog.getText(None, "Mesh Deviation","Mesh Deviation ([range:0.01-1] the smaller the better quality)",QtGui.QLineEdit.Normal,str(mesh_deviation_default)) if reply[1]: # user clicked OK replyText = reply[0] if float (replyText) < 0.01: mesh_dev = 0.01 elif float (replyText) > 1.0: mesh_dev = 1.0 else: mesh_dev = float (replyText) else: # user clicked Cancel replyText = reply[0] # which will be "" if they clicked Cancel mesh_dev=mesh_deviation_default #the smaller the best quality, 1 coarse #default creaseAngle_default=1.0 creaseAngle_max = 3.14 reply = QtGui.QInputDialog.getText(None, "creaseAngle","creaseAngle [range:0-"+str(creaseAngle_max)+"] the bigger the best quality (0->None)\ncheck your wrl result",QtGui.QLineEdit.Normal,str(creaseAngle_default)) if reply[1]: # user clicked OK replyText = reply[0] if float (replyText) < 0.0: creaseAngle = 0.0 elif float (replyText) > creaseAngle_max: creaseAngle = creaseAngle_max else: creaseAngle = float (replyText) else: # user clicked Cancel replyText = reply[0] # which will be "" if they clicked Cancel creaseAngle=creaseAngle_default #the bigger the best quality, 1 coarse #default #say(mesh_deviation) say("mesh deviation: "+str(mesh_dev)) # 1 for small wrl files say("creaseAngle: "+str(creaseAngle)) # 0 for small wrl files color=[] Diffuse_color=[] transparency=[] for obj in componentObjs: #say(obj.Label) color.append(FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor) transparency.append(FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency/100.0) #say("color") #say(FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor) Diffuse_color.append(FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor) i=0 meshes=[] #say("diffuse color") #say(Diffuse_color) indexColor=0; color_vector=[] applyDiffuse=0 for obj in componentObjs: shape1=obj.Shape single_color=Diffuse_color[i]; #check length color #say("len color") #say(len(single_color)) #colors less then faces if(len(single_color)!=len(shape1.Faces)): applyDiffuse=0; #copy color to all faces #else copy singular colors for faces else: applyDiffuse=1; for color in single_color: color_vector.append(color) #say("color_vector") #say(color_vector) for index in range(len(shape1.Faces)): #say("color x") #say(color_vector[indexColor]) singleFace=shape1.Faces[index] if(applyDiffuse): #say(color_vector[indexColor]) meshes.append(shapeToMesh(singleFace, color_vector[indexColor], transparency[i], mesh_dev, scale)) else: #say(single_color[0]) meshes.append(shapeToMesh(singleFace, single_color[0], transparency[i], mesh_dev, scale)) indexColor=indexColor+1 #meshes.append(shapeToMesh(face, Diffuse_color[i], transparency[i], scale)) color_vector=[] indexColor=0; i=i+1 if enable_materials == 1: # print 'ciao' #if applymaterials==1: exportVRMLmaterials(meshes, filename) else: exportVRML(meshes, filename) return ### def check_AP(): #say("AP") sel = FreeCADGui.Selection.getSelection() if 'App::Part' in sel[0].TypeId: sc_list=[] sel0 = FreeCADGui.ActiveDocument.getObject(sel[0].Name) #sel[0].Visibility=False for obj in FreeCADGui.Selection.getSelection(): recurse_node(obj,obj.Placement, sc_list) #if ('App::Part' in obj.TypeId) and (obj.Visibility==False): # for o in obj.OutList: # o.Visibility=False # if ('App::Part' in obj.TypeId): # and (obj.Visibility==True): # recurse_node(obj,obj.Placement, sc_list) no_shape=True for ob in sc_list: #print(ob.Label,hasattr(ob,'Shape')) if hasattr(ob,'Shape'): no_shape=False if no_shape: msg="Select one or more objects with a Shape!" sayerr(msg) say_warning(msg) stop sel0.Visibility=False FreeCAD.activeDocument().addObject("Part::Compound",FreeCADGui.Selection.getSelection()[0].Label+"_cp") FreeCAD.activeDocument().ActiveObject.Links = sc_list #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.activeDocument().recompute() FreeCADGui.Selection.removeSelection(sel[0]) FreeCADGui.Selection.addSelection(FreeCAD.activeDocument().ActiveObject) def simple_copy(obj): s=obj.Shape FreeCAD.ActiveDocument.addObject('Part::Feature',make_string(obj.Label)+"_cp").Shape=s FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency new_label=make_string(obj.Label)+"_cp" FreeCAD.ActiveDocument.ActiveObject.Label=new_label FreeCAD.ActiveDocument.recompute() def group_part(): #say("gp") sel = FreeCADGui.Selection.getSelection() if len(sel)==0: sayw("None selected!") msg="Select a Compound or a Part Design group\nor more than one Part object!" sayerr(msg) say_select_obj() if len(sel)==1: found=0 p0 = FreeCAD.Placement (FreeCAD.Vector(0,0,0), FreeCAD.Rotation(0,0,0), FreeCAD.Vector(0,0,0)) if 'App::Part' in sel[0].TypeId or 'Body' in sel[0].TypeId: sayw("doing compound and single copy") sc_list=[] FreeCADGui.ActiveDocument.getObject(sel[0].Name).Visibility=False original_label=make_string(sel[0].Label) #sayerr(original_label) #sel[0].Visibility=False pOriginal=FreeCADGui.Selection.getSelection()[0].Placement FreeCADGui.Selection.getSelection()[0].Placement=p0 for obj in FreeCADGui.Selection.getSelection(): recurse_node(obj,obj.Placement, sc_list) #print sc_list #stop if len (sc_list) == 1: #simple_copy(FreeCAD.activeDocument().getObject(sc_list[0].Name)) #print pOriginal simple_cpy_plc(sc_list[0],pOriginal) FreeCAD.ActiveDocument.removeObject(sc_list[0].Name) FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sc" found=1 #stop else: FreeCADGui.Selection.getSelection()[0].Placement=pOriginal FreeCAD.activeDocument().addObject("Part::Compound",FreeCADGui.Selection.getSelection()[0].Label+"_cp") FreeCAD.activeDocument().ActiveObject.Links = sc_list #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.activeDocument().ActiveObject.Placement = pOriginal FreeCAD.activeDocument().recompute() FreeCADGui.Selection.removeSelection(sel[0]) FreeCADGui.Selection.addSelection(FreeCAD.activeDocument().ActiveObject) simple_copy(FreeCAD.activeDocument().getObject(mycompound.Name)) FreeCADGui.ActiveDocument.getObject(mycompound.Name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() found=1 if 'Part::Compound' in sel[0].TypeId: sayw("doing single copy") original_label=make_string(FreeCADGui.Selection.getSelection()[0].Label) original_name=FreeCADGui.Selection.getSelection()[0].Name n_objs=0 solids=FreeCAD.ActiveDocument.getObject(FreeCADGui.Selection.getSelection()[0].Name).Shape.Solids for solid in solids: solids.index(solid) n_objs=n_objs+1 simple_copy(sel[0]) FreeCADGui.ActiveDocument.getObject(original_name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() found=1 if "Part::MultiFuse" in sel[0].TypeId: sayw("doing single copy") original_label=make_string(FreeCADGui.Selection.getSelection()[0].Label) original_name=FreeCADGui.Selection.getSelection()[0].Name n_objs=0 solids=FreeCAD.ActiveDocument.getObject(FreeCADGui.Selection.getSelection()[0].Name).Shape.Solids for solid in solids: solids.index(solid) n_objs=n_objs+1 simple_copy(sel[0]) FreeCADGui.ActiveDocument.getObject(original_name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() found=1 if found==0: say_select_obj() if len(sel)>1: objs=[] for obj in sel: #say(obj.Label) if 'Part::' in obj.TypeId: objs.append(obj) #say(obj.Label) if len(objs)>1: sayw("doing compound and single copy") CopyName = FreeCAD.activeDocument().getObject(objs[0].Name).Name original_label=make_string(objs[0].Label) FreeCAD.activeDocument().addObject("Part::Compound",original_label+"_mp") FreeCAD.activeDocument().ActiveObject.Links = objs #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.activeDocument().recompute() simple_copy(FreeCAD.activeDocument().getObject(mycompound.Name)) FreeCADGui.ActiveDocument.getObject(mycompound.Name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() else: say_select_obj() def group_part_union(): #say("gp union") sel = FreeCADGui.Selection.getSelection() if len(sel)==0: sayw("None selected!") msg="Select a Compound or a Part Design group\nor more than one Part object!" sayerr(msg) say_select_obj() if len(sel)==1: found=0 p0 = FreeCAD.Placement (FreeCAD.Vector(0,0,0), FreeCAD.Rotation(0,0,0), FreeCAD.Vector(0,0,0)) if 'App::Part' in sel[0].TypeId or 'Body' in sel[0].TypeId: sayw("doing union and single copy") original_label=make_string(sel[0].Label) sc_list=[] FreeCADGui.ActiveDocument.getObject(sel[0].Name).Visibility=False #sel[0].Visibility=False pOriginal=FreeCADGui.Selection.getSelection()[0].Placement FreeCADGui.Selection.getSelection()[0].Placement=p0 for obj in FreeCADGui.Selection.getSelection(): recurse_node(obj,obj.Placement, sc_list) FreeCADGui.Selection.getSelection()[0].Placement=pOriginal #print sc_list #stop if len (sc_list) == 1: #simple_copy(FreeCAD.activeDocument().getObject(sc_list[0].Name)) #print pOriginal simple_cpy_plc(sc_list[0],pOriginal) FreeCAD.ActiveDocument.removeObject(sc_list[0].Name) FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" #stop else: FreeCAD.activeDocument().addObject("Part::Compound",FreeCADGui.Selection.getSelection()[0].Label+"_cp") FreeCAD.activeDocument().ActiveObject.Links = sc_list #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.activeDocument().ActiveObject.Placement = pOriginal FreeCAD.activeDocument().recompute() FreeCADGui.Selection.removeSelection(sel[0]) FreeCADGui.Selection.addSelection(FreeCAD.activeDocument().ActiveObject) CopyName = FreeCAD.activeDocument().ActiveObject.Name FreeCAD.activeDocument().addObject("Part::MultiFuse",original_label+"_mp_cp") FusionName = FreeCAD.activeDocument().ActiveObject.Name #say(FusionName) FreeCAD.activeDocument().getObject(FusionName).Shapes = [FreeCAD.activeDocument().getObject(CopyName),] FreeCADGui.activeDocument().getObject(CopyName).Visibility=False FreeCADGui.ActiveDocument.getObject(FusionName).ShapeColor=FreeCADGui.ActiveDocument.getObject(CopyName).ShapeColor FreeCADGui.ActiveDocument.getObject(FusionName).DisplayMode=FreeCADGui.ActiveDocument.getObject(CopyName).DisplayMode FreeCAD.ActiveDocument.getObject(FusionName).Label=original_label+"_fd" FreeCAD.ActiveDocument.recompute() if 'Invalid' in FreeCAD.ActiveDocument.getObject(FusionName).State: # fusion failed code... sayerr("fusion failed! doing compound & single copy") msg="""Union of parts failed!
... doing Compound & single copy""" say_warning(msg) FreeCAD.ActiveDocument.removeObject(FusionName) FreeCAD.ActiveDocument.recompute() #mycompound=FreeCAD.activeDocument().getObject #FreeCAD.activeDocument().recompute() simple_copy(FreeCAD.activeDocument().getObject(mycompound.Name)) FreeCADGui.ActiveDocument.getObject(mycompound.Name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() else: simple_copy(FreeCAD.activeDocument().getObject(FusionName)) FreeCADGui.ActiveDocument.getObject(FusionName).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() found=1 if 'Part::Compound' in sel[0].TypeId: sayw("doing single copy") original_label=make_string(FreeCADGui.Selection.getSelection()[0].Label) original_name=FreeCADGui.Selection.getSelection()[0].Name n_objs=0 solids=FreeCAD.ActiveDocument.getObject(FreeCADGui.Selection.getSelection()[0].Name).Shape.Solids for solid in solids: solids.index(solid) n_objs=n_objs+1 simple_copy(sel[0]) FreeCADGui.ActiveDocument.getObject(original_name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() found=1 if "Part::MultiFuse" in sel[0].TypeId: sayw("doing single copy") original_label=make_string(FreeCADGui.Selection.getSelection()[0].Label) original_name=FreeCADGui.Selection.getSelection()[0].Name n_objs=0 solids=FreeCAD.ActiveDocument.getObject(FreeCADGui.Selection.getSelection()[0].Name).Shape.Solids for solid in solids: solids.index(solid) n_objs=n_objs+1 simple_copy(sel[0]) FreeCADGui.ActiveDocument.getObject(original_name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() found=1 if found==0: say_select_obj() if len(sel)>1: objs=[] for obj in sel: #say(obj.Label) if 'Part' in obj.TypeId and 'App::Part' not in sel[0].TypeId and 'Body' not in sel[0].TypeId: objs.append(obj) #say(obj.Label) if len(objs)>1: sayw("doing union and single copy") CopyName = FreeCAD.activeDocument().getObject(objs[0].Name).Name original_label=make_string(objs[0].Label) FreeCAD.activeDocument().addObject("Part::MultiFuse",original_label+"_mp_cp") FusionName = FreeCAD.activeDocument().ActiveObject.Name #say(FusionName) FreeCAD.activeDocument().getObject(FusionName).Shapes = objs FreeCADGui.activeDocument().getObject(CopyName).Visibility=False FreeCADGui.ActiveDocument.getObject(FusionName).ShapeColor=FreeCADGui.ActiveDocument.getObject(CopyName).ShapeColor FreeCADGui.ActiveDocument.getObject(FusionName).DisplayMode=FreeCADGui.ActiveDocument.getObject(CopyName).DisplayMode FreeCAD.ActiveDocument.getObject(FusionName).Label=original_label+"_fd" FreeCAD.ActiveDocument.recompute() if 'Invalid' in FreeCAD.ActiveDocument.getObject(FusionName).State: # fusion failed code... sayerr("fusion failed! doing compound & single copy") msg="""Union of parts failed!
... doing Compound & single copy""" say_warning(msg) FreeCAD.ActiveDocument.removeObject(FusionName) FreeCAD.ActiveDocument.recompute() FreeCAD.activeDocument().addObject("Part::Compound",original_label+"_mp") FreeCAD.activeDocument().ActiveObject.Links = objs #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.activeDocument().recompute() simple_copy(FreeCAD.activeDocument().getObject(mycompound.Name)) FreeCADGui.ActiveDocument.getObject(mycompound.Name).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() #mw=Gui.getMainWindow() ##c=mw.findChild(QtGui.QPlainTextEdit, "Python console") ##c.clear() #r=mw.findChild(QtGui.QTextEdit, "Report view") #say(r.toPlainText()) #if r.toPlainText().find("MultiFusion failed") != -1: ##if "MultiFusion failed" in r.toPlainText(): # sayerr("fusion failed!") #say("here again") #stop else: simple_copy(FreeCAD.activeDocument().getObject(FusionName)) FreeCADGui.ActiveDocument.getObject(FusionName).Visibility=False FreeCAD.ActiveDocument.ActiveObject.Label=original_label+"_sp" FreeCAD.ActiveDocument.recompute() else: say_select_obj() def go_export(fPathName): global exportV, exportS sel = FreeCADGui.Selection.getSelection() if not sel: FreeCAD.Console.PrintWarning("Select something first!\n\n") msg="export VRML from FreeCAD is a python macro that will export simplified VRML of " msg+="a (multi)selected Part or fused Part to VRML optimized to Kicad and compatible with Blender " msg+="the size of VRML is much smaller compared to the one exported from FC Gui " msg+="and the loading/rendering time is also smaller\n" msg+="change mesh deviation to increase quality of VRML" say(msg) else: objs = [] #check_AP() sel = FreeCADGui.Selection.getSelection() for obj in sel: FreeCADGui.Selection.removeSelection(obj) wrl_selected=False say(sel[0].Label) lbl=sel[0].Label for obj in sel: if not 'App::VRMLObject' in obj.TypeId: if 'App::Part' in obj.TypeId: for o in obj.Group: if 'Part' in obj.TypeId: objs.append(o) else: objs.append(obj) #say(obj.Label) #say(obj.Name) else: wrl_selected=True if wrl_selected==False: say(fPathName) # say(objs[0].Label) # lbl=objs[0].Label #say(objs) #export(objs, fullFilePathName, scale=None, lbl=None) export(objs, fPathName, 0.3937, lbl) align_colors_to_materials(objs) if len(objs) == 1 or 'App::Part' in sel[0].TypeId: exportS=True if 'App::Part' in sel[0].TypeId: exportStep([sel[0]], fPathName) # need to refresh color changing FreeCADGui.Selection.addSelection(sel[0]) #FreeCADGui.Selection.clearSelection() else: exportStep(objs, fPathName) else: #say("Select ONE single part object !") exportS=False #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") else: exportS=False;exportV=False say("Do not select VRML object(s)!") say_single_obj() #lbl='empty' #if len(objs)>0: # lbl=objs[0].Label return lbl ### def align_colors_to_materials(objects): global exportS, applymaterials, enable_materials global color_list, color_list_mat newobj_list= objects if align_vrml_step_colors and enable_materials == 1: #(applymaterials==1): sayw('aligning VRML colors to Materials') applyDiffuse=0 color_vector=[] for obj in newobj_list: #objs: color_vector=[] shape1=obj.Shape single_color=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor if(len(single_color)!=len(shape1.Faces)): applyDiffuse=0; #copy color to all faces #else copy singular colors for faces else: applyDiffuse=1; for color in single_color: color_vector.append((color[0], color[1], color[2], color[3])) #sayw(color_vector) #sayerr (color_list) #sayw(color_list_mat) idx=0 if 'color_list' in globals(): for color in color_vector: if color in color_list: #sayerr('found') pos = color_list.index(color) #sayw(pos) if color_list_mat[pos]!='as is': if color_list_mat[pos] in material_properties_names: pos2 = material_properties_names.index(color_list_mat[pos]) color_vector[idx]=material_properties_diffuse[pos2] else: color_vector[idx]=color idx+=1 if(applyDiffuse): FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor=color_vector else: #say(color_vector) #FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor=color_vector[0] FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor=color_vector #[0] FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency=int(float(color_vector[0][3])*100) # end test aligning colors ## ## def exportStep(objs, ffPathName): #Export fused object global exportS, applymaterials, enable_materials global color_list, color_list_mat #if applymaterials==1: # sayw(color_list); sayw(color_list_mat) exp_name=objs[0].Label #removing not allower chars translation_table = dict.fromkeys(map(ord, '<>:"/\|?*,;:\\'), None) exp_name=exp_name.translate(translation_table) path, fname = os.path.split(ffPathName) #fname=os.path.splitext(fname)[0] prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if prefs.GetBool('stpz_export_enabled'): #'stpz' fullFilePathNameStep=path+os.sep+exp_name+'.stpZ' #print('stpZ',fullFilePathNameStep) else: #not 'stpz' fullFilePathNameStep=path+os.sep+exp_name+'.step' exportS=True if os.path.exists(fullFilePathNameStep): say('file exists') QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.question(None, "Info", fullFilePathNameStep+"\nstep file exists, overwrite?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: # this is where the code relevant to a 'Yes' answer goes exportS=True pass if reply == QtGui.QMessageBox.No: # this is where the code relevant to a 'No' answer goes exportS=False pass if exportS: ## resetting placement TBD for Part & Shape if 'App::Part' not in objs[0].TypeId: ## evaluate to modify reset placement base_shape = FreeCAD.ActiveDocument.getObject(objs[0].Name) if base_shape.Placement.Base != FreeCAD.Vector(0,0,0) or base_shape.Placement.Rotation != FreeCAD.Rotation (0.0, 0.0, 0.0, 1.0): newobj=reset_prop_shapes(FreeCAD.ActiveDocument.getObject(objs[0].Name),FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) new_name=FreeCAD.ActiveDocument.ActiveObject.Name else: newobj=objs[0] new_name=objs[0].Name #newobj.Label="TEST" newobj_list=[FreeCAD.ActiveDocument.getObject(new_name)] else: import kicadStepUpCMD FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(objs[0].Name)) kicadStepUpCMD.ksuToolsResetPartPlacement.Activated(FreeCAD.ActiveDocument.getObject(objs[0].Name)) newobj_list=[FreeCAD.ActiveDocument.getObject(objs[0].Name)] #test aligning colors # reducing STEP file size #NB WriteSurfaceCurveMode parameter get after FC close-reopen # paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") # paramGet.SetInt("WriteSurfaceCurveMode", 1) #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") #paramGet.SetInt("WriteSurfaceCurveMode", 0) paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") old_Auth = paramGet.GetString("Author") old_Comp = paramGet.GetString("Company") # old_Prod = paramGet.GetString("Product") paramGet.SetString("Author", "kicad StepUp") paramGet.SetString("Company", "ksu MCAD") paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") #old_Scheme = paramGet.GetString("Scheme") #if old_Scheme != 'AP214CD': # paramGet.SetString("Scheme", "AP214CD") #paramGetVS1 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") #old_hScheme1 = paramGetVS1.GetBool("Scheme_203") #paramGetVS2 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") #old_hScheme2 = paramGetVS2.GetBool("Scheme_214") #sayw (old_hScheme1) #sayw (old_hScheme2) #sayw (old_Scheme) #stop #if old_hScheme1: # paramGetVS1.SetBool("Scheme_203",0) #if not old_hScheme2: # paramGetVS2.SetBool("Scheme_214",1) ## not to be used paramGet.SetString("Product", "Open CASCADE STEP processor 7.0") #print(fullFilePathNameStep) #stop if fullFilePathNameStep.endswith('stpZ'): try: import stepZ stepZ.export(newobj_list,fullFilePathNameStep) except: sayerr('.stpZ not supported!') else: ImportGui.export(newobj_list,fullFilePathNameStep) #del __objs__ say(fullFilePathNameStep+' written') ##ImportGui.export(objs,fullFilePathNameStep) #restoring old Author #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") paramGet.SetString("Author", old_Auth) paramGet.SetString("Company", old_Comp) ##if old_Scheme != 'AP214CD': ## paramGet.SetString("Scheme",old_Scheme) ##if old_hScheme1: ## paramGetVS1.SetBool("Scheme_203",1) ## paramGetVS2.SetBool("Scheme_214",1) # paramGet.SetString("Product", old_Prod) #say(old_Auth) #say(old_Comp) FreeCAD.activeDocument().recompute() return ### home = expanduser("~") #QtGui.QMessageBox.information(None,"info ...","your home path is \r\n"+ home+"\r\n") sayw("kicad StepUp version "+str(___ver___)) #say("tolerance on vertex = "+str(edge_tolerance)) say("tolerance on vertex applied") if apply_light==True: say("applying Lights") if apply_reflex==True: say("applying Materials to Shapes") say("your home path is "+ home) fname_ksu=home+os.sep+'ksu-config.ini' ksu_config_fname=fname_ksu default_ksu_config_ini=u"""[info] ;; kicad StepUp tools config file utf-8 ;; each line starting with a semicolon is a comment [prefix3D] ;; put here your KISYS3DMOD path or 3D model prefix path or 3D Alias ;; only TWO prefixs are allowed; MUST finish with slash or backslash ;prefix3D_1 = C:\\Program Files\\KiCad\share\\kicad\\modules\\packages3d\\ ;prefix3D_1 = kicad/share/modules/packages3d/ ;prefix3D_1 = /Library/Application Support/kicad/modules/packages3d/ ;prefix3D_2 = C:\\extra_packages3d\\ prefix3D_1 = C:\\Program Files\\KiCad\share\\kicad\\modules\\packages3d\\ prefix3D_2 = kicad/share/modules/packages3d/ [PcbColor] ;; pcb color r,g,b e.g. 0.0,0.5,0.0,light green ;pcb_color=0.3333,0.3333,0.5,blue ;pcb_color=0.0,0.5,0.0,light green ;pcb_color = 1.0,0.1,0.0,red (255,25,0) pcb_color=0.0,0.298,1.0,lightblue (0,76,255) ;pcb_color=0.211,0.305,0.455,darkblue (54,79,116) [Blacklist] ;; put here your model names that you don't want to load (e.g. smallest ones) ;; separated by a comma (none means all the models will be parsed) ;; (volume=1 means all models with a volume < 1mm3 will not be included) ;; (height=1 means all models with a height < 1mm will not be included) ;bklist = r_0603,r_0402,c_0402,c_0603 ;bklist = height=1.0 ;bklist = volume=1.0 ;bklist = none bklist = none [BoundingBox] ;; bounding box option LIST=>whitelist (not converted to bbox) ;bbox = LIST dpak-to252,sod80 ;bbox = ALL ;bbox = off default bbox = off default [Placement] ;; placement options ;placement options: useGridOrigin, useAuxOrigin, useBaseOrigin, useBasePoint;x;y, usedefault, +AutoAdjust ;placement = useGridOrigin ;placement = useAuxOrigin ;placement = useAuxOrigin +AutoAdjust ;placement = useBasePoint;37.0;50.0; ;placement = useBasePoint;37.0;50.0; +AutoAdjust ;placement = useBaseOrigin #place board @ 0,0,0 ;placement = useBaseOrigin +AutoAdjust #place board @ 0,0,0 ;placement = usedefault ;placement = usedefault +AutoAdjust placement = useBaseOrigin #place board @ 0,0,0 [Virtual] ;; virtual modules to be or not added to board virt = addVirtual ;virt = addVirtual ;virt = noVirtual [ExportFuse] ;; fuse modules to board ;; be careful ... fusion can be heavy or generate FC crash with a lot of objects ;; please consider to use bbox or blacklist small objs ;exportFusing = fuseAll ;exportFusing = nofuse #default exportFusing = nofuse #default [minimum_drill_size] ;; minimum drill size to be handled ;; set 0.0 to handle all sizes min_drill_size = 0.0 [last_pcb_path] ;; last pcb file path used last_pcb_path = [last_footprint_path] ;; last footprint file path used last_fp_path = [export] export_to_STEP = yes ;; export to STEP ;export_to_STEP = yes ;export_to_STEP = no [Materials] mat = enablematerials ;; VRML models to be or not exported with material properties ;mat = enablematerials ;mat = nomaterials [turntable] spin = disabled ;;turntable spin after loading ;spin = disabled ;spin = enabled [compound] compound = allowed ;;allow compound for STEP models ;compound = allowed ;compound = disallowed ;compound = simplified [docking] dkmode = right ;;docking mode ;dkmode = left ;dkmode = right ;dkmode = float [sketch_constraints] constraints = all ;constraints = all ;constraints = coincident ;constraints = none ;;constraints generated for pcb sketch [step_exporting_mode] exporting_mode = hierarchy ;exporting_mode = hierarchy ;exporting_mode = flat ;exporting_mode = onelevel ;;step exporting mode [links_importing_mode] importing_mode = standard ;importing_mode = links ;importing_mode = standard ;;models importing mode: use Assembly3 Links or Standard mode [fonts] font_size = 8 ;;font size for ksu widget """ def cfg_read_all(): global ksu_config_fname, default_ksu_config_ini, applymaterials ##ksu pre-set global models3D_prefix, models3D_prefix2, models3D_prefix3, models3D_prefix4 global blacklisted_model_elements, col, colr, colg, colb, whitelisted_3Dmodels global bbox, volume_minimum, height_minimum, idf_to_origin, aux_orig global base_orig, base_point, bbox_all, bbox_list, whitelisted_model_elements global fusion, addVirtual, blacklisted_models, exportFusing, min_drill_size global last_fp_path, last_pcb_path, plcmnt, xp, yp, exportFusing, export_board_2step global enable_materials, docking_mode, mat_section, dock_section, compound_section, turntable_section global font_section, ini_vars, num_min_lines, animate_result, allow_compound, font_size, grid_orig global constraints_section, addConstraints, exporting_mode_section, stp_exp_mode global links_importing_mode_section, links_imp_mode, generate_sketch, edge_tolerance, bklist_dnp import os, sys, re from sys import platform as _platform # window GUI dimensions parameters pt_lnx = False;pt_osx = False;pt_win = False; if _platform == "linux" or _platform == "linux2": # linux pt_lnx = True default_prefix3d = '/usr/share/kicad/modules/packages3d' #'/usr/share/kicad/modules/packages3d' elif _platform == "darwin": #osx pt_osx = True default_prefix3d = '/Library/Application Support/kicad/packages3d' #/Library/Application Support/kicad/modules/packages3d/' wrong location else: # Windows pt_win = True #default_prefix3d = os.path.join(os.environ["ProgramFiles"],u'\\KiCad\\share\\kicad\\modules\\packages3d') default_prefix3d = (os.environ["ProgramFiles"]+u'\\KiCad\\share\\kicad\\modules\\packages3d') #print (default_prefix3d) default_prefix3d = re.sub("\\\\", "/", default_prefix3d) #default_prefix3d.replace('\\','/') #print (default_prefix3d) prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") ## if prefs.GetContents() is not None: ## for i,p in enumerate (prefs.GetContents()): ## print (p) ## else: ## print('preferences null') # print (prefs) # for i,p in enumerate (prefs.GetContents()): # print (p) # stop #if prefs.GetContents() is None: # print('Creating first time ksu preferences') # stop #TBD #else: # for i,p in enumerate (prefs.GetContents()): # print (p) models3D_prefix = prefs.GetString('prefix3d_1') # sayw(prefs.GetString('prefix3d_1')) # sayw(prefs.GetString('prefix3d_2')) # sayw(prefs.GetString('prefix3d_3')) # sayw(prefs.GetString('prefix3d_4')) if len (models3D_prefix) == 0: prefs.SetString('prefix3d_1',make_string(default_prefix3d)) models3D_prefix = prefs.GetString('prefix3d_1') models3D_prefix2 = prefs.GetString('prefix3d_2') models3D_prefix3 = prefs.GetString('prefix3d_3') models3D_prefix4 = prefs.GetString('prefix3d_4') light_green = [0.20,0.60,0.40] # std Green green = [0.3098,0.4823,0.4078] # Green (79,123,104) blue = [0.13,0.40,0.73] # Deep Sea Blue red = [1.0,0.16,0.0] # Ferrari Red purple = [0.498,0.090,0.424] # oshpark purple #6D0A8E darkgreen = [0.180,0.373,0.275] # (45,95,70) darkblue = [0.211,0.305,0.455] # (54,79,116) lightblue = [0.0,0.298,1.0] # (0,76,255) yellow = [0.98,0.98,0.34] #sunshine yellow black = [0.18,0.18,0.18] #slick black white = [0.973,0.973,0.941] #[0.98,0.92,0.84] #antique white pcb_color_values = [light_green,green,blue,red,purple,darkgreen,darkblue,lightblue,yellow,black,white] pcb_color_pos = prefs.GetInt('pcb_color') pcb_color = pcb_color_values [pcb_color_pos] col = [] col.append(pcb_color[0]);col.append(pcb_color[1]);col.append(pcb_color[2]) colr=col[0];colg=col[1];colb=col[2] #print(colr,colg,colb) try: min_drill_size = float(prefs.GetString('drill_size')) except: min_drill_size = 0.0 try: edge_tolerance = float(prefs.GetString('edge_tolerance')) except: edge_tolerance = 0.01 #print(min_drill_size) if prefs.GetBool('vrml_materials'): enable_materials=1 else: enable_materials=0 if prefs.GetBool('mode_virtual'): addVirtual=1 else: addVirtual=0 fusion = prefs.GetBool('make_union') export_board_2step = prefs.GetBool('exp_step') animate_result = prefs.GetBool('turntable') generate_sketch = prefs.GetBool('generate_sketch') if prefs.GetBool('asm3_links'): links_imp_mode = 'links_allowed' else: links_imp_mode = 'links_not_allowed' if prefs.GetBool('asm3_linkGroups'): use_LinkGroups = True else: use_LinkGroups = False aux_orig=0;base_orig=0;base_point=0; grid_orig=0 #grid_orig -> 0; aux_orig-> 1; base_orig -> 2 pcb_placement = prefs.GetInt('pcb_placement') if pcb_placement == 0: grid_orig = 1 elif pcb_placement == 1: aux_orig = 1 else: base_orig = 1 step_exp_mode = prefs.GetInt('step_exp_mode') if step_exp_mode == 0: stp_exp_mode = 'hierarchy' elif step_exp_mode == 1: stp_exp_mode = 'flat' else: stp_exp_mode = 'onelevel' m3D_loading_mode = prefs.GetInt('3D_loading_mode') if m3D_loading_mode == 0: #old Standard #allow_compound = 'True' #old Standard allow_compound = 'Hierarchy' #full hierarchy allowed if 'LinkView' not in dir(FreeCADGui): allow_compound = 'True' #old Standard sayw('Links not allowed... \nfalling from Hierarchy to Compound') elif m3D_loading_mode == 1: allow_compound = 'Simplified' elif m3D_loading_mode == 2: allow_compound = 'False' #NotAllowedMultiParts else: #allow_compound = 'Hierarchy' #full hierarchy allowed allow_compound = 'True' #old Standard sketch_constraints = prefs.GetInt('sketch_constraints') if sketch_constraints == 1: addConstraints='all' elif sketch_constraints == 0: addConstraints='coincident' elif sketch_constraints == 2: addConstraints='full' else: addConstraints='none' bbox_all=0; bbox_list=0; whitelisted_model_elements='' bbox_opt = prefs.GetString('bbox_list') if bbox_opt.upper().find('ALL') !=-1: bbox_all=1 whitelisted_model_elements='' elif bbox_opt.upper().find('LIST') !=-1: bbox_list=1 whitelisted_model_elements=bbox_opt.strip('\r\n') #whitelisted_models=whitelisted_model_elements.split(",") #elif len(bbox_opt) > 0: bbox=0;blacklisted_model_elements='' volume_minimum=0. #0.8 ##1 #mm^3, 0 skipped #global var default height_minimum=0. #0.8 ##1 #mm, 0 skipped #global var default bklist = prefs.GetString('blacklist') bkl_none=False bklist_dnp=False blacklisted_model_elements='' if bklist.lower().find('none') !=-1 or len(bklist) == 0: blacklisted_model_elements='' bkl_none=True elif not(bklist.endswith(";")): bklist+=";" # print(bklist) if bklist.lower().find('volume=') !=-1 and not bkl_none: vval=bklist.strip('\r\n') bklist_s=bklist vvalst=vval.split(";") #print(vvalst) for l in vvalst: #print(l) if l.lower().find('volume=') !=-1: vvalue=l.split("=") volume_minimum=float(vvalue[1].replace(',','.')) #print(height_minimum) bklist_s=bklist.strip(l) #if l.lower().find(';height=') !=-1: # bklist=bklist.strip(';'+l) #else: # bklist=bklist.strip(l+';') #print(bklist) bklist_n=[x for x in bklist_s.split(";") if x] #print('bklist_n',bklist_n) ##removing empty elements bklist='' for bm in bklist_n: bklist+=bm+';' #vvalue=vval.split("=") #height_minimum=float(vvalue[1]) #reply = QtGui.QMessageBox.information(None,"info ...","height "+str(height_minimum)) #vvalue=vval.split("=") #volume_minimum=float(vvalue[1]) #reply = QtGui.QMessageBox.information(None,"info ...","volume "+str(volume_minimum)) #print('bklist 1',bklist) if bklist.lower().find('height=') !=-1 and not bkl_none: vval=bklist.strip('\r\n') bklist_s=bklist vvalst=vval.split(";") #print(vvalst) for l in vvalst: #print(l) if l.lower().find('height=') !=-1: vvalue=l.split("=") height_minimum=float(vvalue[1].replace(',','.')) #print(height_minimum) bklist_s=bklist.strip(l) #if l.lower().find(';height=') !=-1: # bklist=bklist.strip(';'+l) #else: # bklist=bklist.strip(l+';') #print(bklist) bklist_n=[x for x in bklist.split(";") if x] #print(bklist_n) ##removing empty elements bklist='' for bm in bklist_n: bklist+=bm+';' #vvalue=vval.split("=") #height_minimum=float(vvalue[1]) #reply = QtGui.QMessageBox.information(None,"info ...","height "+str(height_minimum)) if (bklist.lower().find('dnp') !=-1 or bklist.lower().find('dnf') !=-1) and not bkl_none: #print('DNP or DNF found!') bklist_dnp=True #blacklisted_model_elements+='DNP;' if bklist.find(';') !=-1 and not bkl_none: blacklisted_model_elements=bklist.strip('\r\n') #say(bklist); bklist_m=[x for x in blacklisted_model_elements.split(";") if x] ##removing empty elements #blacklisted_models=blacklisted_model_elements.split(";") blacklisted_models= bklist_m #say(blacklisted_models) #print('bklist',bklist,'height_minimum',height_minimum,'volume_minimum',volume_minimum) whitelist = prefs.GetString('whitelist') whitel_none=False whitelisted_3Dmodels='' if whitelist.lower().find('none') !=-1 or len(whitelist) == 0: whitelisted_3Dmodels='' whitel_none=True elif not(whitelist.endswith(";")): whitelist+=";" if whitelist.find(';') !=-1 or not whitel_none: whitelisted_3Dmodels=whitelist.strip('\r\n') #say(bklist); whitelist_m=[x for x in whitelisted_3Dmodels.split(";") if x] ##removing empty elements #blacklisted_models=blacklisted_model_elements.split(";") whitelisted_3Dmodels= whitelist_m #say(whitelisted_3Dmodels) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") dock_mode = pg.GetInt("dockingMode") if dock_mode == 0: docking_mode='float' elif dock_mode == 1: docking_mode='left' else: docking_mode='right' idf_to_origin = True pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") last_pcb_path = pg.GetString("last_pcb_path") last_fp_path = pg.GetString("last_fp_path") #stop ## #ini_content=read_ini_file() ini_content=cfg_read_all() #ini_vars[2]=u'd:\extrà3D' #print cfg_get('prefix3D','prefix3d_2') #cfg_read_all() # cfg_update_all() # stop #time.sleep(0.5) # configParser = ConfigParser.RawConfigParser() # configParser = ConfigParser.ConfigParser(allow_no_value = True) # configFilePath = ksu_config_fname # cfgParsRead(configFilePath) #assign params def say_time(): global running_time end_milli_time = current_milli_time() running_time=(end_milli_time-start_time)/1000 msg="running time: "+str(round(running_time,3))+"sec" say(msg) ### def get_time(): global running_time end_milli_time = current_milli_time() running_time=(end_milli_time-start_time)/1000 #msg="running time: "+str(running_time)+"sec" #say(msg) ### def reset_prop(obj,doc,App,Gui): #say('resetting props') ##try: newObj =FreeCAD.ActiveDocument.addObject('Part::Feature',obj.Name) newObj.Shape=FreeCAD.ActiveDocument.getObject(obj.Name).Shape FreeCAD.ActiveDocument.ActiveObject.Label=FreeCAD.ActiveDocument.getObject(obj.Name).Label #tsp = FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency final_Label=FreeCAD.ActiveDocument.getObject(obj.Name).Label #say(final_Label) FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor FreeCAD.ActiveDocument.recompute() newObjCommon=FreeCAD.activeDocument().addObject("Part::MultiCommon","Common") newObjCommon.Shapes = [FreeCAD.activeDocument().getObject(obj.Name),FreeCAD.activeDocument().getObject(newObj.Name),] FreeCADGui.activeDocument().getObject(obj.Name).Visibility=False FreeCADGui.activeDocument().getObject(newObj.Name).Visibility=False FreeCADGui.ActiveDocument.Common.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor FreeCADGui.ActiveDocument.Common.DisplayMode=FreeCADGui.ActiveDocument.getObject(obj.Name).DisplayMode FreeCAD.ActiveDocument.recompute() # sleep FreeCAD.ActiveDocument.addObject('Part::Feature','Common').Shape=FreeCAD.ActiveDocument.Common.Shape FreeCAD.ActiveDocument.ActiveObject.Label=final_Label rstObj=FreeCAD.ActiveDocument.ActiveObject # FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.Common.ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.Common.LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.Common.PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.Common.DiffuseColor #FreeCADGui.ActiveDocument.ActiveObject.Transparency=tsp FreeCAD.ActiveDocument.removeObject("Common") FreeCAD.ActiveDocument.recompute() # return rstObj ### def reset_prop_shapes(obj,doc,App,Gui,rmv=None): s=obj.Shape #say('resetting props #2') r=[] t=s.copy() for i in t.childShapes(): #print t.childShapes c=i.copy() c.Placement=t.Placement.multiply(c.Placement) r.append((i,c)) w=t.replaceShape(r) w.Placement=FreeCAD.Placement() Part.show(w) #w1 = Part.Solid(w) #Part.show(w1) #say(w) # FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency #FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor #FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor #FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor #FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor new_label=make_string(obj.Label) #if (obj.TypeId == 'App::Part'): # removesubtree(obj) #else: # FreeCAD.ActiveDocument.removeObject(obj.Name) # if 0: # FreeCAD.ActiveDocument.removeObject(obj.Name) # else: say('renaming not in Origin object') if rmv == True: FreeCAD.ActiveDocument.removeObject(obj.Name) else: FreeCAD.ActiveDocument.getObject(obj.Name).ViewObject.Visibility = False FreeCAD.ActiveDocument.getObject(obj.Name).Label = new_label + '_' FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.ActiveObject.Label=new_label rstObj=FreeCAD.ActiveDocument.ActiveObject #say(rstObj) # return rstObj ### def reset_prop_shapes2(obj,doc,App,Gui): s=obj.Shape #say('resetting props #2') r=[] t=s.copy() for i in t.childShapes(): c=i.copy() c.Placement=t.Placement.multiply(c.Placement) r.append((i,c)) w=t.replaceShape(r) w.Placement=FreeCAD.Placement() Part.show(w) #say(w) # #FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.Part__Feature.ShapeColor #FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.Part__Feature.LineColor #FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.Part__Feature.PointColor #FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.Part__Feature.DiffuseColor FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor #FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency new_label=obj.Label FreeCAD.ActiveDocument.removeObject(obj.Name) FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.ActiveObject.Label=new_label rstObj=FreeCAD.ActiveDocument.ActiveObject #say(rstObj) # return rstObj ### def copy_objs(obj,doc): FreeCAD.ActiveDocument.addObject('Part::Feature',obj.Label).Shape=obj.Shape #App.ActiveDocument.ActiveObject.Label=obj.Label FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(obj.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(obj.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor #FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(obj.Name).Transparency FreeCAD.ActiveDocument.recompute() #App.ActiveDocument.ActiveObject.ViewObject.Visibility = False return FreeCAD.ActiveDocument.ActiveObject ### def copy_objs_BAD(obj,doc,App,Gui): #cpObj = App.ActiveDocument.addObject('Part::Feature', obj.Label+"cp_") #cpObj.Shape = obj.Shape #cpObj.Label = obj.Label + "_cp" #App.ActiveDocument.addObject('Part::Feature',obj.Label+"cp_").Shape=obj.Shape App.ActiveDocument.addObject('Part::Feature',obj.Label).Shape=obj.Shape #App.ActiveDocument.ActiveObject.Label=obj.Label Gui.ActiveDocument.ActiveObject.ShapeColor=Gui.ActiveDocument.getObject(obj.Name).ShapeColor Gui.ActiveDocument.ActiveObject.LineColor=Gui.ActiveDocument.getObject(obj.Name).LineColor Gui.ActiveDocument.ActiveObject.PointColor=Gui.ActiveDocument.getObject(obj.Name).PointColor Gui.ActiveDocument.ActiveObject.DiffuseColor=Gui.ActiveDocument.getObject(obj.Name).DiffuseColor App.ActiveDocument.recompute() #App.ActiveDocument.ActiveObject.ViewObject.Visibility = False return App.ActiveDocument.ActiveObject ### def createSolidBBox3(objIn): s = objIn.Shape name=objIn.Label FreeCAD.Console.PrintMessage(name+" name ") FreeCAD.activeDocument().removeObject(objIn.Name) # boundBox boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) #say(str(boundBox_)) #say("Rectangle : "+str(boundBox_.XLength)+" x "+str(boundBox_.YLength)+" x "+str(boundBox_.ZLength)) #say("_____________________") #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) obj=FreeCAD.ActiveDocument.addObject('Part::Feature',name) obj.Shape=Part.makeBox(boundBox_.XLength, boundBox_.YLength, boundBox_.ZLength, FreeCAD.Vector(oripl_X,oripl_Y,oripl_Z), FreeCAD.Vector(0,0,1)) bbox_col=bbox_default_col if (obj.Name.upper().startswith('X')): bbox_col=bbox_x_col if (obj.Name.upper().startswith('L')): bbox_col=bbox_l_col if (obj.Name.upper().startswith('R')): bbox_col=bbox_r_col if (obj.Name.upper().startswith('C')): bbox_col=bbox_c_col if (obj.Name.upper().startswith('S')|obj.Name.upper().startswith('Q')|obj.Name.upper().startswith('D')|obj.Name.upper().startswith('T')): bbox_col=bbox_IC_col FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor=bbox_col return obj ### ### def createScaledBBox(name,scale): # type: box, cylV, cylH say(name) if name == "box_mcad": type = "cube" if name == "cylV_mcad": type = "cylinder_vert" if name == "cylH_mcad": type = "cylinder_horz" say(type+" type") # boundBox boundBoxLX = float(scale[0]) boundBoxLY = float(scale[1]) boundBoxLZ = float(scale[2]) #oripl_X = float(position[0]) #oripl_Y = float(position[1]) #oripl_Z = float(position[2]) obj=FreeCAD.ActiveDocument.addObject('Part::Feature',name+"_") bbox_col=bbox_default_col if type == "cube": #makeBox(length,width,height,[pnt,dir]) – Make a box located in pnt with the dimensions (length,width,height) By default pnt=Vector(0,0,0) and dir=Vector(0,0,1) obj.Shape=Part.makeBox(boundBoxLX, boundBoxLY, boundBoxLZ, FreeCAD.Vector(-boundBoxLX/2,-boundBoxLY/2,0)) bbox_col=bbox_r_col # makeCylinder(radius,height,[pnt,dir,angle]) # Make a cylinder with a given radius and height By default pnt=Vector(0,0,0),dir=Vector(0,0,1) and angle=360 if type == "cylinder_vert": obj.Shape=Part.makeCylinder(boundBoxLX/2, boundBoxLZ) #, FreeCAD.Vector(-boundBoxLX/2,-boundBoxLY/2,0)) bbox_col=bbox_c_col if type == "cylinder_horz": obj.Shape=Part.makeCylinder(boundBoxLX/2, boundBoxLY, FreeCAD.Vector(0,-boundBoxLY/2,boundBoxLX/2), FreeCAD.Vector(0,1,0)) #, FreeCAD.Vector(-boundBoxLX/2,-boundBoxLY/2,0)) bbox_col=bbox_default_col FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeColor=bbox_col return obj ### def Display_info(blacklisted_models): global bbox_all, bbox_list, fusion, show_messages, last_pcb_path global height_minimum, volume_minimum, idf_to_origin, ksu_config_fname global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global animate_result, apply_reflex, apply_reflex_all, addVirtual, fname_sfx global running_time, missingHeight, whitelisted_3Dmodels say('info message') if blacklisted_model_elements != '': sayw("black-listed module \n"+ ''.join(map(str, blacklisted_models))) if (show_messages==True): QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Info ...","... black-listed module(s)\r\n"+ ''.join(map(str, blacklisted_models)).replace(',','\n')) #FreeCAD.Console.PrintMessage("black-listed module "+ '\r\n'.join(map(str, blacklisted_models))) if whitelisted_3Dmodels != '': #ssay(whitelisted_3Dmodels) sayw("whitelisted_3Dmodels: "+ str(whitelisted_3Dmodels)) msg="""kicad StepUp ver. """ msg+=___ver___ #if len(msgpath)>15: # insert_return(msgpath, 15) if (idf_to_origin==True): new_pos_x=board_base_point_x+real_board_pos_x new_pos_y=board_base_point_y+real_board_pos_y else: new_pos_x=board_base_point_x new_pos_y=board_base_point_y if (grid_orig==1): msg+="
Board Placed @ "+"{0:.2f}".format(board_base_point_x)+";"+"{0:.2f}".format(board_base_point_y)+";0.0" else: msg+="
Board Placed @ "+"{0:.2f}".format(new_pos_x)+";"+"{0:.2f}".format(new_pos_y)+";0.0" msg+="
kicad pcb pos: ("+"{0:.2f}".format(real_board_pos_x)+";"+"{0:.2f}".format(real_board_pos_y)+";"+"{0:.2f}".format(0)+")" if (bbox_all==1) or (bbox_list==1): msg+="
bounding box modules applied" if (volume_minimum!=0): msg+="
modules with volume less than "+str(volume_minimum)+"mm^3 not included" if (height_minimum!=0): msg+="
modules with height less than "+str(height_minimum)+"mm not included" if (min_drill_size>0): msg+="
drills with size less than "+str(min_drill_size)+"mm not included" if (min_drill_size==-1): msg+="
ALL via drills included" if (compound_found): msg+="
found multi-part object(s)" if addVirtual==0: msg+="
Virtual models skipped" #msg+="
kicad StepUp config file in:
"+ksu_config_fname+"
location." doc = FreeCAD.ActiveDocument pcb_name=u'Pcb'+fname_sfx pcb_bbx = doc.getObject(pcb_name).Shape.BoundBox msg+="
pcb dimensions: ("+"{0:.2f}".format(pcb_bbx.XLength)+";"+"{0:.2f}".format(pcb_bbx.YLength)+";"+"{0:.2f}".format(pcb_bbx.ZLength)+")" if missingHeight: msg+="
MISSING pcb height from stack; forced 1.6mm value" msg+="
running time: "+str(round(running_time,2))+"sec" msg+="
StepUp configuration options are located in the preferences system of FreeCAD." if (grid_orig==1): say("Board Placed @ "+"{0:.2f}".format(board_base_point_x)+";"+"{0:.2f}".format(board_base_point_y)+";0.0") else: say("Board Placed @ "+"{0:.2f}".format(new_pos_x)+";"+"{0:.2f}".format(new_pos_y)+";0.0") say("kicad pcb pos: ("+"{0:.2f}".format(real_board_pos_x)+";"+"{0:.2f}".format(real_board_pos_y)+";"+"{0:.2f}".format(0)+")") say("pcb dimensions: ("+"{0:.2f}".format(pcb_bbx.XLength)+";"+"{0:.2f}".format(pcb_bbx.YLength)+";"+"{0:.2f}".format(pcb_bbx.ZLength)+")") if missingHeight: sayerr('MISSING pcb height from stack; forced 1.6mm value') if (show_messages==True): QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) reply = QtGui.QMessageBox.information(None,"Info ...",msg) if apply_light==True: # attach a light to every visible scene graph for obj in FreeCAD.ActiveDocument.Objects: if obj.ViewObject.Visibility: obj.ViewObject.RootNode.insertChild(light1(1,1,-1),2) obj.ViewObject.RootNode.insertChild(light2(1,1,1),2) if animate_result==True and apply_reflex==True: #(apply_reflex==True): doc=FreeCAD.ActiveDocument for obj in doc.Objects: if ("Board_Geoms" not in obj.Label) and ("Step_Models" not in obj.Label) and ("Step_Virtual_Models" not in obj.Label)\ and (obj.TypeId != "App::Line") and (obj.TypeId != "App::Plane") and (obj.TypeId != "App::Origin")\ and (obj.TypeId != "App::Part") and ("Local_CS" not in obj.Name): if show_data: say(obj.Name) shape1=obj.Shape single_color=FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor if(len(single_color)!=len(shape1.Faces)): applyDiffuse=0; if show_data: say(FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.DiffuseColor) #copy color to all faces #else copy singular colors for faces else: applyDiffuse=1; if show_data: say(FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.DiffuseColor) color_vector=[] for color in single_color: color_vector.append((color[0], color[1], color[2])) if show_data: sayw(color_vector) if show_data: for f in shape1.Faces: sayw('faces') #say(f.ShapeMaterial.DiffuseColor) #sayerr (color_list) #sayw(color_list_mat) idx2=0 if(applyDiffuse): if (apply_reflex_all==True): shiny=0.6;sun=(201/255, 226/255, 255/255) if show_data: sayw(FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.Shininess) sayerr('multiple colors on faces') FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.Shininess=shiny if show_data: sayw(FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.SpecularColor) FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.SpecularColor=sun #for color_v in color_vector: # color_vector[idx2]=((color_v[0]*shiny,color_v[1]*shiny,color_v[2]*shiny)) # idx2+=1 #sayw(color_vector) FreeCADGui.ActiveDocument.getObject(obj.Name).DiffuseColor=color_vector else: #say(color_vector) shiny=0.4;bright=1.1 if color_vector[0][0]*bright >1: cv0=1 elif color_vector[0][0]==0: cv0=0.25 else: cv0=color_vector[0][0]*bright if color_vector[0][1]*bright >1: cv1=1 elif color_vector[0][1]==0: cv1=0.25 else: cv1=color_vector[0][1]*bright if color_vector[0][2]*bright >1: cv2=1 elif color_vector[0][2]==0: cv2=0.25 else: cv2=color_vector[0][2]*bright #sayerr(str(cv0)+' '+str(cv1)+' '+str(cv2)) FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.SpecularColor=(cv0,cv1,cv2) FreeCADGui.ActiveDocument.getObject(obj.Name).ShapeMaterial.Shininess=shiny if (animate_result==True): FreeCADGui.ActiveDocument.ActiveView.startAnimating(0,1,0,0.2) ### def checkFCbug(fcv): """ step hierarchy export bug """ if ((fcv[0] == 0) and (fcv[1] == 17) and (fcv[2] >= 13509) and (fcv[2] < 13515))\ or ((fcv[0] == 0) and (fcv[1] == 18) and (fcv[2] >= 13509) and (fcv[2] < 13548)): #if int(fcv[2]) >= 13509 and int(fcv[2]) < 13548: # or fcv[2] == 13516: import Part if hasattr(Part, "OCC_VERSION"): if (Part.OCC_VERSION=='7.2.0'): return True return False ## def find_skt_in_Doc(): """ is returning a list of Sketches and relative Group""" #print sel_name sk_list=[] for MObject1 in FreeCAD.ActiveDocument.Objects: if MObject1.TypeId=="App::Part": #if hasattr(MObject1 ,"Group"): if MObject1.Group is not None: for MObject2 in MObject1.Group: #print MObject1.Name,MObject2.TypeId if 'Sketch' in MObject2.TypeId: #print MObject2.TypeId if MObject2.Name not in sk_list: sk_list.append([MObject2.Name,MObject1.Name]) #return MObject2.Name, MObject1.Name #print 'loop closed' return sk_list ### def Export2MCAD(blacklisted_model_elements): global bbox_all, bbox_list, fusion, show_messages, last_pcb_path global height_minimum, volume_minimum, idf_to_origin, ksu_config_fname global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global animate_result, pcb_path, addVirtual, use_AppPart, force_oldGroups, stp_exp_mode, links_imp_mode global use_Links, fname_sfx say('exporting to MCAD') ## exporting __objs__=[] doc=FreeCAD.ActiveDocument for obj in doc.Objects: # do what you want to automate if ("Board_Geoms" not in obj.Label) and ("Step_Models" not in obj.Label) and ("Step_Virtual_Models" not in obj.Label)\ and (obj.TypeId != "App::Line") and (obj.TypeId != "App::Plane") and (obj.TypeId != "App::Origin")\ and (obj.TypeId != "App::Part") and (obj.TypeId != "Sketcher::SketchObject"): FreeCADGui.Selection.addSelection(obj) __objs__.append(obj) filePath=last_pcb_path if (bbox_all==1) or (bbox_list==1): fpath=filePath+os.sep+doc.Label+"_bbox"+'.step' else: fpath=filePath+os.sep+doc.Label+'.step' # reducing STEP file size #NB WriteSurfaceCurveMode parameter get after FC close-reopen # paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") # paramGet.SetInt("WriteSurfaceCurveMode", 1) #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") #paramGet.SetInt("WriteSurfaceCurveMode", 0) paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") old_Auth = paramGet.GetString("Author") old_Comp = paramGet.GetString("Company") # old_Prod = paramGet.GetString("Product") paramGet.SetString("Author", "kicad StepUp") paramGet.SetString("Company", "ksu MCAD") #sayw("use_AppPart "+str(use_AppPart)+" force_oldGroups "+str(force_oldGroups)+" fusion "+str(fusion)) #stop sayw(stp_exp_mode) # stop # workaround for OCC7.2 & FC bug fcv = getFCversion() fcb = checkFCbug(fcv) #sayw(fcv) #say(fcv[0]) #stop sel = FreeCADGui.Selection.getSelection() selN=sel[0].Name doc = FreeCAD.ActiveDocument paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") old_Auth = paramGet.GetString("Author") old_Comp = paramGet.GetString("Company") # old_Prod = paramGet.GetString("Product") paramGet.SetString("Author", "kicad StepUp") paramGet.SetString("Company", "ksu MCAD") if use_AppPart and not force_oldGroups: # and not fusion: #sayw("exporting STEP with Hierarchy") #stop __ob__=[] skl=[] skl=find_skt_in_Doc() #print skl #print sk_name,';',grp_name for sk in skl: say('moving sketch from grp') #print sk FreeCAD.ActiveDocument.getObject(sk[1]).removeObject(FreeCAD.ActiveDocument.getObject(sk[0])) #FreeCAD.ActiveDocument.getObject(selN).removeObject(FreeCAD.ActiveDocument.getObject(sk_name)) #sayerr(__ob__[0].Name) if (fusion==True): ## be careful ... fusion can be heavy or generate FC crash with a lot of objects ## please consider to use bbox or blacklist small objs # Fuse objects doc.addObject("Part::MultiFuse","ksuFusion_") doc.ksuFusion_.Shapes = __objs__ # doc.ActiveObject.Label=doc.Name+"_union" doc.recompute() doc.addObject('Part::Feature','ksuFusion').Shape=FreeCAD.ActiveDocument.ksuFusion_.Shape if (bbox_all==1) or (bbox_list==1): doc.ActiveObject.Label=doc.Name+"_bbox_union" else: doc.ActiveObject.Label=doc.Name+"_union" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.ksuFusion_.ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.ksuFusion_.LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.ksuFusion_.PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.ksuFusion_.DiffuseColor # Remove the fusion object doc.removeObject("ksuFusion_") doc.recompute() fobjs=[] fused_obj=doc.ActiveObject FreeCAD.Console.PrintMessage(fused_obj) fobjs.append(fused_obj) if (bbox_all==1) or (bbox_list==1): fpath=filePath+os.sep+doc.Label+"_bbox_union"+'.step' else: fpath=filePath+os.sep+doc.Label+"_union"+'.step' FreeCAD.Console.PrintMessage(fpath+" fusion path") FreeCAD.Console.PrintMessage(fobjs) #Export fused object # reducing STEP file size #NB WriteSurfaceCurveMode parameter get after FC close-reopen # paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") # paramGet.SetInt("WriteSurfaceCurveMode", 1) #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/General") #paramGet.SetInt("WriteSurfaceCurveMode", 0) #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") #old_Auth = paramGet.GetString("Author") #old_Comp = paramGet.GetString("Company") # old_Prod = paramGet.GetString("Product") #paramGet.SetString("Author", "kicad StepUp") #paramGet.SetString("Company", "ksu MCAD") ## not to be used paramGet.SetString("Product", "Open CASCADE STEP processor 7.0") ImportGui.export(fobjs,fpath) #restoring old Author #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") # paramGet.SetString("Author", old_Auth) # paramGet.SetString("Company", old_Comp) # paramGet.SetString("Product", old_Prod) #say(old_Auth) #say(old_Comp) FreeCAD.activeDocument().recompute() del fobjs #ImportGui.export(doc.ActiveObject,filePath+os.sep+doc.Label+'.step') elif (fcv[0]==0 and fcv[1]<=16): # FC < 0.17 sayw('exporting flat FC 0.16') ImportGui.export(__objs__,fpath) elif (stp_exp_mode == 'hierarchy' and not fcb): # FC not bugged or < 0.17 sayw('exporting hierarchy') __obtoexp__=[] # FreeCADGui.Selection.removeSelection(obj) # FreeCADGui.Selection.addSelection(doc.getObject("Board")) __obtoexp__.append(doc.getObject("Board"+fname_sfx)) ImportGui.export(__obtoexp__,fpath) del __obtoexp__ elif (stp_exp_mode == 'onelevel') or (stp_exp_mode == 'hierarchy' and fcb): sayw('exporting ONE level hierarchy') FreeCADGui.Selection.removeSelection(obj) FreeCADGui.Selection.addSelection(doc.getObject("Board"+ fname_sfx)) try: import kicadStepUpCMD except: sayerr('to export STEP it is necessary to use StepUp Workbench
instead of the single Macro
(because of '+str(fcv)+' FC bug)') msg="""to export STEP it is necessary to use StepUp Workbench
instead of the single Macro
(because of """+str(fcv)+""" FC bug
""" say_warning(msg) for sk in skl: say('including sketch in grp') FreeCAD.ActiveDocument.getObject(sk[1]).addObject(FreeCAD.ActiveDocument.getObject(sk[0])) stop if fcb: cpmode='compound' else: cpmode='part' suffix='_' to_export_name=kicadStepUpCMD.deep_copy(doc,cpmode,suffix) # to_export_name=FreeCAD.ActiveDocument.ActiveObject.Name #sayw(FreeCAD.ActiveDocument.getObject(to_export_name).Label) #say(sel[0]) __objs__=[] __objs__.append(FreeCAD.ActiveDocument.getObject(to_export_name)) #import ImportGui ImportGui.export(__objs__,fpath) #FreeCAD.ActiveDocument.removeObject(to_export_nam) removesubtree(__objs__) #del __objs__ if fcb: # bugged FC version sayerr('exported a simplified STEP hierarchy because of '+str(fcv)+' FC bug') msg="""exported a simplified STEP hierarchy
because of """+str(fcv)+""" FC bug
""" say_warning(msg) for sk in skl: say('including sketch in grp') FreeCAD.ActiveDocument.getObject(sk[1]).addObject(FreeCAD.ActiveDocument.getObject(sk[0])) #stop elif stp_exp_mode == 'flat': # FC >=0.17 # need to deselect all 'Part' containers and select all simple objs #say('flat') if len(sel)==1 and 'App::Part' in sel[0].TypeId: ## flattening a Part hierarchy container sayw('flattening Part container') # FreeCADGui.Selection.removeSelection(sel[0]) __objs__=[] for o in FreeCAD.ActiveDocument.getObject(selN).OutListRecursive: #print o.TypeId # if 'Part::Feature' in o.TypeId: if hasattr(o, 'Shape'): # print o.Label # say ('adding ') # FreeCADGui.Selection.addSelection(o) __objs__.append(o) ImportGui.export(__objs__,fpath) #del __objs__ else: sayw('exporting selection') ImportGui.export(sel,fpath) if use_AppPart and not force_oldGroups: # and not fusion: for sk in skl: say('including sketch in grp') FreeCAD.ActiveDocument.getObject(sk[1]).addObject(FreeCAD.ActiveDocument.getObject(sk[0])) #paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/STEP") ## restoring old Author paramGet.SetString("Author", old_Auth) paramGet.SetString("Company", old_Comp) # paramGet.SetString("Product", old_Prod) #say(old_Auth) #say(old_Comp) #fusion=False mcompound=False #True #to create a Compound instead of a fusion ... to evaluate after Export STEP has improved vejmarie ##mcompound=True ##fusion=True if (mcompound==True): doc.addObject("Part::Compound","ksuCompound_") #say(cObjs) doc.ksuCompound_.Links = __objs__ #cObjs doc.recompute() doc.addObject('Part::Feature','ksuCompound').Shape=FreeCAD.ActiveDocument.ksuCompound_.Shape FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.ksuCompound_.ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.ksuCompound_.LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.ksuCompound_.PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.ksuCompound_.DiffuseColor # Remove the fusion object doc.removeObject("ksuCompound_") doc.recompute() stop for obj in doc.Objects: # do what you want to automate FreeCADGui.Selection.removeSelection(obj) if blacklisted_model_elements != '': sayw("black-listed module \n"+ ''.join(map(str, blacklisted_models))) if (show_messages==True): QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Info ...","... black-listed module(s)\r\n"+ ''.join(map(str, blacklisted_models)).replace(',','\n')) #FreeCAD.Console.PrintMessage("black-listed module "+ '\r\n'.join(map(str, blacklisted_models))) del __objs__ ## Save to disk in native format FreeCAD.ActiveDocument=None FreeCADGui.ActiveDocument=None FreeCAD.setActiveDocument(doc.Name) FreeCAD.ActiveDocument=FreeCAD.getDocument(doc.Name) FreeCADGui.ActiveDocument=FreeCADGui.getDocument(doc.Name) if (bbox_all==1) or (bbox_list==1): fpath=filePath+os.sep+doc.Name+"_bbox" else: fpath=filePath+os.sep+doc.Name if (fusion==True): fpath=fpath+"_union" say(fpath+".FCStd") #fpath = re.sub("\\\\", "/", fpath) #fpath= make_unicode(fpath) #freeCADFileName = pcb_path + "/" + doc.Name + ".FCStd" #utf-8 test #sayw (pcb_path); sayw(freeCADFileName) #fcfilenametest=(freeCADFileName.encode('utf-8')) #sayw(fcfilenametest) #FreeCAD.getDocument(doc.Name).saveAs(fcfilenametest) #utf-8 test #FreeCAD.getDocument(doc.Name).saveAs(freeCADFileName) #utf-8 test #stop try: FreeCAD.getDocument(doc.Name).saveAs((fpath+".FCStd").encode('utf-8')) #bug in FC need to encode utf-8 FreeCAD.ActiveDocument.recompute() except: say_warning("error writing FreeCAD file. You do not have write permissions to save file!") #FreeCAD.getDocument(doc.Name).Label = doc.Name #FreeCADGui.SendMsgToActiveView("Save") #FreeCAD.getDocument(doc.Name).save() msgpath=filePath+os.sep+doc.Name if (bbox_all==1) or (bbox_list==1): msgpath=msgpath+"_bbox" msg="""kicad StepUp ver. """ msg+=___ver___ msg+="
file exported
"+msgpath+'.step' #if len(msgpath)>15: # insert_return(msgpath, 15) if (fusion==True): msgpath=msgpath+"_union" msg+="
fused file exported
"+msgpath+'.step' if (idf_to_origin==True): new_pos_x=board_base_point_x+real_board_pos_x new_pos_y=board_base_point_y+real_board_pos_y else: new_pos_x=board_base_point_x new_pos_y=board_base_point_y if (grid_orig==1): msg+="
Board Placed @ "+"{0:.2f}".format(board_base_point_x)+";"+"{0:.2f}".format(board_base_point_y)+";0.0" else: msg+="
Board Placed @ "+"{0:.2f}".format(new_pos_x)+";"+"{0:.2f}".format(new_pos_y)+";0.0" msg+="
kicad pcb pos: ("+"{0:.2f}".format(real_board_pos_x)+";"+"{0:.2f}".format(real_board_pos_y)+";"+"{0:.2f}".format(0)+")" if (bbox_all==1) or (bbox_list==1): msg+="
bounding box modules applied" if (volume_minimum!=0): msg+="
modules with volume less than "+str(volume_minimum)+"mm^3 not included" if (height_minimum!=0): msg+="
modules with height less than "+str(height_minimum)+"mm not included" if (min_drill_size!=0): msg+="
drills with size less than "+str(min_drill_size)+"mm not included" if (compound_found): msg+="
found multi-part object(s)" if addVirtual==0: msg+="
Virtual models skipped" #msg+="
kicad StepUp config file in:
"+ksu_config_fname+"
location." msg+="
StepUp configuration options are located in the preferences system of FreeCAD." if (grid_orig==1): say("Board Placed @ "+"{0:.2f}".format(board_base_point_x)+";"+"{0:.2f}".format(board_base_point_y)+";0.0") else: say("Board Placed @ "+"{0:.2f}".format(new_pos_x)+";"+"{0:.2f}".format(new_pos_y)+";0.0") say("kicad pcb pos: ("+"{0:.2f}".format(real_board_pos_x)+";"+"{0:.2f}".format(real_board_pos_y)+";"+"{0:.2f}".format(0)+")") say_time() if (show_messages==True): QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) reply = QtGui.QMessageBox.information(None,"Info ...",msg) if (animate_result==True): FreeCADGui.ActiveDocument.ActiveView.startAnimating(0,1,0,0.2) ### def removesubtree(objs): def addsubobjs(obj,toremoveset): if isinstance(obj, list): for o in obj: toremove.add(o) for subobj in o.OutList: addsubobjs(subobj,toremoveset) else: toremove.add(obj) for subobj in obj.OutList: addsubobjs(subobj,toremoveset) import FreeCAD toremove=set() for obj in objs: addsubobjs(obj,toremove) checkinlistcomplete =False while not checkinlistcomplete: for obj in toremove: if (obj not in objs) and (frozenset(obj.InList) - toremove): try: toremove.remove(obj) except: print('exception remove tree') pass break else: checkinlistcomplete = True for obj in toremove: try: obj.Document.removeObject(obj.Name) except: print('exception remove tree') pass ### def create_compound(count,modelnm): #create compound function when a multipart is loaded global allow_compound, compound_found, use_Links, use_LinkGroups counter=0 for ObJ in FreeCAD.activeDocument().Objects: counter+=1 #sayw(str(counter)+" ");say(str(count)) if count+1 != counter: sayerr('multipart found! ...') compound_found=True counter=0 objs_to_remove = [] for ObJ in FreeCAD.activeDocument().Objects: counter+=1 if counter > count: ##sayw(ObJ.TypeId) if 'App::Plane' not in ObJ.TypeId and 'App::Origin' not in ObJ.TypeId and 'App::Line' not in ObJ.TypeId: #FreeCADGui.Selection.addSelection(ObJ) objs_to_remove.append(ObJ) #sel = FreeCADGui.Selection.getSelection() #lsel = len (sel) lotr = len (objs_to_remove) #print (lsel) #print (sel[lsel-1].Label) #for s in sel: # print(s.Label) #tobeimproved for App:Links # sayw(str(objs_to_remove)) #stop #mycompound_new=FreeCAD.activeDocument().ActiveObject #sayw (sel.Type) #sayw (sel[0].TypeId) #stop nbr_cmpd=0 if 'App::LinkGroup' in objs_to_remove[0].TypeId: simple_copy_link(objs_to_remove[0]) mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.ActiveDocument.recompute() removesubtree([objs_to_remove[0]]) elif 'LinkView' in dir(FreeCADGui): if 'App::Part' in objs_to_remove[lotr-1].TypeId: #from FC 0.17-12090 multipart STEPs are loded as App::Part and have a list inverted simple_copy_link(objs_to_remove[lotr-1]) mycompound=FreeCAD.activeDocument().ActiveObject FreeCAD.ActiveDocument.recompute() removesubtree([objs_to_remove[lotr-1]]) elif 'App::Part' in objs_to_remove[0].TypeId: #from FC 0.17-10647 multipart STEPs are loded as App::Part sc_list=[] recurse_node(objs_to_remove[0],objs_to_remove[0].Placement, sc_list) #else: #from FC 0.17-10647 multipart STEPs are loded as App::Part sc_list_compound=[] for o in sc_list: if 'Part' in o.TypeId and 'App::Part' not in o.TypeId: sc_list_compound.append(o) FreeCAD.ActiveDocument.recompute() #sayw(sc_list_compound) #stop FreeCAD.activeDocument().addObject("Part::Compound",objs_to_remove[0].Label+"_mp") FreeCAD.activeDocument().ActiveObject.Links = sc_list_compound #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject if 1: #FreeCAD.ActiveDocument.getObject(objs_to_remove[0].Name).removeObjectsFromDocument() FreeCAD.ActiveDocument.removeObject(objs_to_remove[0].Name) else: FreeCADGui.Selection.removeSelection(FreeCADGui.Selection.getSelection()[0]) #FreeCADGui.Selection.addSelection(FreeCAD.activeDocument().ActiveObject) FreeCAD.ActiveDocument.recompute() #simple_copy(FreeCAD.activeDocument().ActiveObject) elif 'App::Part' in objs_to_remove[lotr-1].TypeId: #from FC 0.17-12090 multipart STEPs are loded as App::Part and have a list inverted sc_list=[] recurse_node(objs_to_remove[lotr-1],objs_to_remove[lotr-1].Placement, sc_list) #else: #from FC 0.17-10647 multipart STEPs are loded as App::Part sc_list_compound=[] for o in sc_list: if 'Part' in o.TypeId and 'App::Part' not in o.TypeId and 'Compound' not in o.TypeId: sc_list_compound.append(o) FreeCAD.ActiveDocument.recompute() #sayw(sc_list_compound) #say('Part container found') #stop FreeCAD.activeDocument().addObject("Part::Compound",objs_to_remove[lotr-1].Label+"_mp") FreeCAD.activeDocument().ActiveObject.Links = sc_list_compound #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] mycompound=FreeCAD.activeDocument().ActiveObject if 1: #for ob in objs_to_remove: # FreeCAD.ActiveDocument.removeObject(ob.Name) FreeCAD.ActiveDocument.removeObject(objs_to_remove[lotr-1].Name) FreeCAD.ActiveDocument.removeObject(objs_to_remove[0].Name) else: FreeCADGui.Selection.removeSelection(FreeCADGui.Selection.getSelection()[0]) #FreeCADGui.Selection.addSelection(FreeCAD.activeDocument().ActiveObject) FreeCAD.ActiveDocument.recompute() #simple_copy(FreeCAD.activeDocument().ActiveObject) elif 'Compound' in objs_to_remove[0].TypeId: #new release will load already a compound for Obj in objs_to_remove: if 'Compound' in Obj.TypeId: nbr_cmpd+=1 if nbr_cmpd == 1: mycompound=FreeCAD.activeDocument().getObject(objs_to_remove[0].Name) #FreeCADGui.Selection.addSelection(mycompound) sayw('single Compound part') #print sel[0].TypeId #stop else: #if nbr_cmpd > 1 or nbr_cmpd == 0: #if 'App::Part' not in sel[0].TypeId: #if nbr_cmpd > 1 or nbr_cmpd == 0: #new release will load already a compound if nbr_cmpd>=1: sayw('multi Compound part ...') sayw('... doing compound') sc_list_compound=[] for o in objs_to_remove: if 'Compound2' in o.TypeId or 'App::LinkGroup' in o.TypeId: sc_list_compound.append(o) if len(sc_list_compound) == 0: for o in objs_to_remove: if 'Part' in o.TypeId and 'App::Part' not in o.TypeId: sc_list_compound.append(o) #print (sc_list_compound) FreeCAD.ActiveDocument.addObject("Part::Compound",'MultiPart') FreeCAD.ActiveDocument.ActiveObject.Links = sc_list_compound #[FreeCAD.activeDocument().Part__Feature,FreeCAD.activeDocument().Shape,] FreeCADGui.Selection.clearSelection() mycompound=FreeCAD.ActiveDocument.ActiveObject #FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.ActiveObject) FreeCAD.ActiveDocument.recompute() #stop modelnm_norm=make_string(modelnm) #to manage utf-8 FreeCAD.ActiveDocument.addObject('Part::Feature',modelnm_norm).Shape=FreeCAD.ActiveDocument.getObject(mycompound.Name).Shape mynewObj = FreeCAD.ActiveDocument.ActiveObject FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(mycompound.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(mycompound.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(mycompound.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(mycompound.Name).DiffuseColor ## if use_Links: # compound2 colors to be fixed ## FreeCADGui.ActiveDocument.ActiveObject.mapShapeColors(FreeCAD.ActiveDocument) #FreeCAD.ActiveDocument.removeObject(mycompound.Name) #FreeCAD.ActiveDocument.recompute() #stop #for ob in objs_to_remove: # FreeCAD.ActiveDocument.removeObject(ob.Name) #removesubtree(FreeCADGui.Selection.getSelection()) removesubtree([mycompound]) ## reference mode for labels mynewObj.Label=modelnm_norm #sayerr('HERE') #workaround to remove all extra objects instead of searching for top level container for o in objs_to_remove: try: FreeCAD.ActiveDocument.removeObject(o.Name) except: pass #App.ActiveDocument.getObject(FreeCADGui.Selection.getSelection()[0].Name).removeObjectsFromDocument() #App.ActiveDocument.removeObject(FreeCADGui.Selection.getSelection()[0].Name) #removesubtree(mycompound.Name) FreeCAD.ActiveDocument.recompute() #stop FreeCADGui.Selection.clearSelection() ### def find_top_container(objs_list): '''searching for top level Part container''' ap_list = [] cp_list = [] ag_list = [] for o in objs_list: #say(o.Label) if o.TypeId == 'App::Part': ap_list.append(o) elif o.TypeId == 'Part::Compound2': cp_list.append(o) elif o.TypeId == 'App::LinkGroup': ag_list.append(o) top_ap=None top_cp=None for ap in ap_list: if len(ap.InListRecursive) == 0: top_ap = ap break #say(str(ap_list));stop if top_ap is not None: say(top_ap.Label) sayw('multi Part found! ...') return top_ap else: for cp in cp_list: if len(cp.InListRecursive) == 0: top_cp = cp say(top_cp.Label) sayw('multi Compound found! ...') break if top_cp is not None: return top_cp else: top_ag = None for ag in ag_list: if len(ag.InListRecursive) == 0: top_ag = ag say(top_ag.Label) sayw('multi LinkGroup found! ...') break return top_ag ## def check_wrl_transparency(step_module): prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") step_transparency = 0 Led_enabled = prefs.GetBool('transparency_material_led_enabled') Glass_enabled = prefs.GetBool('transparency_material_glass_enabled') if Led_enabled or Glass_enabled: #sayw('force transparency for glass or led materials' if step_module.lower().endswith('wrl'): if os.path.exists(step_module): # a wrl model could be missing read_mode = 'r' if (sys.version_info > (3, 0)): #py3 read_mode = 'rb' LedM=b'material USE LED' GlassM=b'material USE GLASS' else: read_mode = 'r' LedM='material USE LED' GlassM='material USE GLASS' with builtin.open(step_module, read_mode) as f: #FreeCAD.Console.PrintError(step_module) #FreeCAD.Console.PrintError(' WRL MATERIALS\n') model_content = f.read() if Led_enabled and LedM in model_content: sayw('force transparency for led materials') step_transparency = 30 if Glass_enabled and GlassM in model_content: sayw('force transparency for glass materials') step_transparency = 70 elif step_module.lower().endswith('wrz'): read_mode = 'r' if (sys.version_info > (3, 0)): #py3 read_mode = 'rb' LedM=b'material USE LED' GlassM=b'material USE GLASS' else: read_mode = 'r' LedM='material USE LED' GlassM='material USE GLASS' try: with gz.open(step_module, read_mode) as f: model_content = f.read() #FreeCAD.Console.PrintError(model_content) if Led_enabled and LedM in model_content: sayw('force transparency for led materials') step_transparency = 30 if Glass_enabled and GlassM in model_content: sayw('force transparency for glass materials') step_transparency = 70 except: step_transparency = 0 sayerr('wrz transparency NOT supported') return step_transparency ## def findModelPath(model_type, path_list): """ Find module in all paths and types specified """ global models3D_prefix, models3D_prefix2, models3D_prefix3, models3D_prefix4 module_path='not-found' # model_type = [step_module,step_module_lw,step_module_up,step_module2,step_module2_up,step_module3,step_module3_up,step_module4,step_module4_up,step_module5,step_module5_up] # path_list = [models3D_prefix,models3D_prefix2,models3D_prefix3,models3D_prefix4] path_list = list(filter(None, path_list)) #removing empty paths # sayw('searching models:'+str(model_type)) # sayw('on '+str(len(path_list))+' paths: '+str(path_list)) for model in model_type: if (module_path=='not-found'): # sayerr('trying '+model) if os.path.exists(model): # absolute path module_path=model break for mpath in path_list: if (module_path=='not-found'): model=model.replace(u'"', u'') # strip out '"' mpath_U = re.sub("\\\\", "/", mpath) # mpath_U = mpath_U.replace("\\", "/") utf_path=os.path.join(make_unicode(mpath_U),make_unicode(model)) # sayerr('trying '+utf_path) if os.path.exists(utf_path): module_path=utf_path # say('model found! on path: '+re.sub("\\\\", "/", module_path)) break return module_path ## def Load_models(pcbThickness,modules): global off_x, off_y, volume_minimum, height_minimum, bbox_all, bbox_list global whitelisted_model_elements global models3D_prefix, models3D_prefix2, models3D_prefix3, models3D_prefix4 global last_pcb_path, full_placement, whitelisted_3Dmodels global allow_compound, compound_found, bklist, force_transparency, warning_nbr, use_AppPart global conv_offs, use_Links, links_imp_mode, use_pypro, use_LinkGroups, fname_sfx #say (modules) missing_models = '' compound_found=False loaded_models = [] loaded_model_objs = [] loaded_models_skipped = [] createScaledObjs=False #box and cyl from wrl scale params virtual_nbr=0 virtualTop_nbr=0 virtualBot_nbr=0 modelTop_nbr=0 modelBot_nbr=0 mod_cnt=0 top_name='Top'+fname_sfx bot_name='Bot'+fname_sfx topV_name='TopV'+fname_sfx botV_name='BotV'+fname_sfx stepM_name='Step_Models'+fname_sfx stepV_name='Step_Virtual_Models'+fname_sfx my_hide_list="" for i in range(len(modules)): step_module=modules[i][0] module_container = step_module #print(type(step_module)) #maui test py3 #sayw('added '+str(i)+' model(s)') # say(' modelname= '+modules[i][0]+' '+str(i)); # say(' modelLayer= '+modules[i][4]+' '+str(i)); # say(' modelparams '+str(modules[i])+' '+str(i)); #FreeCAD.Console.PrintMessage('step-module '+step_module) encoded=0 sayw(step_module) # utf-8 test if step_module == 'no3Dmodel' or modules[i][4] == 'noLayer': say('virtual skipped') elif (step_module.startswith(':')) or (step_module.startswith('":')): #alias 3D path step_module_t=step_module.split(':', 1)[-1] step_module=step_module_t.split(':', 1)[-1] #step_module=step_module.decode("utf-8").replace(u'"', u'') # name with spaces step_module=step_module.replace(u'"', u'') # name with spaces if (step_module.startswith('/')) or (step_module.startswith('\\')): step_module=step_module[1:] encoded=1 #say(step_module) #step_module=step_module_t[1] #say(step_module.split(':')[1:]) say('adjusting Alias Path') say('step-module-replaced '+step_module) elif (step_module.find('${HOME}')!=-1): #local 3D path #step_module=step_module.replace('${KIPRJMOD}', '.') home = expanduser("~") #step_module=step_module.decode("utf-8").replace(u'${HOME}', home.decode("utf-8")) step_module=step_module.replace(u'${HOME}', home) step_module=step_module.replace(u'"', u'') # name with spaces encoded=1 say('adjusting Local Path') say('step-module-replaced '+step_module) elif (step_module.find('${KIPRJMOD}')!=-1): #local 3D path step_module = re.sub("\\\\", "/", step_module) #if isinstance(step_module, str): # step_module = step_module.decode('unicode_escape') last_pcb_path = re.sub("\\\\", "/", last_pcb_path) #if isinstance(last_pcb_path, str): # last_pcb_path = last_pcb_path.decode('unicode_escape') step_module=step_module.replace(u'${KIPRJMOD}', last_pcb_path) #sm=step_module #step_module=re.sub(r"^\$\{KIPRJMOD\}.*$",last_pcb_path, sm) #step_module=re.sub('\${.KIPRJMOD}/', '', step_module) step_module=step_module.replace(u'"', u'') # name with spaces encoded=1 say('adjusting Relative Path') say('step-module-replaced '+step_module) elif (step_module.startswith('.')) or (step_module.startswith('".')): #relative path #step_module=last_pcb_path+"/"+step_module step_module=last_pcb_path+os.sep+step_module step_module=step_module.replace(u'"', u'') # name with spaces #step_module=last_pcb_path+step_module[14:] encoded=1 sayw('adjusting Relative Path') say('step-module-replaced '+step_module) #stop elif (step_module.find('${KISYS3DMOD}/')!=-1): #local ${KISYS3DMOD} 3D path #step_module=step_module.replace('${KIPRJMOD}', '.') #step_module=step_module.decode("utf-8").replace(u'${KISYS3DMOD}/', u'') step_module=step_module.replace(u'${KISYS3DMOD}/', u'') step_module=step_module.replace(u'"', u'') # name with spaces #step_module=last_pcb_path+step_module[14:] encoded=1 say('adjusting Local Path') say('step-module-replaced '+step_module) elif (step_module.find('${')!=-1) and encoded==0: #extra local ${ENV} 3D path step_module= re.sub('\${.*?}/', '', step_module) #step_module=step_module.decode("utf-8").replace(u'${}/', u'') step_module=step_module.replace(u'${}/', u'') step_module=step_module.replace(u'"', u'') # name with spaces encoded=1 say('adjusting 2nd Local Path') say('step-module-replaced '+step_module) elif (step_module.find('$(')!=-1) and encoded==0: #extra local $(ENV) 3D path step_module= re.sub('\$(.*?)/', '', step_module) #step_module=step_module.decode("utf-8").replace(u'${}/', u'') step_module=step_module.replace(u'$()/', u'') step_module=step_module.replace(u'"', u'') # name with spaces encoded=1 say('adjusting 2nd Local Path') say('step-module-replaced '+step_module) if (encoded == 0) and step_module != 'no3Dmodel' and modules[i][4] != 'noLayer': #test local 3D path without the use of KIPRJMOD or ENV step_module_local = re.sub("\\\\", "/", step_module) #subst '\\' with '/' # step_module_local = step_module_local.replace("\\", "/") #subst '\' with '/' last_pcb_path_local = re.sub("\\\\", "/", last_pcb_path) # print(step_module) # print(step_module_local) step_module_local=step_module_local.replace(u'"', u'') # name with spaces #print(step_module_local) utf_path_local=os.path.join(make_unicode(last_pcb_path_local),make_unicode(step_module_local)) #print(utf_path_local) pos=utf_path_local.rfind('.') #sayw(pos) rel_pos=len(utf_path_local)-pos local_path=utf_path_local[:-rel_pos+1] #print(local_path) #stop if os.path.exists(local_path+u'stpZ'): step_module = local_path+u'stpZ' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'stpz'): step_module = local_path+u'stpz' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'STPZ'): step_module = local_path+u'STPZ' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'step'): step_module = local_path+u'step' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'STEP'): step_module = local_path+u'STEP' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'stp'): step_module = local_path+u'stp' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'STP'): step_module = local_path+u'STP' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'iges'): step_module = local_path+u'iges' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'IGES'): step_module = local_path+u'IGES' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'igs'): step_module = local_path+u'igs' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) elif os.path.exists(local_path+u'IGS'): step_module = local_path+u'IGS' encoded=1 say('adjusting Relative Path to pcb file') say('step-module-replaced '+step_module) # print(modules[i][4],i,step_module) # print(modules[i][4] == 'noLayer') if step_module != 'no3Dmodel' and modules[i][4] != 'noLayer': #model_type = step_module.split('.')[1] #if encoded!=1: step_module = re.sub("\\\\", "/", step_module) #subst '\\' with '/' # step_module = step_module.replace("\\", "/") #subst '\' with '/' wrl_model = '' if step_module.lower().endswith('wrl') or step_module.lower().endswith('wrz'): wrl_model = step_module #step_transparency = check_wrl_transparency step_module=step_module.replace(u'"', u'') # name with spaces pos=step_module.rfind('.') #sayw(pos) rel_pos=len(step_module)-pos #sayw(rel_pos) #stop step_module=step_module[:-rel_pos+1]+u'stpZ' step_module_lw=step_module[:-4]+u'stpz' step_module_up=step_module[:-4]+u'STPZ' #step_module=step_module[:-3]+'step' step_module2=step_module[:-4]+u'step' step_module2_up=step_module[:-4]+u'STEP' step_module3=step_module[:-4]+u'stp' step_module3_up=step_module[:-4]+u'stp' step_module4=step_module[:-4]+u'iges' step_module4_up=step_module[:-4]+u'IGES' step_module5=step_module[:-4]+u'igs' step_module5_up=step_module[:-4]+u'IGS' # step_module=step_module[:-rel_pos+1]+u'step' # #step_module=step_module[:-3]+'step' # step_module2=step_module[:-4]+u'stp' # step_module3=step_module[:-4]+u'iges' # step_module4=step_module[:-4]+u'igs' # step_module5=step_module[:-4]+u'stpz' #if encoded!=1: # #step_module=step_module.decode("utf-8").replace(u'"', u'') # name with spaces # step_module=step_module.replace(u'"', u'') # name with spaces model_name=step_module[:-5] last_slash_pos1=model_name.rfind('/') last_slash_pos2=model_name.rfind('\\') last_slash_pos=max(last_slash_pos1,last_slash_pos2) model_name=model_name[last_slash_pos+1:] #say('model name '+model_name+'.'+model_type) say('model name '+model_name) else: model_name='no3Dmodel' blacklisted=0 if blacklisted_model_elements != '': if blacklisted_model_elements.find(model_name) != -1: if model_name not in whitelisted_3Dmodels: blacklisted=1 ### if (blacklisted==0): # print(modules[i][4],i,step_module) # print(modules[i][4] == 'noLayer') if step_module != 'no3Dmodel' and modules[i][4] != 'noLayer': createScaledObjs=False if model_name=="box_mcad" or model_name=="cylV_mcad" or model_name=="cylH_mcad": createScaledObjs=True if not createScaledObjs: path_list = [models3D_prefix,models3D_prefix2,models3D_prefix3,models3D_prefix4] model_type = [step_module,step_module_lw,step_module_up,step_module2,step_module2_up,step_module3,step_module3_up,step_module4,step_module4_up,step_module5,step_module5_up] module_path = findModelPath(model_type, path_list) # Find module in all paths and types specified else: scale_vrml=modules[i][8] #sayw(scale_vrml) #scale_val=scale_vrml.split(" ") scale_val=scale_vrml #sayw(scale_val) createScaledBBox(model_name,scale_val) module_path='internal shape' if module_path!='not-found' and module_path!='internal shape': #FreeCADGui.Selection.removeSelection(FreeCAD.activeDocument().ActiveObject) mauitemp volume diff say("opening "+ module_path) mod_cnt+=1 doc1=FreeCAD.ActiveDocument counterObj=0;counter=0 prevObjs = doc1.Objects for ObJ in doc1.Objects: counterObj+=1 say(model_name) Links_available = False if 'LinkView' in dir(FreeCADGui): Links_available = True if model_name not in loaded_models: loaded_models.append(model_name) #sayw(module_path) #make_unicode(module_path) #module_path_n = re.sub("/", "\\\\", module_path) #sayerr(module_path_n) #ImportGui.insert(module_path_n,FreeCAD.ActiveDocument.Name) try: #tobefixed HERE # support for stpZ files if module_path.lower().endswith('stpz'): import stepZ stepZ.insert(module_path,FreeCAD.ActiveDocument.Name) elif module_path.lower().endswith('iges') or module_path.lower().endswith('igs'): sayerr("bug for ImportGui *.iges ... using Part.insert") Part.insert(module_path,FreeCAD.ActiveDocument.Name) else: ImportGui.insert(module_path,FreeCAD.ActiveDocument.Name) # on FC0.20+ there is an issue in inserting a 'compound' # FreeCAD.ActiveDocument.ActiveObject.recompute(True) # say('model imported w ImportGui') #FreeCADGui.Selection.clearSelection() imported_obj_list = [] counterTmp=0 for ObJ in doc1.Objects: counterTmp+=1#stop mp_found=False if counterTmp!=counterObj+1: #multipart loaded #print ('allow_compound ',allow_compound) FreeCADGui.Selection.clearSelection() mp_found=True #if allow_compound != 'False' and allow_compound != 'Hierarchy': if allow_compound != 'False' and (allow_compound != 'Hierarchy' or not Links_available): create_compound(counterObj,model_name) myStep = FreeCAD.ActiveDocument.ActiveObject impLabel = myStep.Label elif allow_compound == 'Hierarchy' and Links_available: imported_obj_list = doc1.Objects[counterObj+1:] compound_found=True #say(str(doc1.Objects)+' HERE') #sayw(str(imported_obj_list)+' HERE') newStep = find_top_container(imported_obj_list) if newStep is not None: impLabel = make_string(newStep.Label) else: #old format import multi objs without a Part container actObjs = doc1.Objects actObjNum = len (actObjs) newStep = doc1.addObject('App::Part',model_name) impLabel = make_string(newStep.Label) for o in actObjs[counterObj:]: doc1.getObject(newStep.Name).addObject(doc1.getObject(o.Name)) #print(o.Label) #myStep = FreeCAD.ActiveDocument.ActiveObject #print(myStep.Label) #impLabel = myStep.Label if (allow_compound != 'Hierarchy' or not Links_available) or not mp_found : newStep=reset_prop_shapes(FreeCAD.ActiveDocument.ActiveObject,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui,True) myStep=newStep if wrl_model != '': wrl_module_path = module_path[:module_path.rfind(u'.')]+wrl_model[-4:] step_transparency = check_wrl_transparency(wrl_module_path) if step_transparency != 0: #keeping transparency if found in step file FreeCADGui.ActiveDocument.getObject(myStep.Name).Transparency = step_transparency impLabel = make_string(myStep.Label) #use_pypro=False if use_pypro: #use python property for timestamp myObj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","model3D") myObj.ViewObject.Proxy = 0 # this is mandatory unless we code the ViewProvider too myObj.Shape = newStep.Shape newStep.Label = 'old' #myObj.Label = impLabel #print(modules[i][10]);print(modules[i][11]) myObj.addProperty("App::PropertyString","TimeStamp") myObj.TimeStamp=str(modules[i][10]) myObj.addProperty("App::PropertyString","Reference") myObj.Reference=str(modules[i][11]) if '*' not in myObj.Reference: myObj.Label = myObj.Reference + '_'+ impLabel else: myObj.Label = 'REF_'+impLabel + '_' FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=FreeCADGui.ActiveDocument.getObject(newStep.Name).ShapeColor FreeCADGui.ActiveDocument.ActiveObject.LineColor=FreeCADGui.ActiveDocument.getObject(newStep.Name).LineColor FreeCADGui.ActiveDocument.ActiveObject.PointColor=FreeCADGui.ActiveDocument.getObject(newStep.Name).PointColor FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=FreeCADGui.ActiveDocument.getObject(newStep.Name).DiffuseColor FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(newStep.Name).Transparency FreeCAD.ActiveDocument.removeObject(newStep.Name) else: #use Label for timestamp myReference=str(modules[i][11]).rstrip('"').lstrip('"') myTimeStamp=str(modules[i][10]) if len(myTimeStamp)> 8: myTimeStamp=myTimeStamp[-12:] myModelNbr=(modules[i][12]) #print (myModelNbr)#;stop if myModelNbr == 1: myModelNbr = '' else: myModelNbr = '['+str(myModelNbr)+']' if '*' not in myReference: newStep.Label = myReference + '_'+ impLabel + '_' + myTimeStamp + myModelNbr else: newStep.Label = 'REF_'+impLabel + '_' + myTimeStamp + myModelNbr #stop #sayerr('loading first time!!!') counterTmp=0 for ObJ in doc1.Objects: counterTmp+=1#stop #sayw(str(counterObj)+":"+str(counterTmp)) #stop if counterTmp==counterObj: #bug in ImportGui.insert iges file sayerr("bug for ImportGui *.iges ... using Part.insert") Part.insert(module_path,FreeCAD.ActiveDocument.Name) # s = Part.Shape() # s.read(module_path) # incoming file igs, stp, stl, brep NO colors! # Part.show(s) #Part.Shape.read(module_path) #Part.insert(module_path,FreeCAD.ActiveDocument.Name) except: #tobefixed sayerr('3D STEP model '+model_name+' is WRONG') msg="""3D STEP model """ msg+=model_name+" is WRONG
or are not allowed Multi Part objects...
" msg+="@ "+module_path+"
...stopping execution!
Please fix the model or change your settings." QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Info ...",msg) stop if allow_compound != 'False' and (allow_compound != 'Hierarchy' or not Links_available): create_compound(counterObj,model_name) newobj = FreeCAD.ActiveDocument.ActiveObject if not use_pypro: if '*' not in myReference: newobj.Label = myReference + '_'+ impLabel + '_' + myTimeStamp + myModelNbr else: newobj.Label = 'REF_'+impLabel + '_' + myTimeStamp + myModelNbr ##addProperty mod #newobj=reset_prop_shapes(FreeCAD.ActiveDocument.ActiveObject,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) elif allow_compound == 'Hierarchy' and mp_found: newobj = newStep #tobefixed if not use_pypro: if '*' not in myReference: newobj.Label = myReference + '_'+ impLabel + '_' + myTimeStamp + myModelNbr else: newobj.Label = 'REF_'+impLabel + '_' + myTimeStamp + myModelNbr else: newobj = FreeCAD.ActiveDocument.ActiveObject ##addProperty mod #newobj=reset_prop_shapes(FreeCAD.ActiveDocument.ActiveObject,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) #newobj = FreeCAD.ActiveDocument.ActiveObject #stop ##newobj.Label=newobj.Label+"_" # not adding '_' at the end of the name if (bbox_all==1) or (bbox_list==1): if whitelisted_model_elements.find(model_name) == -1: bboxLabel=newobj.Label=newobj.Label newobj=createSolidBBox3(newobj) skip_status="not" #tobefixed volume for App::Part if model_name not in whitelisted_3Dmodels: if volume_minimum != 0 or height_minimum != 0: #if checking volume or height if newobj.Shape.Volume>volume_minimum: #mauitemp min vol if abs(newobj.Shape.BoundBox.ZLength)>height_minimum: #mauitemp min height if (height_minimum!=0): say("height > Min height "+ str(newobj.Shape.BoundBox.ZLength) + " "+newobj.Label) if (volume_minimum!=0): say("Volume > Min Volume "+ str(newobj.Shape.Volume) + " "+newobj.Label) else: skip_status="skip" say("height <= Min height "+ str(newobj.Shape.BoundBox.ZLength) + " "+newobj.Label) else: skip_status="skip" say("Volume <= Min Volume "+ str(newobj.Shape.BoundBox.ZLength) + " "+newobj.Label) loaded_models_skipped.append(skip_status) use_cache=0 #say("NO use_cache") FreeCADGui.Selection.clearSelection() for ObJ in doc1.Objects: counter+=1 if counterObj+1 != counter and (allow_compound != 'Hierarchy' or not Links_available): msg="""3D STEP model """ msg+=model_name+" is NOT fused ('union') in a single part ...
" msg+="@ "+module_path+"
...stopping execution!
Please fix the model." QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Info ...",msg) stop if skip_status!="skip": loaded_model_objs.append(newobj) else: loaded_model_objs.append(None) FreeCAD.activeDocument().removeObject(newobj.Name) else: use_cache=1 #sayw("using cache!!!") #say(loaded_models);say(" models") #say(str(len(loaded_model_objs))+" nbr loaded objs") if use_cache: counterObj=counterObj-2 pos_x=modules[i][1]-off_x pos_y=modules[i][2]-off_y rot=modules[i][3] step_layer=modules[i][4] #wrl_off_x=modules[i][6] #rotz_vrml_norm=modules[i][7][0].replace("(xyz ","") #rotz_vrml_norm=modules[i][7].replace("(xyz ","") #say("rotz_vrml_norm ");sayw(rotz_vrml_norm) wrl_rot=modules[i][7] #sayerr(wrl_rot);sayw(float(wrl_rot[0]));stop pos_vrml=modules[i][6] wrl_pos=pos_vrml #sayerr(wrl_pos);sayw(float(wrl_pos[0]));stop isVirtual=modules[i][9] isHidden=modules[i][13] if (isHidden): md_hide=True else: md_hide=False #if show_debug: # sayw(wrl_rot) # sayerr(modules[i]) #wrl_pos=pos_vrml[0].split(" ") #wrl_pos=pos_vrml.split(" ") #say(rotz_vrml_norm) #sayw("wrl rot ");sayw(wrl_rot) #say("wrl pos ");sayw(wrl_pos) #say (str(rot)) for j in range(len(loaded_models)): if loaded_models[j]==model_name: #say (str(i)+" i") idxO=j if loaded_models_skipped[idxO]!="skip": if use_cache: #mod_cnt+=1 sayw('copying from cache') ##impPart=copy_objs(loaded_model_objs[idxO],FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) ### FreeCAD.ActiveDocument.addObject('Part::Feature',loaded_model_objs[idxO].Label).Shape=loaded_model_objs[idxO].Shape ### #FreeCAD.ActiveDocument.ActiveObject.Label=obj.Label ### FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=Gui.ActiveDocument.getObject(loaded_model_objs[idxO].Name).ShapeColor ### FreeCADGui.ActiveDocument.ActiveObject.LineColor=Gui.ActiveDocument.getObject(loaded_model_objs[idxO].Name).LineColor ### FreeCADGui.ActiveDocument.ActiveObject.PointColor=Gui.ActiveDocument.getObject(loaded_model_objs[idxO].Name).PointColor ### FreeCADGui.ActiveDocument.ActiveObject.DiffuseColor=Gui.ActiveDocument.getObject(loaded_model_objs[idxO].Name).DiffuseColor ### FreeCAD.ActiveDocument.recompute() try: # Links ATM don't support added proprierties if use_Links and links_imp_mode == 'links_allowed': o = loaded_model_objs[idxO] # FreeCAD.ActiveDocument.addObject('App::Link',o.Label+'_ln_').setLink(o) if use_pypro: FreeCAD.ActiveDocument.addObject('App::LinkPython',o.Label).setLink(o) FreeCAD.ActiveDocument.ActiveObject.addProperty("App::PropertyString","TimeStamp") #FreeCAD.ActiveDocument.ActiveObject.TimeStamp=str(modules[i][10]) FreeCAD.ActiveDocument.ActiveObject.addProperty("App::PropertyString","Reference") #FreeCAD.ActiveDocument.ActiveObject.Reference=str(modules[i][11]) FreeCAD.ActiveDocument.ActiveObject.ViewObject.Proxy = 0 else: FreeCAD.ActiveDocument.addObject('App::Link',o.Label+'_ln_').setLink(o) else: FreeCAD.ActiveDocument.copyObject(loaded_model_objs[idxO], True) #allow_compound != 'Hierarchy': impPart=FreeCAD.ActiveDocument.ActiveObject if use_pypro: impPart.TimeStamp=str(modules[i][10]) impPart.Reference=str(modules[i][11]) if '*' not in impPart.Reference: impPart.Label = loaded_model_objs[idxO].Label[loaded_model_objs[idxO].Label.find('_')+1:] impPart.Label = impPart.Reference + '_' + impPart.Label # loaded_model_objs[idxO].Label #impPart.Label = impPart.Reference + '_'+ impLabel #say("FC 0.15 copy method for preserving color in fusion") else: impPart.Label = 'REF_'+loaded_model_objs[idxO].Label + '_' + myTimeStamp else: myTimeStamp=str(modules[i][10]) if len(myTimeStamp)> 8: myTimeStamp=myTimeStamp[-12:] myReference=str(modules[i][11]).rstrip('"').lstrip('"') myModelNbr=(modules[i][12]) #print (myModelNbr);stop if myModelNbr == 1: myModelNbr = '' else: myModelNbr = '['+str(myModelNbr)+']' if '*' not in myReference: impPart.Label = loaded_model_objs[idxO].Label[loaded_model_objs[idxO].Label.find('_')+1:loaded_model_objs[idxO].Label.rfind('_')] impPart.Label = myReference + '_' + impPart.Label + '_' + myTimeStamp + myModelNbr # loaded_model_objs[idxO].Label else: impPart.Label = 'REF_'+loaded_model_objs[idxO].Label[:loaded_model_objs[idxO].Label.rfind('_')] + '_' + myTimeStamp + myModelNbr except: #impPart=copy_objs(loaded_model_objs[idxO],FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) impPart=copy_objs(loaded_model_objs[idxO],FreeCAD.ActiveDocument) sayw("fusion color problem in FC earlier than 0.15\n") pass ## #impPart=reset_prop_shapes2(impPart,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) ##resetting placement properties impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,-pcbThickness),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) #obj.Placement = impPart.Placement if use_Links and links_imp_mode == 'links_allowed': shape=Part.getShape(o) else: shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,0,1),rot) impPart.Placement=shape.Placement #impPart.Label = impPart.Label + '_ch_' #sayerr('caching') else: impPart=loaded_model_objs[idxO] #impPart.Label = impPart.Label + '_nc_' ## say(loaded_model_objs) say("module "+step_module) #say("selection 3D model "+ impPart.Label) #to verify!!!! next row ##impPart=reset_prop_shapes(impPart,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) model3D=impPart.Name #say("impPart "+ impPart.Name) obj = FreeCAD.ActiveDocument.getObject(model3D) FreeCADGui.Selection.addSelection(obj) obj=FreeCAD.ActiveDocument.ActiveObject #volume_minimum=1 myPart=FreeCAD.ActiveDocument.getObject(obj.Name) #mauitemp min vol if md_hide: myPart.ViewObject.Visibility=False # myPart.ViewObject.Transparency=70 sayerr('hiding '+myPart.Label) my_hide_list+=myPart.Label+'\r\n' #else: # myPart.ViewObject.Transparency=0 # sayerr('hiding '+myPart.Label) #sayw(obj.Label) #sayw(step_layer); #sayw(str(myPart.Shape.Volume)) #sayw(str(myPart.Shape.BoundBox.ZLength)) if step_layer == 'Top': if full_placement: ## new placement wip #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0]),pos_y+float(wrl_pos[1]),0+float(wrl_pos[2])),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),rot)) # (yaw z, pitch y, roll x) #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) ##impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(rot,-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl #say("rot z top ");sayw(wrl_rot);sayw(rot) impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl if impPart.TypeId=='App::Link' or impPart.TypeId=='App::LinkPython': shape=Part.getShape(o) elif impPart.TypeId=='App::Part': #tobefixed shape=Part.getShape(impPart) else: shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,0,1),rot+float(wrl_rot[2])) impPart.Placement=shape.Placement; ##TBChecked if force_transparency: FreeCADGui.ActiveDocument.ActiveObject.Transparency=70 ##TBChecked else: impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),rot)) FreeCADGui.Selection.addSelection(impPart) ## to evaluate to add App::Part hierarchy # App.activeDocument().Tip = App.activeDocument().addObject('App::Part','Part') # App.activeDocument().Part.Label = 'Part' # Gui.activeView().setActiveObject('part', App.activeDocument().Part) # App.ActiveDocument.recompute() if isVirtual == 0: if use_AppPart and not use_LinkGroups: #layer Top FreeCAD.ActiveDocument.getObject(top_name).addObject(impPart) modelTop_nbr+=1 elif use_LinkGroups: #FreeCAD.ActiveDocument.getObject(impPart.Name).adjustRelativeLinks(FreeCAD.ActiveDocument.getObject('Top')) FreeCAD.ActiveDocument.getObject(top_name).ViewObject.dropObject(FreeCAD.ActiveDocument.getObject(impPart.Name),FreeCAD.ActiveDocument.getObject(impPart.Name),'',[]) modelTop_nbr+=1 else: FreeCAD.ActiveDocument.getObject(stepM_name).addObject(impPart) else: #virtual if use_AppPart and not use_LinkGroups: #layer Top #print(topV_name) FreeCAD.ActiveDocument.getObject(topV_name).addObject(impPart) virtualTop_nbr+=1 elif use_LinkGroups: #FreeCAD.ActiveDocument.getObject(impPart.Name).adjustRelativeLinks(FreeCAD.ActiveDocument.getObject('TopV')) FreeCAD.ActiveDocument.getObject(topV_name).ViewObject.dropObject(FreeCAD.ActiveDocument.getObject(impPart.Name),FreeCAD.ActiveDocument.getObject(impPart.Name),'',[]) virtualTop_nbr+=1 else: FreeCAD.ActiveDocument.getObject(stepV_name).addObject(impPart) virtual_nbr+=1 ### else: #Bottom #Bottom #impPart=reset_prop_shapes2(impPart,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) if full_placement: #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,-pcbThickness),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) ## new placement wip #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness-float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2])-rot+180,-float(wrl_rot[1])+180,-float(wrl_rot[0]))) ##impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness-float(wrl_pos[2])*25.4),FreeCAD.Rotation(-rot+180,-float(wrl_rot[1])+180,-float(wrl_rot[0]))) #rot is already rot fp -rot wrl impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,+pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl if impPart.TypeId=='App::Link' or impPart.TypeId=='App::LinkPython': shape=Part.getShape(o) else: shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,0,1),180+rot+float(wrl_rot[2])) impPart.Placement=shape.Placement; if impPart.TypeId=='App::Link' or impPart.TypeId=='App::LinkPython': shape=Part.getShape(o) else: shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,1,0),180) impPart.Placement=shape.Placement; if force_transparency: FreeCADGui.ActiveDocument.ActiveObject.Transparency=60 #say("rot z bot ");sayw(wrl_rot);sayw(rot) #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,-pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) else: impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,-pcbThickness),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) #obj.Placement = impPart.Placement if impPart.TypeId=='App::Link' or impPart.TypeId=='App::LinkPython': shape=Part.getShape(o) else: shape=impPart.Shape.copy() shape.Placement=impPart.Placement; #if not full_placement: #shape.rotate((pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,-pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1])+180,-float(wrl_rot[0]))) #else: shape.rotate((pos_x,pos_y,-pcbThickness),(0,0,1),-rot+180) impPart.Placement=shape.Placement FreeCADGui.Selection.addSelection(impPart) FreeCAD.ActiveDocument.getObject(impPart.Name) if isVirtual == 0: if use_AppPart and not use_LinkGroups: #layer Top FreeCAD.ActiveDocument.getObject(bot_name).addObject(impPart) modelBot_nbr+=1 elif use_LinkGroups: #FreeCAD.ActiveDocument.getObject(impPart.Name).adjustRelativeLinks(FreeCAD.ActiveDocument.getObject('Bot')) FreeCAD.ActiveDocument.getObject(bot_name).ViewObject.dropObject(FreeCAD.ActiveDocument.getObject(impPart.Name),FreeCAD.ActiveDocument.getObject(impPart.Name),'',[]) modelBot_nbr+=1 else: FreeCAD.ActiveDocument.getObject(stepM_name).addObject(impPart) else: #virtual if use_AppPart and not use_LinkGroups: #layer Top FreeCAD.ActiveDocument.getObject(botV_name).addObject(impPart) virtualBot_nbr+=1 elif use_LinkGroups: #FreeCAD.ActiveDocument.getObject(impPart.Name).adjustRelativeLinks(FreeCAD.ActiveDocument.getObject('BotV')) FreeCAD.ActiveDocument.getObject(botV_name).ViewObject.dropObject(FreeCAD.ActiveDocument.getObject(impPart.Name),FreeCAD.ActiveDocument.getObject(impPart.Name),'',[]) virtualBot_nbr+=1 else: FreeCAD.ActiveDocument.getObject(stepV_name).addObject(impPart) virtual_nbr+=1 ### elif module_path=='internal shape': impPart=FreeCAD.ActiveDocument.ActiveObject scale_vrml=modules[i][8] #sayw(scale_vrml) #scale_val=scale_vrml.split(" ") scale_val=scale_vrml #sayw(scale_val) pos_x=modules[i][1]-off_x pos_y=modules[i][2]-off_y rot=modules[i][3] wrl_rot=modules[i][7] step_layer=modules[i][4] #wrl_off_x=modules[i][6] #rotz_vrml_norm=modules[i][7][0].replace("(xyz ","") #rotz_vrml_norm=modules[i][7].replace("(xyz ","") #say("rotz_vrml_norm ");sayw(rotz_vrml_norm) #wrl_rot=rotz_vrml_norm.split(" ") pos_vrml=modules[i][6] wrl_pos=pos_vrml #wrl_pos=pos_vrml[0].split(" ") #wrl_pos=pos_vrml.split(" ") #say(rotz_vrml_norm) #sayw("wrl rot ");sayw(wrl_rot) #say("wrl pos ");sayw(wrl_pos) shape_vol=abs(float(scale_val[0])*float(scale_val[1])*float(scale_val[2])) skip_status="not" if shape_vol>volume_minimum: #mauitemp min vol if abs(float(scale_val[2]))>height_minimum: #mauitemp min height if (height_minimum!=0): say("height > Min height "+ str(scale_val[2]) + " "+impPart.Label) if (volume_minimum!=0): say("Volume > Min Volume "+ str(shape_vol) + " "+impPart.Label) else: skip_status="skip" say("height <= Min height "+ str(scale_val[2]) + " "+impPart.Label) else: skip_status="skip" say("Volume <= Min Volume "+ str(shape_vol) + " "+impPart.Label) if skip_status=="not": if step_layer == 'Top': if full_placement: ## new placement wip #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0]),pos_y+float(wrl_pos[1]),0+float(wrl_pos[2])),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),rot)) # (yaw z, pitch y, roll x) #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(rot,-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(rot,-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,0,1),rot+float(wrl_rot[2])) impPart.Placement=shape.Placement; if force_transparency: FreeCADGui.ActiveDocument.ActiveObject.Transparency=100 ##TBChecked shapes #say("rot z top ");sayw(wrl_rot);sayw(rot) else: impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),rot)) FreeCADGui.Selection.addSelection(impPart) if use_AppPart: #Top FreeCAD.ActiveDocument.getObject(top_name).addObject(impPart) modelTop_nbr+=1 else: FreeCAD.ActiveDocument.getObject(stepM_name).addObject(impPart) else: #Bottom #Bottom #impPart=reset_prop_shapes2(impPart,FreeCAD.ActiveDocument, FreeCAD,FreeCADGui) if full_placement: #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,-pcbThickness),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) ## new placement wip #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness-float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2])-rot+180,-float(wrl_rot[1])+180,-float(wrl_rot[0]))) # impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness),FreeCAD.Rotation(-float(wrl_rot[2])+180,-float(wrl_rot[1])+180,-float(wrl_rot[0]))) #rot is already rot fp -rot wrl # #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness-float(wrl_pos[2])*25.4),FreeCAD.Rotation(float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl # shape=impPart.Shape.copy() # shape.Placement=impPart.Placement; # shape.rotate((pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness-float(wrl_pos[2])*25.4),(0,0,1),-180+rot-float(wrl_rot[2])) # impPart.Placement=shape.Placement; #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[1])*25.4,pos_y+float(wrl_pos[0])*25.4,-pcbThickness-float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2])-rot+180,-float(wrl_rot[1])+180,-float(wrl_rot[0]))) impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,+pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,0,1),180+rot+float(wrl_rot[2])) impPart.Placement=shape.Placement; shape=impPart.Shape.copy() shape.Placement=impPart.Placement; shape.rotate((pos_x,pos_y,0),(0,1,0),180) impPart.Placement=shape.Placement; if force_transparency: FreeCADGui.ActiveDocument.ActiveObject.Transparency=60 ##TBChecked shapes #say("rot z bot ");sayw(wrl_rot);sayw(rot) #impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,-pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) else: impPart.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,-pcbThickness),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) #obj.Placement = impPart.Placement shape=impPart.Shape.copy() shape.Placement=impPart.Placement; #if not full_placement: #shape.rotate((pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,-pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1])+180,-float(wrl_rot[0]))) #else: shape.rotate((pos_x,pos_y,-pcbThickness),(0,0,1),-rot+180) impPart.Placement=shape.Placement FreeCADGui.Selection.addSelection(impPart) FreeCAD.ActiveDocument.getObject(impPart.Name) if use_AppPart: #Bot FreeCAD.ActiveDocument.getObject("Bot").addObject(impPart) modelBot_nbr+=1 else: FreeCAD.ActiveDocument.getObject(stepM_name).addObject(impPart) else: FreeCAD.ActiveDocument.removeObject(impPart.Name) else: #say("error missing "+ make_string(models3D_prefix)+make_string(step_module)) say("error missing "+ make_string(module_container)) #test = missing_models.find(make_string(step_module)) test = missing_models.find(make_string(module_container)) if test == -1: #missing_models += make_string(models3D_prefix)+make_string(step_module)+'\r\n' #matched # missing_models += make_string(step_module)+'\r\n' #matched missing_models += make_string(module_container)+' (.stp or .step)\r\n' #matched ### gui_refresh=20 if int(PySide.QtCore.qVersion().split('.')[0]) > 4 or use_Links: # Qt5 or Links refresh if mod_cnt%gui_refresh == 0: # (one on 'gui_refresh' times) #FreeCADGui.updateGui() QtGui.QApplication.processEvents() ### sayw('added '+str(mod_cnt)+' model(s)') ### ### #say(loaded_models); #sleep if virtual_nbr==0: #FreeCAD.ActiveDocument.getObject("Step_Virtual_Models").removeObjectsFromDocument() FreeCAD.ActiveDocument.removeObject(stepV_name) if use_AppPart: FreeCAD.ActiveDocument.removeObject(botV_name) FreeCAD.ActiveDocument.removeObject(topV_name) else: if use_AppPart: if virtualTop_nbr==0: #FreeCAD.ActiveDocument.getObject("TopV").removeObjectsFromDocument() FreeCAD.ActiveDocument.removeObject(topV_name) #FreeCAD.ActiveDocument.recompute() if virtualBot_nbr==0: #FreeCAD.ActiveDocument.getObject("BotV").removeObjectsFromDocument() FreeCAD.ActiveDocument.removeObject(botV_name) #FreeCAD.ActiveDocument.recompute() if use_AppPart: if modelTop_nbr==0: #FreeCAD.ActiveDocument.getObject("Top").removeObjectsFromDocument() FreeCAD.ActiveDocument.removeObject(top_name) if modelBot_nbr==0: #FreeCAD.ActiveDocument.getObject("Bot").removeObjectsFromDocument() FreeCAD.ActiveDocument.removeObject(bot_name) FreeCAD.ActiveDocument.recompute() say_time() FreeCADGui.Selection.clearSelection() if 0: #try print('TreeView Test collapsing') FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.Board) import kicadStepUpCMD FreeCADGui.runCommand('ksuToolsToggleTreeView',0) s=FreeCADGui.Selection.getSelection()[0] print(s.Label) FreeCADGui.runCommand('ksuToolsToggleTreeView',0) #kicadStepUpCMD.ksuToolsToggleTreeView.Activated(s) #FreeCADGui.Selection.clearSelection() #FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.Board) #s=FreeCADGui.Selection.getSelection()[0] #print(s.Label) #kicadStepUpCMD.ksuToolsToggleTreeView.Activated(s) FreeCADGui.Selection.clearSelection() elif 0: #except: import expTree; print('TreeView Test collapsing 2') #import importlib; importlib.reload(expTree); FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.Board) expTree.collS_Tree() FreeCADGui.Selection.clearSelection() print('TreeView Test collapsing 2 step 2') else: pass #print (my_hide_list) if my_hide_list != "": n_rpt_max=10 sayw(str(len(my_hide_list.split('\r\n'))-1)+" model[s] hidden") sayw(str(my_hide_list.split('\r\n')[:-1])) my_hide_res = [] my_hide_res = my_hide_list.split('\r\n') wmsg="""... model[s] hidden
""" for i in range(min(len (my_hide_res),n_rpt_max)): wmsg=wmsg+my_hide_res[i]+'
' QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Warning ...",wmsg+'. . . '+str(len(my_hide_res)-1)+' model[s] hidden' ) if missing_models != '': last_pcb_path_local = re.sub("\\\\", "/", last_pcb_path) last_pcb_path_local_U = make_unicode(last_pcb_path_local) say("missing models");say (missing_models) say("searching path") for mpath in path_list: say(mpath) #say(models3D_prefix_U);say (models3D_prefix2_U) say(last_pcb_path_local_U) missings=[] missings=missing_models.split('\r\n') n_rpt_max=10 #if len (missings) > n_rpt_max: #warning_nbr =-1 for skipping the test wmsg="""... missing module(s)
""" wmsg+="""... searching path:
""" for mpath in path_list: wmsg+=mpath+"""
""" #wmsg+=models3D_prefix_U+"""
""" #wmsg+=models3D_prefix2_U+"""
""" wmsg+=last_pcb_path_local_U+"""
""" wmsg+="""... missing module(s) '.step' or '.stp' or .iges' or '.igs'
""" for i in range(min(len (missings),n_rpt_max)): wmsg=wmsg+missings[i]+'
' QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Error ...",wmsg+'
. . . missing '+str(len(missings)-1)+' model(s)' ) if len (missings) > warning_nbr and warning_nbr != -1: #warning_nbr =-1 for skipping the test QtGui.QApplication.restoreOverrideCursor() wmsg="""""" wmsg+="too many missing modules [" wmsg+=str(len (missings))+"]
Have you configured your KISYS3DMOD path
or 3d model prefix path?
" wmsg+="
StepUp configuration options are located in the preferences system of FreeCAD." wmsg+="
Are you on FC Snap or Flatpack?
You may need to \'bind mount\' your 3d models folder" reply = QtGui.QMessageBox.information(None,"Error ...",wmsg) #if blacklisted_model_elements != '': # FreeCAD.Console.PrintMessage("black-listed module "+ '\n'.join(map(str, blacklisted_models))) # reply = QtGui.QMessageBox.information(None,"Info ...","... black-listed module(s)\n"+ '\n'.join(map(str, blacklisted_models))) # #FreeCAD.Console.PrintMessage("black-listed module "+ '\n'.join(map(str, blacklisted_models))) return blacklisted_model_elements ### def getPads(board_elab,pcbThickness): # pad TopPadList=[] BotPadList=[] HoleList=[] THPList=[] for module in re.findall(r'\[start\]\(module(.+?)\)\[stop\]', board_elab, re.MULTILINE|re.DOTALL): [X1, Y1, ROT] = re.search(r'\(at\s+([0-9\.-]*?)\s+([0-9\.-]*?)(\s+[0-9\.-]*?|)\)', module).groups() # X1 = float(X1) Y1 = float(Y1) * (-1) if ROT == '': ROT = 0.0 else: ROT = float(ROT) #say('module pos & rot '+str(X1)+' '+str(Y1)+' '+str(ROT)) # for pad in getPadsList(module): #say (pad) # pads.append({'x': x, 'y': y, 'rot': rot, 'padType': pType, 'padShape': pShape, 'rx': drill_x, 'ry': drill_y, 'dx': dx, 'dy': dy, 'holeType': hType, 'xOF': xOF, 'yOF': yOF, 'layers': layers}) pType = pad['padType'] pShape = pad['padShape'] xs = pad['x'] + X1 ys = pad['y'] + Y1 dx = pad['dx'] dy = pad['dy'] hType = pad['holeType'] drill_x = pad['rx'] drill_y = pad['ry'] xOF = pad['xOF'] yOF = pad['yOF'] rot = pad['rot'] if ROT != 0: rot -= ROT rx=drill_x ry=drill_y rx=float(rx) ry=float(ry) numberOfLayers = pad['layers'].split(' ') #if pType=="thru_hole": #pad shape - circle/rec/oval/trapezoid perc=0 if pShape=="circle" or pShape=="oval": pShape="oval" perc=100 # pad type - SMD/thru_hole/connect if dx>rx and dy>ry: #say(pType) #say(str(dx)+"+"+str(rx)+" dx,rx") #say(str(dy)+"+"+str(ry)+" dy,ry") #say(str(xOF)+"+"+str(yOF)+" xOF,yOF") x1=xs+xOF y1=ys-yOF #yoffset opposite #say(str(x1)+"+"+str(y1)+" x1,y1") top=False bot=False if 'F.Cu' in numberOfLayers: top=True if '*.Cu' in numberOfLayers: top=True bot=True if 'B.Cu' in numberOfLayers: bot=True if rx!=0: #say(str(min_drill_size));say(' ');say(rx);say(' ');say(str(ry)); if (rx >= min_drill_size) or (ry >= min_drill_size): obj=createHole3(xs,ys,rx,ry,"oval",pcbThickness) #need to be separated instructions #say(HoleList) if rot!=0: rotateObj(obj, [xs, ys, rot]) rotateObj(obj, [X1, Y1, ROT]) HoleList.append(obj) ### cmt- #todo: pad type trapez return HoleList ### def getPads_flat(board_elab): # pad TopPadList=[] BotPadList=[] HoleList=[] THPList=[] for module in re.findall(r'\[start\]\(module(.+?)\)\[stop\]', board_elab, re.MULTILINE|re.DOTALL): [X1, Y1, ROT] = re.search(r'\(at\s+([0-9\.-]*?)\s+([0-9\.-]*?)(\s+[0-9\.-]*?|)\)', module).groups() # X1 = float(X1) Y1 = float(Y1) * (-1) if ROT == '': ROT = 0.0 else: ROT = float(ROT) #say('module pos & rot '+str(X1)+' '+str(Y1)+' '+str(ROT)) # for pad in getPadsList(module): #say (pad) # # pads.append({'x': x, 'y': y, 'rot': rot, 'padType': pType, 'padShape': pShape, 'rx': drill_x, 'ry': drill_y, 'dx': dx, 'dy': dy, 'holeType': hType, 'xOF': xOF, 'yOF': yOF, 'layers': layers}) pType = pad['padType'] pShape = pad['padShape'] xs = pad['x'] + X1 ys = pad['y'] + Y1 dx = pad['dx'] dy = pad['dy'] hType = pad['holeType'] drill_x = pad['rx'] drill_y = pad['ry'] xOF = pad['xOF'] yOF = pad['yOF'] rot = pad['rot'] if ROT != 0: rot -= ROT rx=drill_x ry=drill_y rx=float(rx) ry=float(ry) numberOfLayers = pad['layers'].split(' ') #say(numberOfLayers ) #if pType=="thru_hole": #pad shape - circle/rec/oval/trapezoid perc=0 if pShape=="circle" or pShape=="oval": pShape="oval" perc=100 # pad type - SMD/thru_hole/connect if dx>rx and dy>ry: #say(pType+"") #say(str(dx)+"+"+str(rx)+" dx,rx") #say(str(dy)+"+"+str(ry)+" dy,ry") #say(str(xOF)+"+"+str(yOF)+" xOF,yOF") x1=xs+xOF y1=ys-yOF #yoffset opposite #say(str(x1)+"+"+str(y1)+" x1,y1") top=False bot=False if 'F.Cu' in numberOfLayers: top=True if '*.Cu' in numberOfLayers: top=True bot=True if 'B.Cu' in numberOfLayers: bot=True if rx!=0: #say(str(min_drill_size));say(' ');say(rx);say(' ');say(str(ry)); #if (rx > min_drill_size): if (rx >= min_drill_size) or (ry >= min_drill_size): #obj=createHole3(xs,ys,rx,ry,"oval",pcbThickness) #need to be separated instructions obj=createHole4(xs,ys,rx,ry,"oval") #need to be separated instructions #say(HoleList) if rot!=0: rotateObj(obj, [xs, ys, rot]) rotateObj(obj, [X1, Y1, ROT]) HoleList.append(obj) ### cmt- #todo: pad type trapez return HoleList ### def Elaborate_Kicad_Board(filename): global xMax, xmin, yMax, ymin global ignore_utf8, ignore_utf8_incfg Levels={} content=[] #txtFile = __builtin__.open(filename,"r") ##txtFile = __builtin__.open(filename,"rb") txtFile = codecs.open(filename, mode='rb', encoding='utf-8', errors='replace', buffering=1) #test maui utf-8 content = txtFile.readlines() content.append(" ") txtFile.close() data=''.join(content) if ignore_utf8: content=re.sub(r'[^\x00-\x7F]+',' ', data) #workaround to remove utf8 extra chars sayw('removing utf-8 chars') else: content=data ## content=data #say(len(content)) Kicad_Board_elaborated = content #''.join(content) if save_temp_data: home = expanduser("~") t1_name=home+os.sep+'test.txt' #f = __builtin__.open(t1_name,'w') # f = builtin.open(t1_name,'wb') #py2 f = builtin.open(t1_name,'w') #py3 f.write(Kicad_Board_elaborated) # python will convert \n to os.linesep f.close() # you can omit in most cases as the destructor will call it #say(len(Kicad_Board_elaborated)) #stop version=getPCBVersion(Kicad_Board_elaborated) pcbThickness=getPCBThickness(Kicad_Board_elaborated) say('kicad_pcb version ' +str(version)) if version < 3: QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Error ...","... KICAD pcb version "+ str(version)+" not supported \r\n"+"\r\nplease open and save your board with the latest kicad version") sys.exit("pcb version not supported") if version==3: Edge_Cuts_lvl=28 Top_lvl=15 if version>=4: Edge_Cuts_lvl=44 Top_lvl=0 # say(Kicad_Board) modified = '' j = 0; txt = ''; start = 0; s=0; prev_char="_" closing_char="" #say (len(Kicad_Board_elaborated)) for i in Kicad_Board_elaborated[1:]: if i in ['"', "'"] and s == 0: closing_char=i if prev_char!="\\": s = 1 elif i in [closing_char] and s == 1: if prev_char!="\\": s = 0 if s == 0: if i == '(': j += 1 start = 1 elif i == ')': j -= 1 txt += i prev_char=i if j == 0 and start == 1: modified += '[start]' + txt.strip() + '[stop]' txt = '' start = 0 #say(len(modified)) #stop #maui layers = re.search(r'\[start\]\(layers(.+?)\)\[stop\]', modified, re.MULTILINE|re.DOTALL).group(0) for k in re.findall(r'\((.*?) (.*?) .*?\)', layers): Levels[k[1]] = int(k[0]) if Levels[k[1]] == Edge_Cuts_lvl: ##Edge.Cuts pcb version 4 #myfile3.write(str(k)[8:-2]) pcbEdgeName=str(k)[8:-2] if save_temp_data: home = expanduser("~") t2_name=home+os.sep+'testM.txt' #f = __builtin__.open(t2_name,'w') #f = builtin.open(t2_name,'wb') #py2 f = builtin.open(t2_name,'w') #p3 f.write(modified) # python will convert \n to os.linesep f.close() # you can omit in most cases as the destructor will call it return modified,Levels,Edge_Cuts_lvl,Top_lvl,version,pcbThickness ### end Elaborate_Kicad_Board def get3DParams (mdl_name,params,rot, virtual): ''' list of single 3D model with parameters ''' global addVirtual rotz_vrml_m = re.findall(r'\(rotate.*$', params) #bug on multiple model per footprint wrl, step sequence !!! #sayw(rotz_vrml_m);sayw("here") rotz='' if rotz_vrml_m: rotz_vrml=rotz_vrml_m[0].lstrip('(rotate').lstrip(' ') #say('rotation ');sayw(rotz_vrml)#; rotz=rotz_vrml #rotz=rotz.lstrip('(rotate') #rotz=rotz[13:-1] rotz=rotz[5:] #sayw("rotz:"+rotz) #stop #say("rotz:"+rotz) temp=rotz.split(" ") #say("rotz temp:"+temp[2]) rotz=temp[2] rotx=temp[0] roty=temp[1] #warn=None warn="" # if float(rotx)!=0: # warn=("rx ") # if float(roty)!=0: # warn+=("ry ") #if warn: # sayw(warn) #say("rotate vrml: "+rotz) else: rotz_vrml_m="(xyz 0 0 0" if rotz=='': rotz=0.0 else: rotz=float(rotz) rot_comb=rot-rotz #adding vrml module z-rotation #re.findall(r'\(rotate\s+(.+?)\)', i) pos_vrml_m = re.findall(r'\(at\s\(xyz\s+(.+?)\)', params) pos_vrml=pos_vrml_m[0] #len(model_list)-j-1] #bug on multiple model per footprint #if pos_vrml: # say('pos ');sayw(pos_vrml)#; #say(i) scale_vrml_m = re.findall(r'\(scale\s\(xyz\s+(.+?)\)', params) scale_vrml=scale_vrml_m[0] #len(model_list)-j-1] #bug on multiple model per footprint error_scale_module=False if scale_vrml: #say('scale ');sayw(scale_vrml)#; #error_scale_module=False scale_vrml_vals=scale_vrml.split(" ") xsc_vrml_val=scale_vrml_vals[0] ysc_vrml_val=scale_vrml_vals[1] zsc_vrml_val=scale_vrml_vals[2] # if scale_vrml!='1 1 1': if float(xsc_vrml_val)!=1 or float(ysc_vrml_val)!=1 or float(zsc_vrml_val)!=1: if "box_mcad" not in params and "cylV_mcad" not in params and "cylH_mcad" not in params: sayw('wrong scale!!! set scale to (1 1 1)') error_scale_module=True #model_list.append(mdl_name[0]) #model=model_list[j]+'.wrl' model=mdl_name[0]+'.wrl' # if show_debug: # sayerr(model_list) # #stop if (virtual==1 and addVirtual==0): model_name='no3Dmodel' side='noLayer' if model: sayw("virtual model "+model+" skipped") #virtual found warning else: if model: # say (model.group(0)) #model_name=model.group(0)[6:] #model_name=model[6:] model_name=model #sayw(model_name) if "box_mcad" not in model_name and "cylV_mcad" not in model_name and "cylH_mcad" not in model_name: if error_scale_module: sayw('wrong scale!!! for '+model_name+' Set scale to (1 1 1)') msg="""Error in '.kicad_pcb' model footprint
""" msg+="
reset values of
"+model_name+"
to:
" msg+="(scale (xyz 1 1 1))
" warn+=("reset values of scale to (xyz 1 1 1)") ##reply = QtGui.QMessageBox.information(None,"info", msg) #stop #model_name=model_name[1:] #say(model_name) #sayw("here") else: model_name='no3Dmodel' side='noLayer' #sayerr('no3Dmodel') return model_name, rot_comb, warn, pos_vrml, rotz_vrml, scale_vrml ### def getPCBThickness(Board): #say(len(Kicad_Board)) return float(re.findall(r'\(thickness (.+?)\)', Board)[0]) def getPCBVersion(Board): return int(re.findall(r'\(kicad_pcb \(version (.+?)\)', Board)[0]) def getPCBArea(Kicad_Board): area = (re.findall(r'\(area (.+?)\)', Kicad_Board)[0]) # say(area) return area def createSolidBBox(model3D): selEx=model3D selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: s = objs[0].Shape name=objs[0].Label FreeCAD.Console.PrintMessage(name+" name \r\n") # boundBox boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) #say(str(boundBox_)) #say("Rectangle : "+str(boundBox_.XLength)+" x "+str(boundBox_.YLength)+" x "+str(boundBox_.ZLength)) #say("_____________________") #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) obj=FreeCAD.ActiveDocument.addObject('Part::Feature',name) obj.Shape=Part.makeBox(boundBox_.XLength, boundBox_.YLength, boundBox_.ZLength, FreeCAD.Vector(oripl_X,oripl_Y,oripl_Z), FreeCAD.Vector(0,0,1)) # Part.show(cube) #say("cube name "+ obj.Name) else: FreeCAD.Console.PrintMessage("Select a single part object !"+"\r\n") #end bbox macro name=obj.Name #say("bbox name "+name) return name del objs ### end createSolidBBox def findPcbCenter(pcbName): pcb = FreeCAD.ActiveDocument.getObject(pcbName) s=pcb.Shape name=pcb.Label # boundBox boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength center = s.BoundBox.Center #say(center) #say("["+str(center.x)+"],["+str(center.y)+"] center of pcb") a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) #say(str(boundBox_)) #say("Rectangle : "+str(boundBox_.XLength)+" x "+str(boundBox_.YLength)+" x "+str(boundBox_.ZLength)) #say("_____________________") #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) center_x=center.x; center_y=center.y bb_x=boundBox_.XLength; bb_y=boundBox_.YLength return center_x, center_y, bb_x, bb_y ### end findPcbCenter def getArc_minMax(xC,xA,yC,yA,alpha): # x1=xA start point; x2=xC center; xB end point; alpha=angle global xMax, xmin, yMax, ymin j=0 R=sqrt((xA-xC)**2+(yA-yC)**2) #say('R = '+str(R)) if (xA>=xC) and (yAxC) and (yA= beta and ABeta <= pi/2: xB=R*sin(alpha+beta)+xC xMax=max(xB,xMax) xmin= min(xA,xmin) yB=yC-R*cos(alpha+beta) yMax= max(yB, yMax) ymin= min(yA, ymin) if ABeta >pi/2 and ABeta <=pi: xMax = max(R+xC,xMax) xB=R*sin(alpha+beta)+xC xmin = min(xA, xB, xmin) # yB = yC+R*cos(pi-(alpha+beta)) yB=yC-R*cos(alpha+beta) yMax = max(yB, yMax) ymin = min(yA, ymin) if ABeta >pi and ABeta <=3/2*pi: xB=R*sin(alpha+beta)+xC xMax=max(R+xC,xMax) xmin = min(xB,xmin) yB=yC-R*cos(alpha+beta) yMax = max(yC+R, yMax) ymin = min(yA, ymin) if ABeta >3/2*pi and ABeta <= 2*pi: xB=R*sin(alpha+beta)+xC xMax=max(R+xC,xMax) xmin = min(xC-R,xmin) yB=yC-R*cos(alpha+beta) yMax = max(yC+R, yMax) ymin = min(yA, yB, ymin) if ABeta >2*pi and ABeta <= 2*pi+beta: xmin = min(xC-R,xmin) xMax = max(R+xC,xMax) ymin = min(yC-R, ymin) yMax = max(yC+R, yMax) if (xA>xC) and (yA>=yC): beta=atan(abs(yA-yC)/abs(xA-xC)) j=2; ABeta=(alpha+beta) #say(str(degrees(beta))+" beta "+ str(degrees(ABeta))+" ABeta") yB=yC+R*sin(ABeta) xB=xC+R*cos(ABeta) if ABeta >= beta and ABeta <= pi/2: xMax=max(xA,xMax) xmin= min(xB,xmin) yMax= max(yB, yMax) ymin= min(yA, ymin) if ABeta > pi/2 and ABeta <= pi: xmin= min(xB,xmin) xMax=max(xA,xMax) ymin= min(yA, yB, ymin) yMax= max(yC+R, yMax) if ABeta > pi and ABeta <= 3/2*pi: xmin= min(xC-R,xmin) xMax=max(xA,xMax) ymin= min(yB, ymin) yMax= max(yC+R, yMax) if ABeta > 3/2*pi and ABeta <= 2*pi: xmin= min(xC-R,xmin) xMax= max(xA,xB,xMax) ymin= min(yC-R, ymin) yMax= max(yC+R, yMax) if ABeta > 2*pi and ABeta <= beta+2*pi: xmin= min(xC-R,xmin) xMax= max(xC+R,xMax) ymin= min(yC-R, ymin) yMax= max(yC+R, yMax) if (xA<=xC) and (yA>yC): beta=atan(abs(xA-xC)/abs(yA-yC)) j=3; ABeta=(alpha+beta) #say(str(degrees(beta))+" beta "+ str(degrees(ABeta))+" ABeta") yB=yC+R*cos(ABeta) xB=xC-R*sin(ABeta) if ABeta >= beta and ABeta <= pi/2: xMax= max(xA,xMax) xmin= min(xB,xmin) yMax= max(yA, yMax) ymin= min(yB, ymin) if ABeta > pi/2 and ABeta <= pi: xmin= min(xC-R,xmin) xMax= max(xA,xB,xMax) ymin= min(yB,ymin) yMax= max(yA,yMax) if ABeta > pi and ABeta <= 3/2*pi: xmin= min(xC-R,xmin) xMax= max(xB,xMax) ymin= min(yC-R, ymin) yMax= max(yA, yMax) if ABeta > 3/2*pi and ABeta <= 2*pi: xmin= min(xC-R,xmin) xMax= max(xC+R,xMax) ymin= min(yC-R, ymin) yMax= max(yA,yB, yMax) if ABeta > 2*pi and ABeta <= beta+2*pi: xmin= min(xC-R,xmin) xMax= max(xC+R,xMax) ymin= min(yC-R, ymin) yMax= max(yC+R, yMax) if (xA= beta and ABeta <= pi/2: xMax= max(xB,xMax) xmin= min(xA,xmin) yMax= max(yA, yMax) ymin= min(yB, ymin) if ABeta > pi/2 and ABeta <= pi: xmin= min(xA,xmin) xMax= max(xB,xMax) ymin= min(yC-R,ymin) yMax= max(yA,yB,yMax) if ABeta > pi and ABeta <= 3/2*pi: xmin= min(xA,xmin) xMax= max(xC+R,xMax) ymin= min(yC-R, ymin) yMax= max(yB, yMax) if ABeta > 3/2*pi and ABeta <= 2*pi: xmin= min(xA,xB,xmin) xMax= max(xC+R,xMax) ymin= min(yC-R,ymin) yMax= max(yC+R, yMax) if ABeta > 2*pi and ABeta <= beta+2*pi: xmin= min(xC-R,xmin) xMax= max(xC+R,xMax) ymin= min(yC-R, ymin) yMax= max(yC+R, yMax) #say(str(j)+" case j") #say('xC='+str(xC)+';yC='+str(yC)+';xA='+str(xA)+';yA='+str(yA)) #print x1,x2,y1,y2 #calculating xmin of arc R=sqrt((xA-xC)**2+(yA-yC)**2) #say('R = '+str(R)) #say(str(xMax)+" xMax") #say(str(xmin)+" xmin") # print xMax, xmin, yMax, ymin # print pcbarcs[n] #print (pcbarcs[n][8:].split(' ')[0]) return 0 ### end getArc_minMax def mid_point(prev_vertex,vertex,angle): """mid_point(prev_vertex,vertex,angle)-> mid_vertex returns mid point on arc of angle between prev_vertex and vertex""" angle=radians(angle/2) basic_angle=atan2(vertex.y-prev_vertex.y,vertex.x-prev_vertex.x)-pi/2 shift=(1-cos(angle))*hypot(vertex.y-prev_vertex.y,vertex.x-prev_vertex.x)/2/sin(angle) midpoint=Base.Vector((vertex.x+prev_vertex.x)/2+shift*cos(basic_angle),(vertex.y+prev_vertex.y)/2+shift*sin(basic_angle),0) return midpoint ### def Per_point(prev_vertex,vertex): """Per_point(center,vertex)->per point returns opposite perimeter point of circle""" #basic_angle=atan2(prev_vertex.y-vertex.y,prev_vertex.x-vertex.x) #shift=hypot(prev_vertex.y-vertex.y,prev_vertex.x-vertex.x) #perpoint=Base.Vector(prev_vertex.x+shift*cos(basic_angle),prev_vertex.y+shift*sin(basic_angle),0) perpoint=Base.Vector(2*prev_vertex.x-vertex.x,2*prev_vertex.y-vertex.y,0) return perpoint ### #os.system("ps -C 'kicad-SteUp-tool' -o pid=|xargs kill -9") # UI Class definitions ##if _platform == "linux" or _platform == "linux2": ## # linux ##elif _platform == "darwin": ## # MAC OS X ##elif _platform == "win32": ## # Windows ##################################### # Function infoDialog ##################################### def infoDialog(msg): #QtGui.qFreeCAD.setOverrideCursor(QtCore.Qt.WaitCursor) QtGui.qFreeCAD.restoreOverrideCursor() QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Information,u"Info Message",msg ) diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) diag.exec_() QtGui.qFreeCAD.restoreOverrideCursor() ## getAuxAxisOrigin def getAuxAxisOrigin(): match = re.search(r'\(aux_axis_origin (.+?) (.+?)\)', Kicad_Board) return [float(match.group(1)), float(match.group(2))]; ##################################### # Main Class old ##################################### # Class definitions # Function definitions def onLoadFootprint(file_name=None): #name=QtGui.QFileDialog.getOpenFileName(this,tr("Open Image"), "/home/jana", tr("Image Files (*.png *.jpg *.bmp)"))[0] #global module_3D_dir global last_fp_path, test_flag, pt_lnx global configParser, configFilePath, start_time global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer #self.setGeometry(25, 250, 500, 500) clear_console() default_value='/' module_3D_dir=os.getenv('KISYS3DMOD', default_value) module_3D_dir=module_3D_dir+'/../' ## getting 3D models path # say('KISYS3DMOD=') say('KISYS3DMOD='+os.getenv('KISYS3DMOD', default_value)+'\n'+'module_3D_dir='+module_3D_dir) if not os.path.isdir(module_3D_dir): module_3D_dir="/" if last_fp_path=='': last_fp_path=module_3D_dir if file_name is not None: #export_board_2step=True #for cmd line force exporting to STEP name=file_name elif test_flag==False: #if test_flag==False: Filter="" ##if _platform == "darwin": ## ##workaround for OSX not opening native fileopen ## name=QtGui.QFileDialog.getOpenFileName(self, 'Open file', ## last_file_path,"kicad module files (*.kicad_mod)", ## options=QtGui.QFileDialog.DontUseNativeDialog )[0] ##else: ## name=QtGui.QFileDialog.getOpenFileName(self, "Open File...", last_file_path, ## "kicad module files (*.kicad_mod)")[0] #path = FreeCAD.ConfigGet("AppHomePath") #path = FreeCAD.ConfigGet("UserAppData") #path=last_file_path #try: # name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open File", last_file_path, "*.kicad_mod")#PySide #except Exception: # FreeCAD.Console.PrintError("Error : " + str(name) + "\n") prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") #print('native_dlg',prefs_.GetBool('native_dlg')) if not(prefs_.GetBool('not_native_dlg')): name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open File...", make_unicode(last_fp_path), "*.kicad_mod") else: name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open File...", make_unicode(last_fp_path), "*.kicad_mod",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: name="C:/Cad/Progetti_K/ksu-test/test.kicad_mod" if len(name) > 0: #txtFile = __builtin__.open(name,"r") #with io.open(name,'r', encoding='utf-8') as txtFile: with codecs.open(name,'r', encoding='utf-8') as txtFile: #text = f.read() content = txtFile.readlines() # problems? ## txtFile = __builtin__.open(name,"rb") ## content = txtFile.readlines() content.append(u" ") last_fp_path=os.path.dirname(txtFile.name) txtFile.close() last_fp_path = re.sub("\\\\", "/", last_fp_path) #stop ini_vars[11] = last_fp_path ##with __builtin__.open(configFilePath, 'wb') as configfile: # configParser.write(configfile) #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_fp_path", make_string(last_fp_path)) data=u''.join(content) #for item in content: # data += item #print content; print data;stop if ignore_utf8: content=re.sub(r'[^\x00-\x7F]+',' ', data) #workaround to remove utf8 extra chars sayw('removing utf-8 chars') else: content=data #print content; stop #content=data #FreeCAD.Console.PrintMessage(content) #FreeCAD.Console.PrintMessage(data) FC_majorV=int(float(FreeCAD.Version()[0])) FC_minorV=int(float(FreeCAD.Version()[1])) say('FC Version '+str(FC_majorV)+str(FC_minorV)) if int(FC_majorV) <= 0 and int(FC_minorV) < 16: routineDrawFootPrint_old(content,name) else: if disable_VBO: paramGetV = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View") VBO_status=paramGetV.GetBool("UseVBO") #sayerr("checking VBO") say("VBO status "+str(VBO_status)) if VBO_status: paramGetV.SetBool("UseVBO",False) sayw("disabling VBO") if disable_PoM_Observer: #paramGetPoM = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/PartOMagic") #PoMObs_status=paramGetPoM.GetBool("EnableObserver") PoMObs_status = False if Observer.isRunning(): PoMObs_status=True #if PoMObs_status: Observer.stop() # paramGetPoM.SetBool("EnableObserver",False) sayw("disabling PoM Observer") routineDrawFootPrint(content,name) if (not pt_lnx): # and (not pt_osx): issue on AppImages hanging on loading FreeCADGui.SendMsgToActiveView("ViewFit") else: zf= Timer (0.3,ZoomFitThread) zf.start() #zf= Timer (0.3,ZoomFitThread) #zf.start() if disable_VBO: if VBO_status: paramGetV.SetBool("UseVBO",True) sayw("enabling VBO") if disable_PoM_Observer: if PoMObs_status: Observer.start() # paramGetPoM.SetBool("EnableObserver",True) sayw("enabling PoM Observer") #txtFile.close() ### def check_requirements(): # checking FC version requirement ###################################################################### #say("FC Version ") #say(FreeCAD.Version()) global start_time, fusion, FC_export_min_version, use_AppPart, force_oldGroups, use_Links, use_LinkGroups FC_majorV,FC_minorV,FC_git_Nbr=getFCversion() #FC_majorV=FreeCAD.Version()[0] #FC_minorV=FreeCAD.Version()[1] ##FC_majorV=int(FreeCAD.Version()[0]) ##FC_minorV=int(FreeCAD.Version()[1]) #try: # FC_git_Nbr=int(FreeCAD.Version()[2].strip(" (Git)")) #except: # FC_git_Nbr=0 #FC_git_Nbr=(FreeCAD.Version()[2].strip(" (Git)")) sayw('FC Version '+str(FC_majorV)+str(FC_minorV)+"-"+str(FC_git_Nbr)) msg1="use ONLY FreeCAD STABLE version 0.15 or later\r\n" #msg1+="to generate your STEP and VRML models\r\nFC 016 dev version results are still unpredictable" msg1+="to generate your STEP and VRML models\r\n" if int(FC_majorV) <= 0: if int(FC_minorV) < 15: QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Warning! ...",msg1) msg='' if FC_majorV == 0 and FC_minorV == 17: if FC_git_Nbr >= int(FC_export_min_version): use_AppPart=True if FC_majorV > 0: use_AppPart=True if FC_majorV == 0 and FC_minorV > 17: if FC_git_Nbr >= int(FC_export_min_version): use_AppPart=True if use_AppPart and not force_oldGroups: sayw("creating hierarchy") if (fusion==True): msg+="you have chosen: fuse modules to board\r\nbe careful ... fusion can be heavy or generate FC crash" msg+="when fusing a lot of objects\r\nplease consider to use bbox or blacklist small objects\r\n\r\n" ##start_time=current_milli_time() ### def sanitizeSketch(s_name): ''' simplifying & sanitizing sketches ''' global edge_tolerance, addConstraints s=FreeCAD.ActiveDocument.getObject(s_name) sayw('check to sanitize') # print(s.TypeId) if 'Sketcher' in s.TypeId: idx_to_del=[] #print(s.Geometry) for i,g in enumerate (s.Geometry): #print(g,i) # g.length() #print('str(g)',str(g)) if 'Line' in str(g): #print(g.length()) lng = distance(g.StartPoint, g.EndPoint) #print(lng) if lng <= edge_tolerance: print(g,i) sayw('too short') idx_to_del.append(i) if 'Circle' in str(g): if g.Radius <= edge_tolerance: print(g,i) sayw('too short') idx_to_del.append(i) if 'Arc' in str(g): #print('str(g)',str(g)) #stop lng = distance(g.StartPoint, g.EndPoint) #print(lng) if lng <= edge_tolerance: print(g,i) sayw('too short') idx_to_del.append(i) j=0 if len(idx_to_del) >0 and addConstraints != 'none': sayw(u'sanitizing '+s.Label) for i in idx_to_del: s.delGeometry(i-j) j+=1 # if len(idx_to_del) >0 and addConstraints!='none': tol1 = edge_tolerance #0.01 constr1='coincident' import constrainator constrainator.add_constraints(s.Name, tol1, constr1) elif len(idx_to_del) >0 and addConstraints == 'none': sayerr(u'sanitizing skipped because of constraints=\'None\' in kSU settings') ## def add_constraints(s_name): """ adding coincident points constraints """ global addConstraints, edge_tolerance s=FreeCAD.ActiveDocument.getObject(s_name) if hasattr(Part,"LineSegment"): g_geom_points = { Base.Vector: [1], Part.LineSegment: [1, 2], # first point, last point Part.Circle: [0, 3], # curve, center Part.ArcOfCircle: [1, 2, 3], # first point, last point, center Part.BSplineCurve: [0,1,2,3], # for poles Part.ArcOfEllipse: [0,1,2,3], # Part.Ellipse: [0,1], # Part.ArcOfHyperbola: [0,1,2], # Part.Point: [0], # } else: g_geom_points = { Base.Vector: [1], Part.Line: [1, 2], # first point, last point Part.Circle: [0, 3], # curve, center Part.ArcOfCircle: [1, 2, 3], # first point, last point, center Part.BSplineCurve: [0,1,2,3], # for poles Part.ArcOfEllipse: [0,1,2,3], # Part.Ellipse: [0,1], # Part.ArcOfHyperbola: [0,1,2], # Part.Point: [0], # } points=[] geoms=[] #print len((s.Geometry)) #stop for geom_index in range(len((s.Geometry))): point_indexes = g_geom_points[type(s.Geometry[geom_index])] #sayerr(point_indexes), say (geom_index) #if 'Line' in type(PCB_Sketch.Geometry[geom_index]).__name__: if 'ArcOfCircle' in type(s.Geometry[geom_index]).__name__\ or 'Line' in type(s.Geometry[geom_index]).__name__: point1 = s.getPoint(geom_index, point_indexes[0]) #sayerr(str(point1[0])+';'+str(point1[1])) point2 = s.getPoint(geom_index, point_indexes[1]) #sayw(str(point2[0])+';'+str(point1[1])) #points.append([[point1[0],point1[1]],[geom_index],[1]]) #points.append([[point2[0],point2[1]],[geom_index],[2]]) #points.append([[point1[0],point1[1]],[geom_index]]) #,[1]]) #points.append([[point2[0],point2[1]],[geom_index]]) #,[2]]) if 'Line' in type(s.Geometry[geom_index]).__name__: tp = 'Line' else: tp = 'Arc' geoms.append([point1[0],point1[1],point2[0],point2[1],tp]) elif 'ArcOfEllipse' in type(s.Geometry[geom_index]).__name__: point1 = s.getPoint(geom_index, point_indexes[1]) point2 = s.getPoint(geom_index, point_indexes[2]) tp = 'Arc' geoms.append([point1[0],point1[1],point2[0],point2[1],tp]) #print points def simu_distance(p0, p1): return max (abs(p0[0] - p1[0]), abs(p0[1] - p1[1])) # #print geom sk_constraints = [] cnt=1 #print (addConstraints, ' constraints') #stop if addConstraints=='all': for i, geo in enumerate(geoms): #for i in range(len(geom)): p_g0_0=[geo[0],geo[1]] p_g0_1=[geo[2],geo[3]] #print p_g0_0,pg_g0_1 if abs(p_g0_0[0]-p_g0_1[0])< edge_tolerance and geo[4] == 'Line': #s.addConstraint(Sketcher.Constraint('Vertical',i)) sk_constraints.append(Sketcher.Constraint('Vertical',i)) elif abs(p_g0_0[1]-p_g0_1[1])< edge_tolerance and geo[4] == 'Line': #s.addConstraint(Sketcher.Constraint('Horizontal',i)) sk_constraints.append(Sketcher.Constraint('Horizontal',i)) j=i+1 for geo2 in geoms[(i + 1):]: p_g1_0=[geo2[0],geo2[1]] p_g1_1=[geo2[2],geo2[3]] #rint p_g0_0, p_g0_1 #rint p_g1_0, p_g1_1 if distance(p_g0_0,p_g1_0)< edge_tolerance: ##App.ActiveDocument.PCB_Sketch.addConstraint(Sketcher.Constraint('Coincident',0,2,3,1)) #s.addConstraint(Sketcher.Constraint('Coincident',i,1,j,1)) sk_constraints.append(Sketcher.Constraint('Coincident',i,1,j,1)) #print i,1,i+1,1 elif distance(p_g0_0,p_g1_1)< edge_tolerance: #s.addConstraint(Sketcher.Constraint('Coincident',i,1,j,2)) sk_constraints.append(Sketcher.Constraint('Coincident',i,1,j,2)) #print i,1,i+1,2 elif distance(p_g0_1,p_g1_0)< edge_tolerance: #s.addConstraint(Sketcher.Constraint('Coincident',i,2,j,1)) sk_constraints.append(Sketcher.Constraint('Coincident',i,2,j,1)) #print i,2,i+1,1 elif distance(p_g0_1,p_g1_1)< edge_tolerance: #s.addConstraint(Sketcher.Constraint('Coincident',i,2,j,2)) sk_constraints.append(Sketcher.Constraint('Coincident',i,2,j,2)) #print i,2,i+1,2 j=j+1 cnt=cnt+1 elif addConstraints=='coincident' or addConstraints=='full': if hasattr (FreeCAD.ActiveDocument.getObject(s_name), "autoconstraint"): sayw('using constrainator') sanitizeSketch(s_name) sk1=FreeCAD.ActiveDocument.getObject(s_name) sk1.detectMissingPointOnPointConstraints(edge_tolerance) sk1.makeMissingPointOnPointCoincident() FreeCAD.activeDocument().recompute() sk1.autoRemoveRedundants(True) sk1.solve() FreeCAD.activeDocument().recompute() else: sayw('using old constrainator') for i, geo in enumerate(geoms): #for i in range(len(geom)): #print (geo) #stop p_g0_0=[geo[0],geo[1]] p_g0_1=[geo[2],geo[3]] #print p_g0_0,pg_g0_1 #if addConstraints=='all': # if abs(p_g0_0[0]-p_g0_1[0])< edge_tolerance: # s.addConstraint(Sketcher.Constraint('Vertical',i)) # elif abs(p_g0_0[1]-p_g0_1[1])< edge_tolerance: # s.addConstraint(Sketcher.Constraint('Horizontal',i)) j=i+1 for geo2 in geoms[(i + 1):]: p_g1_0=[geo2[0],geo2[1]] p_g1_1=[geo2[2],geo2[3]] #rint p_g0_0, p_g0_1 #rint p_g1_0, p_g1_1 if distance(p_g0_0,p_g1_0)< edge_tolerance: ##App.ActiveDocument.PCB_Sketch.addConstraint(Sketcher.Constraint('Coincident',0,2,3,1)) #s.addConstraint(Sketcher.Constraint('Coincident',i,1,j,1)) sk_constraints.append(Sketcher.Constraint('Coincident',i,1,j,1)) #print i,1,i+1,1 elif distance(p_g0_0,p_g1_1)< edge_tolerance: #s.addConstraint(Sketcher.Constraint('Coincident',i,1,j,2)) sk_constraints.append(Sketcher.Constraint('Coincident',i,1,j,2)) #print i,1,i+1,2 elif distance(p_g0_1,p_g1_0)< edge_tolerance: #s.addConstraint(Sketcher.Constraint('Coincident',i,2,j,1)) sk_constraints.append(Sketcher.Constraint('Coincident',i,2,j,1)) #print i,2,i+1,1 elif distance(p_g0_1,p_g1_1)< edge_tolerance: #s.addConstraint(Sketcher.Constraint('Coincident',i,2,j,2)) sk_constraints.append(Sketcher.Constraint('Coincident',i,2,j,2)) #print i,2,i+1,2 j=j+1 cnt=cnt+1 if len(sk_constraints) > 0: s.addConstraint(sk_constraints) #print 'counter ',cnt #print geo2 ### def add_missing_geo(s_name): """ adding missing geo on near but non coincident points""" s=FreeCAD.ActiveDocument.getObject(s_name) if hasattr(Part,"LineSegment"): g_geom_points = { Base.Vector: [1], Part.LineSegment: [1, 2], # first point, last point Part.Circle: [0, 3], # curve, center Part.ArcOfCircle: [1, 2, 3], # first point, last point, center Part.BSplineCurve: [0,1,2,3], # for poles Part.ArcOfEllipse: [0,1,2,3], # Part.Ellipse: [0,1], # Part.ArcOfHyperbola: [0,1,2], # Part.Point: [0], # } else: g_geom_points = { Base.Vector: [1], Part.Line: [1, 2], # first point, last point Part.Circle: [0, 3], # curve, center Part.ArcOfCircle: [1, 2, 3], # first point, last point, center Part.BSplineCurve: [0,1,2,3], # for poles Part.ArcOfEllipse: [0,1,2,3], # Part.Ellipse: [0,1], # Part.ArcOfHyperbola: [0,1,2], # Part.Point: [0], # } geo_points=[] geoms=[] #print len((s.Geometry)) #stop for geom_index in range(len((s.Geometry))): point_indexes = g_geom_points[type(s.Geometry[geom_index])] #sayerr(point_indexes), say (geom_index) #if 'Line' in type(PCB_Sketch.Geometry[geom_index]).__name__: if 'ArcOfCircle' in type(s.Geometry[geom_index]).__name__\ or 'Line' in type(s.Geometry[geom_index]).__name__: point1 = s.getPoint(geom_index, point_indexes[0]) #sayerr(str(point1[0])+';'+str(point1[1])) point2 = s.getPoint(geom_index, point_indexes[1]) #sayw(str(point2[0])+';'+str(point1[1])) #points.append([[point1[0],point1[1]],[geom_index],[1]]) #points.append([[point2[0],point2[1]],[geom_index],[2]]) #points.append([[point1[0],point1[1]],[geom_index]]) #,[1]]) #points.append([[point2[0],point2[1]],[geom_index]]) #,[2]]) #points.append([[point1[0],point1[1]],[geom_index]]) #,[1]]) #geo_points.append([[point1[0],point1[1]],[point2[0],point2[1]],[geom_index]]) #,[2]]) geoms.append([point1[0],point1[1],point2[0],point2[1]]) # elif 'ArcOfEllipse' in type(s.Geometry[geom_index]).__name__\ # or 'ArcOfHyperbola' in type(s.Geometry[geom_index]).__name__: # point1 = s.getPoint(geom_index, point_indexes[1]) # point2 = s.getPoint(geom_index, point_indexes[2]) # geoms.append([point1[0],point1[1],point2[0],point2[1],tp]) # elif 'Ellipse' in type(s.Geometry[geom_index]).__name__: # pass # elif 'Point' in type(s.Geometry[geom_index]).__name__: # pass sk_add_geo = [] #say(geoms) for i, geo in enumerate(geoms): p_g0_0=[geo[0],geo[1]] p_g0_1=[geo[2],geo[3]] j=i+1 for geo2 in geoms[(i + 1):]: p_g2_0_0=[geo2[0],geo2[1]] p_g2_0_1=[geo2[2],geo2[3]] d = distance(p_g0_0,p_g2_0_0) if d < edge_tolerance and d > 0: sk_add_geo.append(PLine(Base.Vector(p_g0_0[0],p_g0_0[1],0), Base.Vector(p_g2_0_0[0],p_g2_0_0[1],0))) #print i,1,i+1,1 d = distance(p_g0_1,p_g2_0_0) if d < edge_tolerance and d>0: #s.addConstraint(Sketcher.Constraint('Coincident',i,1,j,2)) sk_add_geo.append(PLine(Base.Vector(p_g0_1[0],p_g0_1[1],0), Base.Vector(p_g2_0_0[0],p_g2_0_0[1],0))) #print i,1,i+1,2 d = distance(p_g0_0,p_g2_0_1) if d < edge_tolerance and d>0: #s.addConstraint(Sketcher.Constraint('Coincident',i,2,j,1)) sk_add_geo.append(PLine(Base.Vector(p_g0_0[0],p_g0_0[1],0), Base.Vector(p_g2_0_1[0],p_g2_0_1[1],0))) #print i,2,i+1,1 d = distance(p_g0_1,p_g2_0_1) if d < edge_tolerance and d >0: #s.addConstraint(Sketcher.Constraint('Coincident',i,2,j,2)) sk_add_geo.append(PLine(Base.Vector(p_g0_1[0],p_g0_1[1],0), Base.Vector(p_g2_0_1[0],p_g2_0_1[1],0))) #print i,2,i+1,2 j=j+1 sayerr('added Geometry') sayerr(sk_add_geo) if len(sk_add_geo) > 0: s.addGeometry(sk_add_geo) ### def cpy_sketch(sname,nname=None): """ copy Sketch NB Geometry sequence is not conserved!!! """ s=FreeCAD.ActiveDocument.getObject(sname) #geoL=len(App.ActiveDocument.getObject(sname).Geometry) if nname is None: nname="Temp_Sketch" tsk= FreeCAD.activeDocument().addObject('Sketcher::SketchObject',nname) tsk.addGeometry(FreeCAD.ActiveDocument.getObject(sname).Geometry) tsk.addConstraint(FreeCAD.ActiveDocument.getObject(sname).Constraints) tsk.Placement=FreeCAD.ActiveDocument.getObject(sname).Placement #print tsk.Geometry FreeCAD.ActiveDocument.recompute() #stop return FreeCAD.ActiveDocument.ActiveObject.Name ## def shift_sketch(sname, ofs, nname): """ shift Sketch Geometry (Geom sequence is not conserved!!!) """ s1n=cpy_sketch(sname,nname) FreeCAD.ActiveDocument.recompute() s1=FreeCAD.ActiveDocument.getObject(s1n) lg=len (s1.Geometry) #print lg geo=[] for k in range(lg): geo.append(str(FreeCAD.ActiveDocument.getObject(s1.Name).Geometry[k])) #geo=FreeCAD.ActiveDocument.getObject(s1.Name).Geometry for k in range(lg): FreeCAD.ActiveDocument.getObject(s1.Name).addCopy([k],FreeCAD.Vector(ofs[0],-ofs[1],0),False) #print FreeCAD.ActiveDocument.getObject(s1.Name).Geometry[k] #FreeCAD.ActiveDocument.getObject(s1.Name).delGeometry(k) FreeCAD.ActiveDocument.recompute() #print len (s1.Geometry) #stop #print (s1.Geometry) nlg=len (s1.Geometry) idx_to_del=[] idx_to_del_str=[] #print geo for k in range(nlg): if str(FreeCAD.ActiveDocument.getObject(s1.Name).Geometry[k]) in geo: idx_to_del.append(k) #for j in range (len(idx_to_del)): # idx_to_del_str.append(str(idx_to_del[j])) #print idx_to_del #stop #print idx_to_del_str for i in range (nlg-1,-1,-1): # #FreeCAD.ActiveDocument.getObject(s_name).delGeometry(k) #print i if i in idx_to_del: FreeCAD.ActiveDocument.getObject(s1.Name).delGeometry(i) FreeCAD.ActiveDocument.recompute() #FreeCAD.ActiveDocument.getObject(s1.Name).Placement = FreeCAD.Placement(FreeCAD.Vector(2*ofs[0],2*ofs[1],0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) #geoL=len(App.ActiveDocument.getObject(sname).Geometry) #Temp_Sketch_Sft= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Temp_Sketch_Sft') #geo=[] #for k in range(len (s.Geometry)): # App.ActiveDocument.Temp_Sketch_Sft.addCopy([k],App.Vector(100,100,0),False) # # #if 'LineSegment' in type(s.Geometry[k]).__name__: # ##if 'Line' in type(s.Geometry[k]).__name__: # # #ls=''.format(s.Geometry[k].StartPoint.x+ofs[0],s.Geometry[k].StartPoint.y+ofs[1], s.Geometry[k].EndPoint.x+ofs[0],s.Geometry[k].EndPoint.y+ofs[1]) # # #geo.append(ls);geo.append(le) # # #Temp_Sketch_Sft.addGeometry(FreeCAD.ActiveDocument.getObject(sname).Geometry[k]) # # #Temp_Sketch_Sft.addGeometry(geo) # # spx=s.Geometry[k].StartPoint.x+ofs[0] # # spy=s.Geometry[k].StartPoint.y+ofs[1] # # epx=s.Geometry[k].EndPoint.x+ofs[0] # # epy=s.Geometry[k].EndPoint.y+ofs[1] # # # # FreeCAD.ActiveDocument.Temp_Sketch_Sft.addGeometry(PLine(Base.Vector(spx,spy,0), Base.Vector(epx,epy,0))) # #Temp_Sketch_Sft.Placement.Base[0]=FreeCAD.ActiveDocument.getObject(sname).Placement.Base[0]+ofs[0] #Temp_Sketch_Sft.Placement.Base[1]=FreeCAD.ActiveDocument.getObject(sname).Placement.Base[1]+ofs[1] #print Temp_Sketch.Geometry #FreeCAD.ActiveDocument.recompute() return FreeCAD.ActiveDocument.ActiveObject.Name ## def PullPCB(file_name=None): onLoadBoard(None,False) #layer_list = ['Edge.Cuts','Dwgs.User','Eco1.User','Eco2.User'] #LayerSelectionDlg = QtGui.QDialog() #ui = Ui_LayerSelection() #ui.setupUi(LayerSelectionDlg) #ui.comboBoxLayerSel.addItems(layer_list) #ui.label.setText("Select the layer to pull into the Sketch\nDefault \'Edge.Cuts\'") #reply=LayerSelectionDlg.exec_() #if reply==1: # ok # SketchLayer=str(ui.comboBoxLayerSel.currentText()) # print(SketchLayer) #else: # print('Cancel') ## def crc_gen(data): import binascii import re #data=u'WĂĽrfel' content=re.sub(r'[^\x00-\x7F]+','_', data) #make_unicode(hex(binascii.crc_hqx(content.encode('utf-8'), 0x0000))[2:]) #hex(binascii.crc_hqx(content.encode('utf-8'), 0x0000))[2:].encode('utf-8') #print(data +u'_'+ hex(binascii.crc_hqx(content.encode('utf-8'), 0x0000))[2:]) return u'_'+ make_unicode(hex(binascii.crc_hqx(content.encode('utf-8'), 0x0000))[2:]) ## def onLoadBoard(file_name=None,load_models=None,insert=None): #name=QtGui.QFileDialog.getOpenFileName(this,tr("Open Image"), "/home/jana", tr("Image Files (*.png *.jpg *.bmp)"))[0] #global module_3D_dir global test_flag, last_pcb_path, configParser, configFilePath, start_time global aux_orig, base_orig, base_point, idf_to_origin, off_x, off_y, export_board_2step global real_board_pos_x, real_board_pos_y, board_base_point_x, board_base_point_y global models3D_prefix, models3D_prefix2, models3D_prefix3, models3D_prefix4 global blacklisted_model_elements, col, colr, colg, colb, whitelisted_3Dmodels global bbox, volume_minimum, height_minimum, idf_to_origin, aux_orig global base_orig, base_point, bbox_all, bbox_list, whitelisted_model_elements global fusion, addVirtual, blacklisted_models, exportFusing, min_drill_size global last_fp_path, last_pcb_path, plcmnt, xp, yp, exportFusing global ignore_utf8, ignore_utf8_incfg, pcb_path, disable_VBO, use_AppPart, force_oldGroups, use_Links, use_LinkGroups global original_filename, edge_width, load_sketch, grid_orig, warning_nbr, running_time, addConstraints global conv_offs, zfit, fname_sfx, missingHeight import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser objs_toberemoved = [] ImportMode_status=0 import_drawings = False pull_sketch = False override_pcb = None keep_pcb_sketch = None SketchLayer = 'Edge.Cuts' #None if load_models is None: load_models = True if load_models == False: # layer_list = ['Edge.Cuts','Dwgs.User','Cmts.User','Eco1.User','Eco2.User','Margin'] # layer_list = ['Edge.Cuts','Dwgs.User','Cmts.User','Eco1.User','Eco2.User','Margin', 'F.FillZone', 'F.KeepOutZone', 'F.MaskZone','B.FillZone', 'B.KeepOutZone', 'B.MaskZone',] layer_list = ['Edge.Cuts','Dwgs.User','Cmts.User','Eco1.User','Eco2.User','User.1','User.2','User.3','User.4','User.5','User.6','User.7','User.8','User.9','Margin', 'F.FillZone', 'F.KeepOutZone', 'F.MaskZone','B.FillZone', 'B.KeepOutZone', 'B.MaskZone',] LayerSelectionDlg = QtGui.QDialog() ui = Ui_LayerSelection() ui.setupUi(LayerSelectionDlg) ui.comboBoxLayerSel.addItems(layer_list) if 0: ui.comboBoxLayerSel.setEditable(True) ui.label.setText("Select the layer to pull into the Sketch\nDefault: \'Edge.Cuts\'") reply=LayerSelectionDlg.exec_() if reply==1: # ok SketchLayer=str(ui.comboBoxLayerSel.currentText()) print(SketchLayer) if SketchLayer == 'Edge.Cuts': #override_pcb = ui.checkBox_replace.isChecked() if ui.radioBtn_replace_pcb.isChecked(): override_pcb = True elif ui.radioBtn_keep_sketch.isChecked(): #enabling keep sketch only if override is True override_pcb = True keep_pcb_sketch = True else: if ui.radioBtn_replace_pcb.isChecked(): import_drawings = True pull_sketch = True else: print('Cancel') if pull_sketch or load_models: default_value='/' clear_console() #lastPcb_dir='C:/Cad/Progetti_K/ksu-test' #say(lastPcb_dir+' last Pcb dir') #print(make_string(last_pcb_path)) #print (make_unicode(last_pcb_path)) if not os.path.isdir(make_unicode(last_pcb_path)): last_pcb_path=u"./" #say(last_pcb_path) if file_name is not None: #export_board_2step=True #for cmd line force exporting to STEP name=file_name elif test_flag==False: Filter="" #minimize main window #self.setWindowState(QtCore.Qt.WindowMinimized) #infoDialog('ciao') #reply = QtGui.QInputDialog.getText(None, "Hello","Enter your thoughts for the day:") #if reply[1]: # # user clicked OK # replyText = reply[0] #else: # # user clicked Cancel # replyText = reply[0] # which will be "" if they clicked Cancel #restore main window #self.setWindowState(QtCore.Qt.WindowActive) prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open kicad PCB File...", make_unicode(last_pcb_path), "*.kicad_pcb") else: name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Open kicad PCB File...", make_unicode(last_pcb_path), "*.kicad_pcb",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: name="C:/Cad/Progetti_K/ksu-test/multidrill.kicad_pcb" if len(name) > 0: if os.path.isfile(name): original_filename=name say('opening '+name) path, fname = os.path.split(name) fname=os.path.splitext(fname)[0] fname_sfx=crc_gen(make_unicode(fname)) top_name='Top'+fname_sfx bot_name='Bot'+fname_sfx topV_name='TopV'+fname_sfx botV_name='BotV'+fname_sfx stepM_name='Step_Models'+fname_sfx stepV_name='Step_Virtual_Models'+fname_sfx pcb_name='Pcb'+fname_sfx sketch_name_sfx = 'PCB_Sketch'+fname_sfx board_name='Board'+fname_sfx boardG_name='Board_Geoms'+fname_sfx LCS_name = 'Local_CS'+fname_sfx #say(fname_sfx) #fpth = os.path.dirname(os.path.abspath(__file__)) fpth = os.path.dirname(os.path.abspath(name)) #filePath = os.path.split(os.path.realpath(__file__))[0] say ('my file path '+fpth) if fpth == "": fpth = u"." last_pcb_path = fpth #last_pcb_path=path pcb_path=fpth # update existing value #say(default_ksu_msg) #stop last_pcb_path = re.sub("\\\\", "/", last_pcb_path) # configParser.write(configfile) ##stop utf-8 test ini_vars[10] = last_pcb_path #cfg_update_all() test_import = False if override_pcb == True: insert=True if insert == True: test_import = True if test_import: doc=FreeCAD.ActiveDocument if doc is None: doc=FreeCAD.newDocument(fname) override_pcb = False try: doc.removeObject(LCS_name) except: pass elif override_pcb == True: if doc.getObject(boardG_name) in doc.Objects: #if 1: #try: if keep_pcb_sketch==True: #doc.getObject(boardG_name).removeObject(doc.getObject(sketch_name_sfx)) #keep sketck & constrains doc.getObject(boardG_name).ViewObject.dragObject(doc.getObject(sketch_name_sfx)) #objs_toberemoved.append([doc.getObject(sketch_name_sfx)]) removesubtree([doc.getObject(boardG_name)]) #objs_toberemoved.append([doc.getObject(boardG_name)]) #doc.recompute() try: doc.removeObject(LCS_name) except: pass sayw('old Pcb removed') #stop else: #except: override_pcb = False say('Pcb not present') else: if import_drawings and FreeCAD.ActiveDocument is not None: doc=FreeCAD.ActiveDocument else: doc=FreeCAD.newDocument(fname) # doc.commitTransaction() doc.openTransaction('opening_kicad') say('opening Transaction \'opening_kicad\'') pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) # py3 .decode("utf-8") #pg.SetString("last_pcb_path", last_pcb_path.decode("utf-8")) modules=[] start_time=current_milli_time() #filename="C:/Cad/Progetti_K/D-can-term/can-term-test-fcad.kicad_pcb" #filename="c:\\Temp\\backpanel3.kicad_pcb" mypcb = KicadPCB.load(name) #test parser off_x=0; off_y=0 #offset of the board & modules grid_orig_warn=False aux_orig_warn=False if (grid_orig==1): #xp=getAuxAxisOrigin()[0]; yp=-getAuxAxisOrigin()[1] #offset of the board & modules if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'grid_origin'): #say('aux_axis_origin' + str(mypcb.setup.aux_axis_origin)) xp=-mypcb.setup.grid_origin[0]; yp=mypcb.setup.grid_origin[1] sayw('grid origin found @ ('+str(xp)+', '+str(yp)+')') else: say('grid origin not set\nusing default top left corner') xp=0;yp=0 grid_orig_warn=True else: say('grid origin not set\nusing default top left corner') xp=0;yp=0 grid_orig_warn=True ##off_x=-xp+xmin+(xMax-xmin)/2; off_y=-yp-(ymin+(yMax-ymin)/2) #offset of the board & modules #off_x=-xp+center_x;off_y=-yp+center_y off_x=-xp;off_y=-yp if (aux_orig==1): #xp=getAuxAxisOrigin()[0]; yp=-getAuxAxisOrigin()[1] #offset of the board & modules if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'aux_axis_origin'): #say('aux_axis_origin' + str(mypcb.setup.aux_axis_origin)) sayw('aux origin found @: '+str(mypcb.setup.aux_axis_origin)) xp=-mypcb.setup.aux_axis_origin[0]; yp=mypcb.setup.aux_axis_origin[1] else: aux_orig_warn=True say('aux origin not set') xp=-148.5;yp=98.5 else: aux_orig_warn=True say('aux origin not set') xp=-148.5;yp=98.5 ##off_x=-xp+xmin+(xMax-xmin)/2; off_y=-yp-(ymin+(yMax-ymin)/2) #offset of the board & modules #off_x=-xp+center_x;off_y=-yp+center_y off_x=-xp;off_y=-yp #if (aux_orig==1): # #xp=getAuxAxisOrigin()[0]; yp=-getAuxAxisOrigin()[1] #offset of the board & modules # if hasattr(mypcb.setup, 'aux_axis_origin'): # #say('aux_axis_origin' + str(mypcb.setup.aux_axis_origin)) # xp=mypcb.setup.aux_axis_origin[0]; yp=-mypcb.setup.aux_axis_origin[1] # else: # say('aux origin not used') # ##off_x=-xp+xmin+(xMax-xmin)/2; off_y=-yp-(ymin+(yMax-ymin)/2) #offset of the board & modules # off_x=-xp+center_x;off_y=-yp+center_y # #off_x=-xp;off_y=-yp modules,nsk = DrawPCB(mypcb,SketchLayer,override_pcb,keep_pcb_sketch) if override_pcb == True: if use_AppPart and not force_oldGroups and not use_LinkGroups: doc.getObject(board_name).addObject(doc.getObject(boardG_name)) elif use_LinkGroups: doc.getObject(board_name).ViewObject.dropObject(doc.getObject(boardG_name),doc.getObject(boardG_name),'',[]) if SketchLayer == 'Edge.Cuts': FreeCAD.ActiveDocument.getObject(board_name).Label = fname if hasattr(mypcb, 'general'): pcbThickness=float(mypcb.general.thickness) else: pcbThickness=1.6 ## stop #test parser check_requirements() #stop #pcbThickness,modules,board_elab,mod_lines,mod_arcs,mod_circles=LoadKicadBoard(name) #say(modules) #routineDrawPCB(pcbThickness,board_elab,mod_lines,mod_arcs,mod_circles) doc.commitTransaction() say('closing Transaction \'opening_kicad\'') else: say(name+' missing\r') stop ##Placing board at configured position # pos objs x,-y # pos board xm+(xM-xm)/2 # pos board -(ym+(yM-ym)/2) if SketchLayer == 'Edge.Cuts': #center_x, center_y, bb_x, bb_y = findPcbCenter("Pcb") center_x, center_y, bb_x, bb_y = findPcbCenter(u"Pcb"+fname_sfx) else: draw=FreeCAD.ActiveDocument.PCB_Sketch_draft center_x, center_y, bb_x, bb_y = findPcbCenter(draw.Name) ## using PcbCenter xMax=center_x+bb_x/2 xmin=center_x-bb_x/2 yMax=center_y+bb_y/2 ymin=center_y-bb_y/2 #off_x=0; off_y=0 #offset of the board & modules if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'edge_width'): #maui edge width edge_width=mypcb.setup.edge_width elif hasattr(mypcb.setup, 'edge_cuts_line_width'): #maui edge cuts new width k 5.99 edge_width=mypcb.setup.edge_cuts_line_width #if (grid_orig==1): # #xp=getAuxAxisOrigin()[0]; yp=-getAuxAxisOrigin()[1] #offset of the board & modules # if hasattr(mypcb.setup, 'grid_origin'): # #say('aux_axis_origin' + str(mypcb.setup.aux_axis_origin)) # xp=-mypcb.setup.grid_origin[0]; yp=mypcb.setup.grid_origin[1] # else: # say('grid origin not found\nplacing at center of an A4') # xp=-148.5;yp=98.5 # ##off_x=-xp+xmin+(xMax-xmin)/2; off_y=-yp-(ymin+(yMax-ymin)/2) #offset of the board & modules # #off_x=-xp+center_x;off_y=-yp+center_y # off_x=-xp;off_y=-yp if (base_orig==1): ##off_x=xmin+(xMax-xmin)/2; off_y=-(ymin+(yMax-ymin)/2) #offset of the board & modules off_x=center_x;off_y=center_y #sayw(base_point);sayw(" base point") if (base_point==1): ##off_x=-xp+xmin+(xMax-xmin)/2; off_y=-yp-(ymin+(yMax-ymin)/2) #offset of the board & modules #off_x=-xp+center_x;off_y=-yp+center_y off_x=-xp+center_x;off_y=-yp+center_y #sayw(off_x) ## test maui board_base_point_x=(xMax-xmin)/2-off_x ## test maui board_base_point_y=-((yMax-ymin)/2)-off_y #real_board_pos_x=xmin+(xMax-xmin)/2 #real_board_pos_y=-(ymin+(yMax-ymin)/2) ## using PcbCenter real_board_pos_x=center_x real_board_pos_y=center_y # doc = FreeCAD.ActiveDocument if idf_to_origin == True: board_base_point_x=-off_x board_base_point_y=-off_y else: ## using PcbCenter say ('using PcbCenter') #board_base_point_x=xmin+(xMax-xmin)/2-off_x #board_base_point_y=-(ymin+(yMax-ymin)/2)-off_y board_base_point_x=center_x-off_x board_base_point_y=center_y-off_y sayw('placing board @ '+str(board_base_point_x)+','+str(board_base_point_y)) if SketchLayer == 'Edge.Cuts': #FreeCAD.ActiveDocument.getObject("Pcb").Placement = FreeCAD.Placement(FreeCAD.Vector(board_base_point_x,board_base_point_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCAD.ActiveDocument.getObject(pcb_name).Placement = FreeCAD.Placement(FreeCAD.Vector(board_base_point_x,board_base_point_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) #else: # draw.Placement = FreeCAD.Placement(FreeCAD.Vector(board_base_point_x,board_base_point_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) newname="PCB_Sketch"+fname_sfx if load_sketch: if SketchLayer != 'Edge.Cuts' and SketchLayer is not None: newname = SketchLayer.split('.')[0]+'_Sketch' say_inline('building up pcb time') get_time() say(str(running_time)) t1=(running_time) #add_constraints("PCB_Sketch_draft") #FreeCAD.ActiveDocument.recompute() if aux_orig==1 or grid_orig ==1: s_name=cpy_sketch("PCB_Sketch_draft",newname) FreeCAD.ActiveDocument.recompute() #add_constraints(s_name) #say_time() #stop elif (base_point==1): s_name=shift_sketch("PCB_Sketch_draft", [-center_x,center_y],newname) #stop #add_constraints(s_name) FreeCAD.ActiveDocument.getObject(s_name).Placement = FreeCAD.Placement(FreeCAD.Vector(xp,yp,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) #say_time() elif (base_orig==1): s_name=shift_sketch("PCB_Sketch_draft", [-center_x,center_y],newname) #stop #add_constraints(s_name) FreeCAD.ActiveDocument.getObject(s_name).Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) #say_time() else: s_name=shift_sketch("PCB_Sketch_draft", [-center_x,center_y],newname) #stop #add_constraints(s_name) #sayerr('usebasepoint') #sayerr('usedefault') FreeCAD.ActiveDocument.getObject(s_name).Placement = FreeCAD.Placement(FreeCAD.Vector(center_x,center_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) #stop #say_time() # FreeCAD.ActiveDocument.removeObject("PCB_Sketch_draft") objs_toberemoved.append([FreeCAD.ActiveDocument.getObject("PCB_Sketch_draft")]) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") if 0: # test_face # addConstraints!='none': say('start adding constraints to pcb sketch') add_constraints(s_name) get_time() #say('adding constraints time ' +str(running_time-t1)) say('adding constraints time ' + "{0:.3f}".format(running_time-t1)) ##FreeCAD.ActiveDocument.recompute() pcb_sk=FreeCAD.ActiveDocument.getObject(newname) gi = 0 for g in pcb_sk.Geometry: if 'BSplineCurve object' in str(g): # say(str(g)) FreeCAD.ActiveDocument.getObject(newname).exposeInternalGeometry(gi) gi+=1 if use_LinkGroups and SketchLayer == 'Edge.Cuts': FreeCAD.ActiveDocument.getObject(boardG_name).ViewObject.dropObject(FreeCAD.ActiveDocument.getObject(newname),FreeCAD.ActiveDocument.getObject(newname),'',[]) FreeCADGui.Selection.clearSelection() sl = FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(newname)) #FreeCADGui.runCommand('Std_HideSelection',0) FreeCADGui.runCommand('Std_ToggleVisibility',0) FreeCADGui.Selection.clearSelection() #FreeCADGui.ActiveDocument.PCB_Sketch.Visibility = False #FreeCAD.ActiveDocument.getObject('PCB_Sketch').adjustRelativeLinks(FreeCAD.ActiveDocument.getObject('Board_Geoms')) elif SketchLayer == 'Edge.Cuts': FreeCAD.ActiveDocument.getObject(boardG_name).addObject(pcb_sk) #updating pcb_sketch if SketchLayer != 'Edge.Cuts' and SketchLayer is not None: pcb_sk.Label = SketchLayer if nsk > 1: pcb_sk.Label+="s" pcb_sk.ViewObject.Visibility=False #FreeCAD.ActiveDocument.getObject("PCB_Sketch").Placement = FreeCAD.Placement(FreeCAD.Vector(board_base_point_x,board_base_point_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) #FreeCAD.ActiveDocument.getObject("PCB_SketchN").Placement = FreeCAD.Placement(FreeCAD.Vector(board_base_point_x,board_base_point_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) ## FreeCAD.ActiveDocument.getObject("Pcb").Placement = FreeCAD.Placement(FreeCAD.Vector(-off_x,-off_y,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") #ImportGui.insert(u"./c0603.step","demo_5D_vrml_from_step") if (not pt_lnx): # and (not pt_osx): issue on AppImages hanging on loading FreeCADGui.SendMsgToActiveView("ViewFit") else: zf= Timer (0.1,ZoomFitThread) zf.start() if keep_pcb_sketch == True: #sayw(sketch_name_sfx+'001') if doc.getObject(sketch_name_sfx+'001') in doc.Objects: #if 1: #try: doc.removeObject(sketch_name_sfx+'001') #keep sketck & constrains #objs_toberemoved.append([doc.getObject(sketch_name_sfx+'001')]) docG = FreeCADGui.ActiveDocument docG.getObject(sketch_name_sfx).Visibility=True elif override_pcb == True: if doc.getObject(sketch_name_sfx) in doc.Objects: #if 1: #try: docG = FreeCADGui.ActiveDocument docG.getObject(sketch_name_sfx).Visibility=True if not pull_sketch or load_models: if use_AppPart and not force_oldGroups and not use_LinkGroups: #sayw("creating hierarchy") ## to evaluate to add App::Part hierarchy doc.Tip = doc.addObject('App::Part',stepM_name) stepM = doc.ActiveObject stepM.Label = stepM_name doc.Tip = doc.addObject('App::Part',top_name) topG = doc.ActiveObject topG.Label = top_name doc.Tip = doc.addObject('App::Part',bot_name) botG = doc.ActiveObject botG.Label = bot_name doc.getObject(stepM_name).addObject(doc.getObject(top_name)) doc.getObject(stepM_name).addObject(doc.getObject(bot_name)) try: doc.Step_Models.License = '' doc.Step_Models.LicenseURL = '' except: pass #FreeCADGui.activeView().setActiveObject('Step_Models', doc.Step_Models) doc.getObject(board_name).addObject(doc.getObject(stepM_name)) doc.Tip = doc.addObject('App::Part',stepV_name) stepV = doc.ActiveObject stepV.Label = stepV_name doc.Tip = doc.addObject('App::Part',topV_name) topV = doc.ActiveObject topV.Label = topV_name doc.Tip = doc.addObject('App::Part',botV_name) botV = doc.ActiveObject botV.Label = botV_name doc.getObject(stepV_name).addObject(doc.getObject(topV_name)) doc.getObject(stepV_name).addObject(doc.getObject(botV_name)) try: stepV.License = '' stepV.LicenseURL = '' except: pass FreeCADGui.activeView().setActiveObject(stepV_name, stepV) doc.getObject(board_name).addObject(doc.getObject(stepV_name)) doc.getObject(board_name).Label=fname try: doc.getObject(board_name).License='' doc.getObject(board_name).LicenseURL='' except: pass ## end hierarchy elif use_LinkGroups: doc.Tip = doc.addObject('App::LinkGroup',stepM_name) stepM=doc.ActiveObject stepM.Label = stepM_name doc.Tip = doc.addObject('App::LinkGroup',stepV_name) stepV=doc.ActiveObject stepV.Label = stepV_name doc.addObject('App::LinkGroup',top_name) topG=doc.ActiveObject topG.Label = top_name doc.addObject('App::LinkGroup',bot_name) botG=doc.ActiveObject botG.Label = bot_name doc.addObject('App::LinkGroup',topV_name) topVG=doc.ActiveObject topVG.Label = topV_name doc.addObject('App::LinkGroup',botV_name) botVG=doc.ActiveObject botVG.Label = botV_name #doc.getObject('Top').adjustRelativeLinks(doc.getObject('Step_Models')) doc.getObject(stepM_name).ViewObject.dropObject(doc.getObject(top_name),doc.getObject(top_name),'',[]) #doc.getObject('TopV').adjustRelativeLinks(doc.getObject('Step_Virtual_Models')) doc.getObject(stepV_name).ViewObject.dropObject(doc.getObject(topV_name),doc.getObject(topV_name),'',[]) #doc.getObject('Bot').adjustRelativeLinks(doc.getObject('Step_Models')) doc.getObject(stepM_name).ViewObject.dropObject(doc.getObject(bot_name),doc.getObject(bot_name),'',[]) #doc.getObject('BotV').adjustRelativeLinks(doc.getObject('Step_Virtual_Models')) doc.getObject(stepV_name).ViewObject.dropObject(doc.getObject(botV_name),doc.getObject(botV_name),'',[]) #doc.getObject('Step_Models').adjustRelativeLinks(doc.getObject('Board')) doc.getObject(board_name).ViewObject.dropObject(doc.getObject(stepM_name),doc.getObject(stepM_name),'',[]) #doc.getObject('Step_Virtual_Models').adjustRelativeLinks(doc.getObject('Board')) doc.getObject(board_name).ViewObject.dropObject(doc.getObject(stepV_name),doc.getObject(stepV_name),'',[]) FreeCADGui.Selection.clearSelection() else: #sayerr("creating flat groups") doc.addObject("App::DocumentObjectGroup", stepM_name) doc.addObject("App::DocumentObjectGroup", stepV_name) doc.recompute() say_time() if disable_VBO: paramGetV = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View") VBO_status=paramGetV.GetBool("UseVBO") #sayerr("checking VBO") say("VBO status "+str(VBO_status)) if VBO_status: paramGetV.SetBool("UseVBO",False) sayw("disabling VBO") #stop #stop if disable_PoM_Observer: #paramGetPoM = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/PartOMagic") #PoMObs_status=paramGetPoM.GetBool("EnableObserver") PoMObs_status = False if Observer.isRunning(): PoMObs_status=True #if PoMObs_status: Observer.stop() # paramGetPoM.SetBool("EnableObserver",False) sayw("disabling PoM Observer") prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import") ImportMode_status = 0 if hasattr(prefs, 'GetInts'): if len(prefs.GetInts()) > 0: if prefs.GetInt('ImportMode') != 0: ImportMode_status = prefs.GetInt('ImportMode') prefs.SetInt('ImportMode', 0) sayerr('STEP ImportMode NOT as \'Single document\''+'\n') ##ReadShapeCompoundMode paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") ReadShapeCompoundMode_status=paramGetVS.GetBool("ReadShapeCompoundMode") #sayerr("checking ReadShapeCompoundMode") sayw("ReadShapeCompoundMode status "+str(ReadShapeCompoundMode_status)) #FreeCAD.Console.PrintLog("ReadShapeCompoundMode status "+str(ReadShapeCompoundMode_status)+"\n") #stop enable_ReadShapeCompoundMode=False if ReadShapeCompoundMode_status and allow_compound=='True' \ or ReadShapeCompoundMode_status and allow_compound=='Hierarchy': paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") paramGetVS.SetBool("ReadShapeCompoundMode",False) sayw("disabling ReadShapeCompoundMode") enable_ReadShapeCompoundMode=True elif not ReadShapeCompoundMode_status and allow_compound=='Simplified': paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") paramGetVS.SetBool("ReadShapeCompoundMode",True) sayw("enabling ReadShapeCompoundMode -> Simplified Mode") enable_ReadShapeCompoundMode=True #paramGetVS.SetBool("ReadShapeCompoundMode",False) if load_sketch: FreeCADGui.ActiveDocument.getObject(newname).Visibility=False # hidden Sketch ##Load 3D models #Load_models(pcbThickness,modules) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") #else: Load_models(pcbThickness,modules) #enable_ReadShapeCompoundMode=False if enable_ReadShapeCompoundMode: paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") paramGetVS.SetBool("ReadShapeCompoundMode",ReadShapeCompoundMode_status) sayw("enabling ReadShapeCompoundMode") if disable_VBO: if VBO_status: paramGetV.SetBool("UseVBO",True) sayw("enabling VBO") if disable_PoM_Observer: if PoMObs_status: Observer.start() # paramGetPoM.SetBool("EnableObserver",True) sayw("enabling PoM Observer") def find_nth(haystack, needle, n): start = haystack.find(needle) while start >= 0 and n > 1: start = haystack.find(needle, start+len(needle)) n -= 1 return start msg="" n_rpt=0 for mod3d in modules: #say(mod3d) #for e in mod3d: # print e #.decode("utf-8") #if mod3d[5] is not None: if mod3d[5] != "": say(mod3d[0]);sayw(" error: reset"+mod3d[5]) #stop #msg+=""+mod3d[0].decode("utf-8")+" error: "+mod3d[5]+"
" msg+=""+mod3d[0]+"
error: "+mod3d[5]+"
" n_rpt=n_rpt+1 n_rpt_max=10 zf= Timer (0.3,ZoomFitThread) zf.start() if (show_messages==True) and msg!="": msg="""error in model(s)
"""+msg QtGui.QApplication.restoreOverrideCursor() #print n_rpt,'-',p_rpt if n_rpt > n_rpt_max: p_rpt=find_nth(msg, '
', n_rpt_max) #print n_rpt,'-',p_rpt reply = QtGui.QMessageBox.information(None,"Info ...",msg[:p_rpt]+'
. . .') else: reply = QtGui.QMessageBox.information(None,"Info ...",msg) #if 'LinkView' in dir(FreeCADGui): # FreeCADGui.Selection.clearSelection() # o=FreeCAD.ActiveDocument.getObject('Board') # #FreeCADGui.Selection.addSelection('Board') # FreeCADGui.Selection.addSelection(doc.Name,o.Name) # #import expTree; #import importlib;importlib.reload(expTree) # #print('collapsing selection') # #expTree.collS_Tree() #toggle_Tree() # clps = Timer (3,collaps_Tree) # clps.start() if export_board_2step: #say('aliveTrue') Export2MCAD(blacklisted_model_elements) else: #say('aliveFalse') Display_info(blacklisted_model_elements) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") msg="running time: "+str(round(running_time,3))+"sec" say(msg) zf= Timer (0.3,ZoomFitThread) zf.start() zf.cancel() if SketchLayer != 'Edge.Cuts' and SketchLayer is not None: FreeCADGui.ActiveDocument.ActiveView.viewTop() if grid_orig_warn: #adding a warning message because GridOrigin is set in FC Preferences but not set in KiCAD pcbnew file msg = 'GridOrigin is set in FC Preferences but not set in KiCAD pcbnew file' sayw(msg) QtGui.QApplication.restoreOverrideCursor() msg="""GridOrigin is set in FreeCAD Preferences
but not set in KiCAD pcbnew file
""" msg+="""

Please assign Grid Origin to your KiCAD pcbnew board file""" msg+="""
for a better Mechanical integration""" reply = QtGui.QMessageBox.information(None,"Warning ...",msg) elif aux_orig_warn: #adding a warning message because AuxOrigin is set in FC Preferences but not set in KiCAD pcbnew file msg = 'AuxOrigin is set in FC Preferences but not set in KiCAD pcbnew file' sayw(msg) QtGui.QApplication.restoreOverrideCursor() msg="""AuxOrigin is set in FreeCAD Preferences
but not set in KiCAD pcbnew file
""" msg+="""

Please assign Aux Origin to your KiCAD pcbnew board file""" msg+="""
for a better Mechanical integration""" reply = QtGui.QMessageBox.information(None,"Warning ...",msg) prefsKSU = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import") paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") ReadShapeCompoundMode_status=paramGetVS.GetBool("ReadShapeCompoundMode") if ImportMode_status != 0: prefs.SetInt('ImportMode',ImportMode_status) FCV_date = '' STEP_UseAppPart_available = False if len (FreeCAD.Version()) >= 5: FCV_date = str(FreeCAD.Version()[-3]) FCV_date = FCV_date[0:FCV_date.find(' ')] say('FreeCAD version: '+FreeCAD.Version()[0]+'.'+FreeCAD.Version()[1]) say('FreeCAD build date: '+FCV_date) if FCV_date >= '2020/06/27': STEP_UseAppPart_available = True #new STEP import export mode available say('STEP UseAppPart available') if hasattr(prefs, 'GetBools'): if (('UseAppPart' in prefs.GetBools() or 'UseLinkGroup' in prefs.GetBools()) and STEP_UseAppPart_available) or len (prefs.GetBools()) == 0: if (not prefs.GetBool('UseAppPart') and not ('UseLinkGroup' in prefs.GetBools())) or prefs.GetBool('UseLegacyImporter') or not prefs.GetBool('UseBaseName')\ or prefs.GetBool('ExportLegacy') or ReadShapeCompoundMode_status or prefs.GetBool('UseLinkGroup'): # or ImportMode_status != 0: msg = "Please set your preferences for STEP Import Export as in the displayed image\n" msg += "(you can disable this warning on StepUp preferences)\n" if 'help_warning_enabled' in prefsKSU.GetBools(): if prefsKSU.GetBool('help_warning_enabled'): StepPrefsDlg = QtGui.QDialog() ui = Ui_STEP_Preferences() ui.setupUi(StepPrefsDlg) reply=StepPrefsDlg.exec_() sayw(msg) #QtGui.QApplication.restoreOverrideCursor() #reply = QtGui.QMessageBox.information(None,"Info ...",msg) else: #first time new settings parameter StepPrefsDlg = QtGui.QDialog() ui = Ui_STEP_Preferences() ui.setupUi(StepPrefsDlg) reply=StepPrefsDlg.exec_() sayw(msg) # TB reviewed #if 'LinkView' in dir(FreeCADGui): # FreeCADGui.Selection.clearSelection() # o=FreeCAD.ActiveDocument.getObject('Board') # #FreeCADGui.Selection.addSelection('Board') # FreeCADGui.Selection.addSelection(doc.Name,o.Name) # #import expTree; #import importlib;importlib.reload(expTree) # #print('collapsing selection') # #expTree.collS_Tree() #toggle_Tree() # clps = Timer (3,collaps_Tree) # FreeCADGui.Selection.clearSelection() # o=FreeCAD.ActiveDocument.getObject('Board') # #FreeCADGui.Selection.addSelection('Board') # FreeCADGui.Selection.addSelection(doc.Name,o.Name) # #collaps_Tree() # clps.start() #say_time() #stop def removing_kobjs(): ''' removing objects after delay ''' from kicadStepUptools import removesubtree doc=FreeCAD.ActiveDocument if doc is not None: doc.openTransaction('rmv_objs_kicad') for tbr in objs_toberemoved: removesubtree(tbr) doc.commitTransaction() # doc.undo() # doc.undo() # adding a timer to allow double transactions during the python code QtCore.QTimer.singleShot(0.2,removing_kobjs) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") #ImportGui.insert(u"./c0603.step","demo_5D_vrml_from_step") if (not pt_lnx): # and (not pt_osx): issue on AppImages hanging on loading FreeCADGui.SendMsgToActiveView("ViewFit") else: zf= Timer (0.25,ZoomFitThread) zf.start() ### def routineR_XYZ(axe,alpha): global resetP say('routine Rotate XYZ') if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument #FreeCAD.Console.PrintMessage("hereXYZ !") selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: #if len(sel[0]) == 1: doc.openTransaction('rot') check_AP() selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] s = objs[0].Shape shape=s.copy() shape.Placement=s.Placement; boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) #say("bbx: "+str(boundBoxLX)+" bby: "+str(boundBoxLY)+"bbz: "+str(boundBoxLZ)) #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) #shape.rotate((oripl_X,oripl_Y,oripl_Z),(1,0,0),90) angle=alpha if axe=='x': #shape.rotate((0,0,0),(1,0,0),90) shape.rotate((oripl_X+boundBoxLX/2,oripl_Y+boundBoxLY/2,oripl_Z+boundBoxLZ/2),(1,0,0),int(angle)) if axe=='y': #shape.rotate((0,0,0),(0,1,0),90) shape.rotate((oripl_X+boundBoxLX/2,oripl_Y+boundBoxLY/2,oripl_Z+boundBoxLZ/2),(0,1,0),int(angle)) if axe=='z': #shape.rotate((0,0,0),(0,0,1),90) shape.rotate((oripl_X+boundBoxLX/2,oripl_Y+boundBoxLY/2,oripl_Z+boundBoxLZ/2),(0,0,1),int(angle)) #Part.show(shape) objs[0].Placement=shape.Placement FreeCADGui.Selection.addSelection(objs[0]) FreeCAD.activeDocument().recompute() if resetP==True: #doc.commitTransaction() routineResetPlacement() doc.commitTransaction() #say("end of rotineZ!") else: say(translate("Say", "Select ONE single part object !")) say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") ### end RotateXYZ def routineT_XYZ(axe,v): global resetP say('routine Translate XYZ') if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: doc.openTransaction('trs') check_AP() selEx = FreeCADGui.Selection.getSelectionEx() if 0: vis_objs=[] for selobj in selEx: if selobj.Object.ViewObject.isVisible==True: vis_obj.append(selobj.Object) objs = [o for o in vis_objs] objs = [selobj.Object for selobj in selEx] s = objs[0].Shape shape=s.copy() shape.Placement=s.Placement; #shape.rotate((oripl_X,oripl_Y,oripl_Z),(1,0,0),90) #say("axe "+axe+", value "+v) if axe=='x': shape.translate((float(v),0,0)) if axe=='y': shape.translate((0,float(v),0)) if axe=='z': shape.translate((0,0,float(v))) #Part.show(shape) objs[0].Placement=shape.Placement FreeCADGui.Selection.addSelection(objs[0]) FreeCAD.activeDocument().recompute() if resetP==True: routineResetPlacement() doc.commitTransaction() #say("end of rotineT!") else: say(translate("Say", "Select ONE single part object !")) say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") ### end TranslateXYZ def routineResetPlacement(keepWB=None): objs=[] if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): if keepWB is None: FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] #print 'here' if len(objs) == 1: # doc.openTransaction('rst') #say('routine reset Placement properties') # print(objs[0].Label) CpyName = ''; RefName = '' if objs[0] != 'App::Part': # using std method # if objs[0].TypeId == 'Part::MultiFuse' or objs[0].TypeId == 'Part::Compound' or \ # objs[0].TypeId == 'Part::Cut' or objs[0].TypeId == 'Part::MultiCommon': # workaround for issue in resetting pacement for STEP 'merge' importing if 1: say('routine reset Placement std') s=objs[0].Shape r=[] t=s.copy() for i in t.childShapes(): c=i.copy() c.Placement=t.Placement.multiply(c.Placement) r.append((i,c)) w=t.replaceShape(r) w.Placement=FreeCAD.Placement() Part.show(w) CpyName = FreeCAD.ActiveDocument.ActiveObject.Name #say(w) ## removed the need to workaround, with FC fix else: # workaround for issue in resetting pacement for STEP 'merge' importing say('routine reset Placement refining') FreeCAD.ActiveDocument.addObject('Part::Refine','Refined').Source=FreeCAD.ActiveDocument.getObject(objs[0].Name) RefName = FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.recompute() __shape = Part.getShape(FreeCAD.ActiveDocument.getObject(RefName),'',needSubElement=False,refine=False) FreeCAD.ActiveDocument.addObject('Part::Feature','sc').Shape=__shape #FreeCAD.ActiveDocument.recompute() CpyName = FreeCAD.ActiveDocument.ActiveObject.Name # FreeCAD.ActiveDocument.ActiveObject.Label=FreeCAD.ActiveDocument.getObject('Part__Feature001').Label if hasattr(FreeCADGui.ActiveDocument.getObject(objs[0].Name),'ShapeColor'): say('has shapecolor') FreeCADGui.ActiveDocument.getObject(CpyName).ShapeColor=FreeCADGui.ActiveDocument.getObject(objs[0].Name).ShapeColor FreeCADGui.ActiveDocument.getObject(CpyName).LineColor=FreeCADGui.ActiveDocument.getObject(objs[0].Name).LineColor FreeCADGui.ActiveDocument.getObject(CpyName).PointColor=FreeCADGui.ActiveDocument.getObject(objs[0].Name).PointColor FreeCADGui.ActiveDocument.getObject(CpyName).DiffuseColor=FreeCADGui.ActiveDocument.getObject(objs[0].Name).DiffuseColor FreeCADGui.ActiveDocument.ActiveObject.Transparency=FreeCADGui.ActiveDocument.getObject(objs[0].Name).Transparency new_label=objs[0].Label if RefName != '': FreeCAD.ActiveDocument.removeObject(RefName) if objs[0].TypeId == 'PartDesign::Body' or 'PartDesign::' in objs[0].TypeId or len(objs[0].OutList)>0: FreeCAD.ActiveDocument.getObject(objs[0].Name).ViewObject.Visibility=False else: FreeCAD.ActiveDocument.removeObject(objs[0].Name) FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.ActiveObject.Label=new_label rObj=FreeCAD.ActiveDocument.getObject(CpyName) del objs FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(CpyName)) #doc.commitTransaction() #FreeCAD.activeDocument().recompute() #say("end of rotineRP!") else: say(translate("Say", "Select ONE single part object !")) say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") del objs ### end reset prop def routineScaleVRML(): global exportV, exportS, applymaterials if FreeCAD.ActiveDocument.FileName == "": msg = translate("Save", "please save your job file before exporting.") QtGui.QApplication.restoreOverrideCursor() QtGui.QMessageBox.information(None,translate("Save", "Info ..."),msg) FreeCADGui.SendMsgToActiveView(translate("Save", "Save")) say(translate("Say", 'routine Scale to VRML 1/2.54')) cfg_read_all() doc = FreeCAD.ActiveDocument doc.openTransaction('exportModel') selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) >= 1: #allow more than 1 obj for vrml say('exporting') fullFilePathName=doc.FileName if fullFilePathName=="": if os.path.exists(models3D_prefix): if isWritable(models3D_prefix): #if os.access(models3D_prefix, os.W_OK | os.X_OK): models3D_prefix_tosave = re.sub("\\\\", "/", models3D_prefix) if models3D_prefix_tosave.endswith('/'): fullFilePathName=models3D_prefix+doc.Label+'.FCStd' else: fullFilePathName=models3D_prefix+os.sep+doc.Label+'.FCStd' say('saving to '+models3D_prefix_tosave) else: home = expanduser("~") fullFilePathName=home+os.sep+doc.Label+'.FCStd' say('path not found/writable, saving to '+home) #say(fullFilePathName) else: home = expanduser("~") fullFilePathName=home+os.sep+doc.Label+'.FCStd' say('path not found/writable, saving to '+home) #say(fullFilePathName) else: say(fullFilePathName) lbl=go_export(fullFilePathName) path, fname = os.path.split(fullFilePathName) #fname=os.path.splitext(fname)[0] #fname=objs[0].Label ##fname=FreeCAD.ActiveDocument.ActiveObject.Label #step reset placement fname=lbl #step reset placement #removing not allowed chars translation_table = dict.fromkeys(map(ord, '<>:"/\|?*,;:\\'), None) fname=fname.translate(translation_table) vrml_ext='.wrl' prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if prefs.GetBool('wrz_export_enabled'): #'stpz' vrml_ext='.wrz' stp_ext='.step' if prefs.GetBool('stpz_export_enabled'): stp_ext='.stpZ' #print('stpZ',fullFilePathNameStep) if exportV or exportS: msg="""export STEP & scaled VRML file for kicad! ****************************************************************************
exporting folder:
- """+path+"""""" msg+="""
exporting filename:
""" if exportV: msg+="""- """+fname+vrml_ext+"""
""" if exportS: msg+="""
- """+fname+stp_ext+"""""" else: if len(objs) >= 1: msg+="""
- step file not exported; multi-part selected""" #msg="export scaled VRML file for kicad!\r\n" #msg=msg+"****************************************************************************" msg=msg+"

3D settings in kicad Module Editor:
" msg=msg+"- scale 1 1 1\r\n- offset 0 0 0
- rotation 0 0 "+str(rot_wrl)+"
" ##self.setWindowState(QtCore.Qt.WindowMinimized) QtGui.QApplication.restoreOverrideCursor() QtGui.QMessageBox.information(None,"Info ...",msg) ##self.setWindowState(QtCore.Qt.WindowActive) say('done') FreeCAD.ActiveDocument.commitTransaction() else: say(translate("Say", "Select ONE single part object !")) say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") return 0 ### ### def routineScaleVRML_1(): global rot_wrl, zfit say('routine Scale to VRML 1/2.54') if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: objS=FreeCAD.ActiveDocument.getObject(objs[0].Name).Shape #FreeCADGui.ActiveDocument.getObject(objs[0].Name).BoundingBox = True final_Label=FreeCAD.ActiveDocument.getObject(objs[0].Name).Label myobjG=FreeCADGui.ActiveDocument.getObject(objs[0].Name) myobjA=FreeCAD.ActiveDocument.getObject(objs[0].Name) mynewdoc=FreeCAD.newDocument() FreeCAD.ActiveDocument.addObject('Part::Feature',objs[0].Name).Shape=objS #print 'here' myobjA1=FreeCAD.ActiveDocument.ActiveObject #myobjA1.Label=final_Label myobjG1=FreeCADGui.ActiveDocument.ActiveObject myobjG1.ShapeColor=myobjG.ShapeColor myobjG1.LineColor=myobjG.LineColor myobjG1.PointColor=myobjG.PointColor myobjG1.DiffuseColor=myobjG.DiffuseColor myobjG1.Transparency=myobjG.Transparency FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.ActiveObject.Label=final_Label+'_vrml' say( final_Label+'_vrml' ) #FreeCADGui.ActiveDocument.getObject(objs[0].Name).Visibility=False FreeCAD.ActiveDocument.recompute() vrml_obj = Draft.scale(FreeCAD.ActiveDocument.ActiveObject,delta=FreeCAD.Vector(0.3937,0.3937,0.3937),center=FreeCAD.Vector(0,0,0),legacy=True) FreeCAD.ActiveDocument.recompute() #FreeCAD.ActiveDocument.ActiveObject.ViewObject.DisplayMode = 'Shaded' FreeCADGui.ActiveDocument.ActiveObject.BoundingBox = False #FreeCAD.ActiveDocument.ActiveObject.ViewObject.DisplayMode = 'Shaded' #vrml_obj.ViewObject.DisplayMode = u'Shaded' shade_val='Shaded' #FreeCAD.ActiveDocument.ActiveObject.ViewObject.DisplayMode = 'Shaded' FreeCAD.ActiveDocument.ActiveObject.ViewObject.DisplayMode = 1 #Shaded if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") msg="""export scaled VRML file for kicad! ****************************************************************************
3D settings in kicad Module Editor:
- scale 1 1 1\r\n- offset 0 0 0\r\n- rotation 0 0 {0} """.format(rot_wrl) #msg="export scaled VRML file for kicad!\r\n" #msg=msg+"****************************************************************************" #msg=msg+"\r\n3D settings in kicad Module Editor:\r\n" #msg=msg+"- scale 1 1 1\r\n- offset 0 0 0\r\n- rotation 0 0 "+str(rot_wrl) self.setWindowState(QtCore.Qt.WindowMinimized) QtGui.QApplication.restoreOverrideCursor() QtGui.QMessageBox.information(None,"Info ...",msg) self.setWindowState(QtCore.Qt.WindowActive) else: say(translate("Say", "Select ONE single part object !")) say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") return 0 ### end ScaleVRML_1 def routineC_XYZ(axe): global resetP say('routine center position') #if self.checkBox_1.isChecked(): # routineResetPlacement() if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument say("Centering on Axe XYZ !") selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: doc.openTransaction('cntr') check_AP() selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] s = objs[0].Shape shape=s.copy() #shape.Placement=s.Placement; shape.Placement= FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength say("bbox: "+str(boundBox_)) a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) #say("bbx: "+str(boundBoxLX)+" bby: "+str(boundBoxLY)+"bbz: "+str(boundBoxLZ)) #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) p=s.Placement #say("PlacementBase : "+str(p)) #say(str(p.Base[0])+' '+str(p.Base[1])+' '+str(p.Base[2])) if axe=='x': #shape.translate((0,0,0)) diffPl=-oripl_X-boundBoxLX/2 #shape.Placement.move(diffPl,0,0) #shape.translate(Base.Vector(diffPl,0,0)) shape.translate((diffPl,p.Base[1],p.Base[2])) if axe=='y': diffPl=-oripl_Y-boundBoxLY/2 #shape.translate(Base.Vector(0,diffPl,0)) shape.translate((p.Base[0],diffPl,p.Base[2])) if axe=='z': diffPl=-oripl_Z-boundBoxLZ/2 shape.translate((p.Base[0],p.Base[1],diffPl)) #shape.translate(Base.Vector(0,0,diffPl)) ### to zero posX -bboxX/2 #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) #say("axe "+axe+" placement"+str(diffPl)) #Part.show(shape) objs[0].Placement=shape.Placement FreeCADGui.Selection.addSelection(objs[0]) FreeCAD.activeDocument().recompute() #say("x: "+str(oripl_X)+"\r\ny: "+str(oripl_Y)+"\r\nz: "+str(oripl_Z)) if resetP==True: routineResetPlacement() #say("pos reset done") doc.commitTransaction() #say("done") else: say("Select an object !") say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select an object !") ### end routineC_XYZ def routineP_XYZ(axe): global resetP say('routine put on axe') #routineResetPlacement() if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument say(translate("Say", "Put on Axe XYZ !")) selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: doc.openTransaction('plc') check_AP() selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] s = objs[0].Shape shape=s.copy() #shape.Placement=s.Placement; shape.Placement= FreeCAD.Placement(FreeCAD.Vector(0,0,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) p=s.Placement say("PlacementBase : "+str(p)) #say(str(p.Base[0])+' '+str(p.Base[1])+' '+str(p.Base[2])) if axe=='x': #shape.translate((0,0,0)) diffPl=p.Base[0]-oripl_X #shape.Placement.move(diffPl,0,0) #shape.translate(Base.Vector(diffPl,0,0)) shape.translate((diffPl,p.Base[1],p.Base[2])) if axe=='y': diffPl=p.Base[1]-oripl_Y #shape.translate(Base.Vector(0,diffPl,0)) shape.translate((p.Base[0],diffPl,p.Base[2])) if axe=='z': diffPl=p.Base[2]-oripl_Z shape.translate((p.Base[0],p.Base[1],diffPl)) #shape.translate(Base.Vector(0,0,diffPl)) ### to zero posX -bboxX/2 #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) #say("axe "+axe+" placement"+str(diffPl)) #Part.show(shape) objs[0].Placement=shape.Placement FreeCADGui.Selection.addSelection(objs[0]) FreeCAD.activeDocument().recompute() #say("x: "+str(oripl_X)+"\r\ny: "+str(oripl_Y)+"\r\nz: "+str(oripl_Z)) #say("placement "+str(p[0])) #return [oripl_X, oripl_Y, oripl_Z,p.Base[0],p.Base[1],p.Base[2]]; if resetP==True: routineResetPlacement() doc.commitTransaction() else: say(translate("Say", "Select ONE single part object !")) say_single_obj() #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n") ### end routineP_XYZ def say_single_obj(): QtGui.QApplication.restoreOverrideCursor() msg = translate("Say", "Select ONE single part object !
\n" "suggestion for multi-part:
\n" "  Part Boolean Union (recommended)
\n" "or
\n" "  Part Make compound (alternative choice)" ) spc="""*******************************************************************************
""" msg1=translate("Say", "Error in selection") QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, msg1, msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() def say_select_obj(): QtGui.QApplication.restoreOverrideCursor() msg = translate("Say", "Select a Compound or
\n" "a Part Design group
\n" "or more than one Part object !
" ) spc="""*******************************************************************************
""" msg1=translate("Say", "Error in selection") QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, msg1, msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() def say_warning(msg): QtGui.QApplication.restoreOverrideCursor() # msg="""Select a Compound or
a Part Design group
or more than one Part object !
""" spc="""*******************************************************************************
""" msg1 = translate("Say", "Warning ...") QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning, msg1, msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() def say_error(msg): QtGui.QApplication.restoreOverrideCursor() # msg="""Select a Compound or
a Part Design group
or more than one Part object !
""" spc="""*******************************************************************************
""" msg1 = translate("Say", "ERROR! ...") QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, msg1, msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() def say_info(msg): QtGui.QApplication.restoreOverrideCursor() # msg="""Select a Compound or
a Part Design group
or more than one Part object !
""" spc="""*******************************************************************************
""" msg1 = translate("Say","Info ...") QtGui.QApplication.restoreOverrideCursor() #RotateXYZGuiClass().setGeometry(25, 250, 500, 500) diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information, msg1, msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() def get_position(): global min_val, exportS say('routine get base position') if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument #say("hereXYZ !") selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: try: s = objs[0].Shape boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) FreeCADGui.Selection.addSelection(objs[0]) FreeCAD.activeDocument().recompute() #say("x: "+str(oripl_X)+"\r\ny: "+str(oripl_Y)+"\r\nz: "+str(oripl_Z)) p=s.Placement #say("PlacementBase : "+str(p)) #say(str(p.Base[0])+' '+str(p.Base[1])+' '+str(p.Base[2])) ### to zero posX -bboxX/2 #say("placement "+str(p[0])) #min_val=10e-16 #say("min_val "+str(min_val)) if abs(oripl_X) < min_val: oripl_X=0 if abs(oripl_Y) < min_val: oripl_Y=0 if abs(oripl_Z) < min_val: oripl_Z=0 return [oripl_X, oripl_Y, oripl_Z,p.Base[0],p.Base[1],p.Base[2]]; except: pass else: if exportS: say(translate("Say", "Select ONE single part object !")) ##QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") #QtGui.QApplication.restoreOverrideCursor() #msg="""Select ONE single part object !
#suggestion for multi-part:
  Part Boolean Union (recommended)
or
  Part Make compound (alternative choice)
""" #spc="""*******************************************************************************
#""" #msg1="Error in selection" #QtGui.QApplication.restoreOverrideCursor() ##RotateXYZGuiClass().setGeometry(25, 250, 500, 500) #diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, # msg1, # msg) #diag.setWindowModality(QtCore.Qt.ApplicationModal) #diag.exec_() ### end get position def routineM_XYZ(axe,v): global resetP mydoc=FreeCAD.ActiveDocument say('routine Move to point XYZ') if 0: if "Assembly2Workbench" not in FreeCADGui.activeWorkbench().name(): if "PartWorkbench" not in FreeCADGui.activeWorkbench().name(): FreeCADGui.activateWorkbench("PartWorkbench") #FreeCADGui.SendMsgToActiveView("ViewFit") ##FreeCADGui.activeDocument().activeView().viewTop() doc = FreeCAD.ActiveDocument selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: doc.openTransaction('move') check_AP() selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] s = objs[0].Shape boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength boundBoxLY = boundBox_.YLength boundBoxLZ = boundBox_.ZLength a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0]) oripl_Y = float(c[1]) oripl_Z = float(c[2]) shape=s.copy() shape.Placement=s.Placement;p=s.Placement #shape.rotate((oripl_X,oripl_Y,oripl_Z),(1,0,0),90) #say("axe "+axe+", value "+v) if axe=='x': #if abs(float(v)-p.Base[0])>min_val: shape.translate((float(v)-oripl_X,0,0)) if axe=='y': shape.translate((0,float(v)-oripl_Y,0)) if axe=='z': shape.translate((0,0,float(v)-oripl_Z)) #Part.show(shape) objs[0].Placement=shape.Placement FreeCADGui.Selection.addSelection(objs[0]) FreeCAD.activeDocument().recompute() if resetP==True: routineResetPlacement() doc.commitTransaction() #say("end of rotineM!") else: say(translate("Say", "Select an object !")) #QtGui.QMessageBox.information(None,"Info ...","Select ONE single part object !\r\n"+"\r\n") ### end Move to Point XYZ def recurse_app_part(ap,apl): # recursive function to get a list of Part objects contained in an AppPart obj if "App::Part" in ap.TypeId or "Body" in ap.TypeId: for o in ap.Group: #sayw(o.Name) if "App::Part" in o.TypeId or "Body" in o.TypeId: recurse_app_part(o,apl) elif "Sketch" not in o.TypeId: #print str(apl) #print o #if str(o.Name) not in str(apl): apl.append(o) return apl elif "Compound" in ap.TypeId: for e in ap.Links: if 'Compound' in e.Name: recurse_app_part(e,apl) else: apl.append(e) return apl ## def routineCollisions(): global conflict_tolerance def error_dialog(msg): """Create a simple dialog QMessageBox with an error message""" FreeCAD.Console.PrintError(msg + '\n') QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, 'Error in checking Collisions ."+"\r\n"', msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() #if len(FreeCADGui.Selection.getSelectionEx()) < 2: if len(FreeCADGui.Selection.getSelection()) < 2: error_dialog('Select at least two objects') collisions=2 return collisions object_list = [] object_names_list = [] collisions=0 #for obj in FreeCADGui.Selection.getSelectionEx(): # object_list.append(obj.Object) n_objs=0 apl=[] for obj in FreeCADGui.Selection.getSelection(): #print obj.TypeId if ('Part' in obj.TypeId or 'App::Link' in obj.TypeId or 'App::LinkPython' in obj.TypeId) and 'App::Part' not in obj.TypeId and 'Compound' not in obj.TypeId and 'Body' not in obj.TypeId: #print obj.TypeId #object_list.append(obj) if obj.Name not in object_names_list: if hasattr(obj,"Placement"): object_list.append(obj) n_objs=n_objs+1 #object_names_list.append(obj.Name) #n_objs=n_objs+1 elif 'App::Part' in obj.TypeId or 'Compound' in obj.TypeId or 'Body' in obj.TypeId: # adding any single part of the group #say('recursing AppPart or Compound') apl=[] recurse_app_part(obj,apl) apl_names=[] for o in apl: #print o.Name,' ','apl' ##if o.Name not in apl_names: ##apl_names.append(o.Name) apl_names.append(o.Name) #print o.Name,' name appended' #print apl_names,' apl names' for on in apl_names: object_list.append(FreeCAD.ActiveDocument.getObject(on)) n_objs=n_objs+1 #print object_list #stop if n_objs < 2: error_dialog('Select at least two simple objects') collisions=2 return collisions for i, object_a in enumerate(object_list): for object_b in object_list[(i + 1):]: say(make_string(object_a.Label)+" "+make_string(object_b.Label)) if not hasattr(object_a,'Shape'): # use_Links shape_a = Part.getShape(object_a) else: shape_a = object_a.Shape #shape_a = object_a.Shape if not hasattr(object_b,'Shape'): # use_Links shape_b = Part.getShape(object_b) else: shape_b = object_b.Shape #shape_b = object_b.Shape label_a = make_string(object_a.Label) label_b = make_string(object_b.Label) try: ## find the real position of the Part inside App::Part, then check collisions if use_AppPart: #print object_a.InListRecursive #print object_b.InListRecursive ## copy objects and apply absolute placement to each one, then check collisions p0 = FreeCAD.Placement (FreeCAD.Vector(0,0,0), FreeCAD.Rotation(0,0,0), FreeCAD.Vector(0,0,0)) pa_Original=shape_a.Placement s=shape_a #say('resetting props #2') r=[] t=s.copy() for i in t.childShapes(): c=i.copy() c.Placement=t.Placement.multiply(c.Placement) r.append((i,c)) acpy=t.replaceShape(r) acpy.Placement=FreeCAD.Placement() #Part.show(acpy) #stop lrl=len(object_a.InListRecursive) #for o in object_a.InListRecursive: # say(o.Name) inverted=True if len(object_a.InList): if object_a.InListRecursive[0].Name == object_a.InList[0].Name: inverted=False if inverted: for i in range (0,lrl): if hasattr(object_a.InListRecursive[i],'Placement'): acpy.Placement=acpy.Placement.multiply(object_a.InListRecursive[i].Placement) else: for i in range (0,lrl): if hasattr(object_a.InListRecursive[i],'Placement'): acpy.Placement=acpy.Placement.multiply(object_a.InListRecursive[lrl-1-i].Placement) #acpy.Placement=acpy.Placement.multiply(pa_Original) # Part.show(acpy) # stop pb_Original=shape_b.Placement s=shape_b #say('resetting props #2') r=[] t=s.copy() for i in t.childShapes(): c=i.copy() c.Placement=t.Placement.multiply(c.Placement) r.append((i,c)) bcpy=t.replaceShape(r) bcpy.Placement=FreeCAD.Placement() lrl=len(object_b.InListRecursive) if len(object_b.InList): if object_b.InListRecursive[0].Name == object_b.InList[0].Name: inverted=False if inverted: for i in range (0,lrl): if hasattr(object_b.InListRecursive[i],'Placement'): bcpy.Placement=bcpy.Placement.multiply(object_b.InListRecursive[i].Placement) else: for i in range (0,lrl): if hasattr(object_b.InListRecursive[i],'Placement'): bcpy.Placement=bcpy.Placement.multiply(object_b.InListRecursive[lrl-1-i].Placement) #Part.show(bcpy) common = acpy.common(bcpy) #FreeCAD.ActiveDocument.removeObject(acpy) #FreeCAD.ActiveDocument.removeObject(bcpy) FreeCAD.ActiveDocument.recompute() #stop ##try: ## ## find the real position of the Part inside App::Part, then check collisions ## ## print object_a.InListRecursive ## ## ## #b=App.ActiveDocument.addObject("Part::Box","Box") ## if use_AppPart: ## acpy= FreeCAD.ActiveDocument.copyObject(object_a,False) ## bcpy= FreeCAD.ActiveDocument.copyObject(object_b,False) ## #shape=acpy.Shape.copy() ## #shape.Placement=acpy.Placement ## #impPart.Placement=shape.Placement; ## ## copy objects and apply absolute placement to each one, then check collisions ## for o in object_b.InListRecursive: ## acpy.Placement=acpy.Placement.multiply(o.Placement) ## #Part.show(shape) ## #acpy.Placement=shape.Placement ## #stop ## #lr=len(object_a.InListRecursive) ## #for i in range(lr-1,-1,-1): ## # print object_a.InListRecursive[i].Label ## # #print get_node_plc(object_a.InListRecursive[i],acpy) ## # shape.Placement=shape.Placement.multiply(object_a.InListRecursive[i].Placement) ## # #acpy.Placement=acpy.Placement.multiply(object_a.InListRecursive[i].Placement) ## #acpy.Placement=shape.Placement ## #stop ## for o in object_b.InListRecursive: ## bcpy.Placement=bcpy.Placement.multiply(o.Placement) ## #print 'doing' ## #print bcpy.Name, 'here' ## common = acpy.Shape.common(bcpy.Shape) ## #FreeCAD.ActiveDocument.removeObject(acpy.Name) ## #FreeCAD.ActiveDocument.removeObject(bcpy.Name) ## #FreeCAD.ActiveDocument.recompute() ## #stop else: common = shape_a.common(shape_b) #d = shape_a.distToShape(shape_b) #sayw(d) #sayerr(d[0]) if common.Volume > conflict_tolerance: say( 'Volume of the intersection between {} and {}: {}\n'.format( label_a, label_b, common.Volume)) redundant=False for o in FreeCAD.ActiveDocument.Objects: if make_string(o.Label) == 'Collisions ({} - {})'.format(label_a, label_b): sayw('collision redundant') redundant=True if not redundant: intersection_object = FreeCAD.ActiveDocument.addObject( 'Part::Feature') intersection_object.Label = 'Collisions ({} - {})'.format( label_a, label_b) intersection_object.Shape = common ## print object_a.InListRecursive ## for o in object_a.InListRecursive: ## intersection_object.Placement=intersection_object.Placement.multiply(o.Placement) ## print o.Name ## bg=App.ActiveDocument.getObject('Board') ## intersection_object.Placement=bg.Placement.multiply(common.Placement) intersection_object.ViewObject.ShapeColor = (1.0, 0.0, 0.0, 1.0) #object_a.ViewObject.Transparency = 80 #object_b.ViewObject.Transparency = 80 #object_a.ViewObject.Visibility=False #object_b.ViewObject.Visibility=False sel1 = FreeCADGui.Selection.getSelection() for e in sel1: FreeCADGui.ActiveDocument.getObject(e.Name).Visibility=False ##for e in FreeCAD.ActiveDocument.Objects: ## if 'Compound' in e.TypeId: ## if object_a in e.Links or object_b in e.Links: ## e.ViewObject.Visibility=False ## elif 'App::Part' in e.TypeId: ## if object_a in e.Group: # and object_a not in sel1: ## object_a.ViewObject.Visibility=True ## FreeCADGui.ActiveDocument.getObject(e.Name).Visibility=False ## if object_b in e.Group: # and object_b not in sel1: ## object_b.ViewObject.Visibility=True ## FreeCADGui.ActiveDocument.getObject(e.Name).Visibility=False ## if FreeCADGui.ActiveDocument.getObject(e.Name).Visibility==False: ## for a in FreeCAD.ActiveDocument.Objects: ## if 'App::Part' in a.TypeId and a.Name != e.Name: ## #if a not in sel1: ## #print a.Group, e ## if e in a.Group and: e not in sel1: ## FreeCADGui.ActiveDocument.getObject(e.Name).Visibility=True collisions=1 else: say( 'No intersection between {} and {}\n'.format( label_a, label_b)) #collisions=0 except Exception as e: FreeCAD.Console.PrintWarning(u"{0}\n".format(e)) #say("here_collision\r\n") return collisions ### end Collisions def create_axis(): global disablePoM_Observer if disable_PoM_Observer: #paramGetPoM = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/PartOMagic") #PoMObs_status=paramGetPoM.GetBool("EnableObserver") PoMObs_status = False if Observer.isRunning(): PoMObs_status=True #if PoMObs_status: Observer.stop() sayw("disabling PoM Observer") FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "axis") #Z axis FreeCAD.ActiveDocument.addObject("Part::Box","AxisBoxZ") FreeCAD.ActiveDocument.ActiveObject.Label = "CubeZ" FreeCAD.ActiveDocument.addObject("Part::Cone","AxisConeZ") FreeCAD.ActiveDocument.ActiveObject.Label = "ConeZ" FreeCAD.ActiveDocument.getObject("AxisBoxZ").Width = '0 mm' FreeCAD.ActiveDocument.getObject("AxisBoxZ").Width = '0.1 mm' FreeCAD.ActiveDocument.getObject("AxisBoxZ").Length = '0 mm' FreeCAD.ActiveDocument.getObject("AxisBoxZ").Length = '0.2 mm' FreeCAD.ActiveDocument.getObject("AxisConeZ").Radius1 = '0 mm' FreeCAD.ActiveDocument.getObject("AxisConeZ").Radius1 = '0.4 mm' FreeCAD.ActiveDocument.getObject("AxisConeZ").Radius2 = '0 mm' FreeCAD.ActiveDocument.getObject("AxisConeZ").Radius2 = '0.1 mm' FreeCAD.ActiveDocument.getObject("AxisConeZ").Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,9),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCAD.ActiveDocument.getObject("AxisConeZ").Height = '5 mm' FreeCAD.ActiveDocument.getObject("AxisBoxZ").Placement = FreeCAD.Placement(FreeCAD.Vector(-0.1,-0.05,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCADGui.ActiveDocument.getObject("AxisConeZ").ShapeColor = (0.0000,0.0000,1.0000) FreeCADGui.ActiveDocument.getObject("AxisBoxZ").ShapeColor = (0.0000,0.0000,1.0000) FreeCAD.activeDocument().addObject("Part::MultiFuse","FusionAxisZ") FreeCAD.activeDocument().FusionAxisZ.Shapes = [FreeCAD.activeDocument().AxisBoxZ,FreeCAD.activeDocument().AxisConeZ,] FreeCADGui.activeDocument().AxisBoxZ.Visibility=False FreeCADGui.activeDocument().AxisConeZ.Visibility=False FreeCADGui.ActiveDocument.FusionAxisZ.ShapeColor=FreeCADGui.ActiveDocument.AxisBoxZ.ShapeColor FreeCADGui.ActiveDocument.FusionAxisZ.DisplayMode=FreeCADGui.ActiveDocument.AxisBoxZ.DisplayMode FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.addObject('Part::Feature','FusionAxisZ1').Shape=FreeCAD.ActiveDocument.FusionAxisZ.Shape FreeCAD.ActiveDocument.ActiveObject.Label = "Z" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=(0.0000,0.0000,1.0000) obj=FreeCAD.ActiveDocument.ActiveObject FreeCAD.ActiveDocument.getObject("axis").addObject(obj) FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.removeObject("FusionAxisZ") FreeCAD.ActiveDocument.removeObject("AxisBoxZ") FreeCAD.ActiveDocument.removeObject("AxisConeZ") FreeCAD.ActiveDocument.recompute() #Y axis FreeCAD.ActiveDocument.addObject("Part::Box","AxisBoxY") FreeCAD.ActiveDocument.ActiveObject.Label = "CubeY" FreeCAD.ActiveDocument.addObject("Part::Cone","AxisConeY") FreeCAD.ActiveDocument.ActiveObject.Label = "ConeY" FreeCAD.ActiveDocument.getObject("AxisBoxY").Width = '0 mm' FreeCAD.ActiveDocument.getObject("AxisBoxY").Width = '0.1 mm' FreeCAD.ActiveDocument.getObject("AxisBoxY").Length = '0 mm' FreeCAD.ActiveDocument.getObject("AxisBoxY").Length = '0.2 mm' FreeCAD.ActiveDocument.getObject("AxisConeY").Radius1 = '0 mm' FreeCAD.ActiveDocument.getObject("AxisConeY").Radius1 = '0.4 mm' FreeCAD.ActiveDocument.getObject("AxisConeY").Radius2 = '0 mm' FreeCAD.ActiveDocument.getObject("AxisConeY").Radius2 = '0.1 mm' FreeCAD.ActiveDocument.getObject("AxisConeY").Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,9),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCAD.ActiveDocument.getObject("AxisConeY").Height = '5 mm' FreeCAD.ActiveDocument.getObject("AxisBoxY").Placement = FreeCAD.Placement(FreeCAD.Vector(-0.1,-0.05,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCADGui.ActiveDocument.getObject("AxisConeY").ShapeColor = (0.0000,1.0000,0.0000) FreeCADGui.ActiveDocument.getObject("AxisBoxY").ShapeColor = (0.0000,1.0000,0.0000) FreeCAD.activeDocument().addObject("Part::MultiFuse","FusionAxisY") FreeCAD.activeDocument().FusionAxisY.Shapes = [FreeCAD.activeDocument().AxisBoxY,FreeCAD.activeDocument().AxisConeY,] FreeCADGui.activeDocument().AxisBoxY.Visibility=False FreeCADGui.activeDocument().AxisConeY.Visibility=False FreeCADGui.ActiveDocument.FusionAxisY.ShapeColor=FreeCADGui.ActiveDocument.AxisBoxY.ShapeColor FreeCADGui.ActiveDocument.FusionAxisY.DisplayMode=FreeCADGui.ActiveDocument.AxisBoxY.DisplayMode FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.addObject('Part::Feature','FusionAxisY1').Shape=FreeCAD.ActiveDocument.FusionAxisY.Shape FreeCAD.ActiveDocument.ActiveObject.Label = "Y" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=(0.0000,1.0000,0.000) obj=FreeCAD.ActiveDocument.ActiveObject FreeCAD.ActiveDocument.getObject("axis").addObject(obj) FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.removeObject("FusionAxisY") FreeCAD.ActiveDocument.removeObject("AxisBoxY") FreeCAD.ActiveDocument.removeObject("AxisConeY") FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.ActiveObject.Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,0.05),FreeCAD.Rotation(FreeCAD.Vector(1,0,0),-90)) #X axis FreeCAD.ActiveDocument.addObject("Part::Box","AxisBoxX") FreeCAD.ActiveDocument.ActiveObject.Label = "CubeX" FreeCAD.ActiveDocument.addObject("Part::Cone","AxisConeX") FreeCAD.ActiveDocument.ActiveObject.Label = "ConeX" FreeCAD.ActiveDocument.getObject("AxisBoxX").Width = '0 mm' FreeCAD.ActiveDocument.getObject("AxisBoxX").Width = '0.2 mm' FreeCAD.ActiveDocument.getObject("AxisBoxX").Length = '0 mm' FreeCAD.ActiveDocument.getObject("AxisBoxX").Length = '0.1 mm' FreeCAD.ActiveDocument.getObject("AxisConeX").Radius1 = '0 mm' FreeCAD.ActiveDocument.getObject("AxisConeX").Radius1 = '0.4 mm' FreeCAD.ActiveDocument.getObject("AxisConeX").Radius2 = '0 mm' FreeCAD.ActiveDocument.getObject("AxisConeX").Radius2 = '0.1 mm' FreeCAD.ActiveDocument.getObject("AxisConeX").Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,9),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCAD.ActiveDocument.getObject("AxisConeX").Height = '5 mm' FreeCAD.ActiveDocument.getObject("AxisBoxX").Placement = FreeCAD.Placement(FreeCAD.Vector(-0.1,-0.05,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) FreeCADGui.ActiveDocument.getObject("AxisConeX").ShapeColor = (1.0000,0.0000,0.0000) FreeCADGui.ActiveDocument.getObject("AxisBoxX").ShapeColor = (1.0000,0.0000,0.0000) FreeCAD.activeDocument().addObject("Part::MultiFuse","FusionAxisX") FreeCAD.activeDocument().FusionAxisX.Shapes = [FreeCAD.activeDocument().AxisBoxX,FreeCAD.activeDocument().AxisConeX,] FreeCADGui.activeDocument().AxisBoxX.Visibility=False FreeCADGui.activeDocument().AxisConeX.Visibility=False FreeCADGui.ActiveDocument.FusionAxisX.ShapeColor=FreeCADGui.ActiveDocument.AxisBoxX.ShapeColor FreeCADGui.ActiveDocument.FusionAxisX.DisplayMode=FreeCADGui.ActiveDocument.AxisBoxX.DisplayMode FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.addObject('Part::Feature','FusionAxisX1').Shape=FreeCAD.ActiveDocument.FusionAxisX.Shape FreeCAD.ActiveDocument.ActiveObject.Label="X" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor=(1.0000,0.0000,0.0000) obj=FreeCAD.ActiveDocument.ActiveObject FreeCAD.ActiveDocument.getObject("axis").addObject(obj) FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.removeObject("FusionAxisX") FreeCAD.ActiveDocument.removeObject("AxisBoxX") FreeCAD.ActiveDocument.removeObject("AxisConeX") FreeCAD.ActiveDocument.getObject("FusionAxisX1").Placement = FreeCAD.Placement(FreeCAD.Vector(0,-0.05,0),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90)) FreeCAD.ActiveDocument.recompute() if disable_PoM_Observer: if PoMObs_status: Observer.start() sayw("enabling PoM Observer") ### ############################# def createSolidBBox2(model3D): #FreeCADGui.Selection.removeSelection(FreeCAD.activeDocument().ActiveObject) selEx=model3D selEx = FreeCADGui.Selection.getSelectionEx() objs = [selobj.Object for selobj in selEx] if len(objs) == 1: s = objs[0].Shape name=objs[0].Label #say(name+" name") # boundBox delta=0.6 boundBox_ = s.BoundBox boundBoxLX = boundBox_.XLength*(1+delta) boundBoxLY = boundBox_.YLength*(1+delta) #boundBoxLZ = boundBox_.ZLength boundBoxLZ = 1.58 offX=boundBox_.XLength*(-delta)/2 offY=boundBox_.YLength*(-delta)/2 offZ=-0.01 a = str(boundBox_) a,b = a.split('(') c = b.split(',') oripl_X = float(c[0])+offX oripl_Y = float(c[1])+offY #oripl_Z = float(c[2])+offZ oripl_Z = -boundBoxLZ+offZ #say(str(boundBox_)) #say("Rectangle : "+str(boundBox_.XLength)+" x "+str(boundBox_.YLength)+" x "+str(boundBox_.ZLength)) #say("_____________________") #say("x: "+str(oripl_X)+" y: "+str(oripl_Y)+"z: "+str(oripl_Z)) obj=FreeCAD.ActiveDocument.addObject('Part::Feature',name) #obj.Shape=Part.makeBox(boundBox_.XLength, boundBox_.YLength, boundBox_.ZLength, FreeCAD.Vector(oripl_X,oripl_Y,oripl_Z), FreeCAD.Vector(0,0,01)) #obj.Shape=Part.makeBox(boundBoxLX, boundBoxLY, boundBoxLZ, FreeCAD.Vector(oripl_X,oripl_Y,oripl_Z), FreeCAD.Vector(0,0,01)) obj.Shape=Part.makeBox(boundBoxLX, boundBoxLY, boundBoxLZ, FreeCAD.Vector(oripl_X,oripl_Y,oripl_Z), FreeCAD.Vector(0,0,1)) #obj.translate(offX,offY,0) # Part.show(cube) #say("cube name "+ obj.Name) ### FreeCAD.ActiveDocument.recompute() else: say("Select a single part object !") #end bbox macro name=obj.Name #say("bbox name "+name) del objs return name ### def rotateObj(mainObj, rot): return mainObj.rotate(FreeCAD.Vector(rot[0], rot[1], 0), FreeCAD.Vector(0, 0, 1), rot[2]) ### def rotateObjs(listObjs, rot): #listObjs.rotate(FreeCAD.Vector(rot[0], rot[1], 0), FreeCAD.Vector(0, 0, 1), rot[2]) Draft.rotate(listObjs,rot[2],FreeCAD.Vector(rot[0],rot[1],0.0),axis=FreeCAD.Vector(0.0,0.0,1.0),copy=False) ### def changeSide(self, mainObj, X1, Y1, top): if top == 0: #to bot side mainObj.rotate(FreeCAD.Vector(X1, Y1, 0), FreeCAD.Vector(0, 1, 0), 180) ### def arcMidPoint(prev_vertex, vertex, angle): if len(prev_vertex) == 3: [x1, y1, z1] = prev_vertex else: [x1, y1] = prev_vertex if len(vertex) == 3: [x2, y2, z2] = vertex else: [x2, y2] = vertex angle = radians(angle / 2) basic_angle = atan2(y2 - y1, x2 - x1) - pi / 2 shift = (1 - cos(angle)) * hypot(y2 - y1, x2 - x1) / 2 / sin(angle) midpoint = [(x2 + x1) / 2 + shift * cos(basic_angle), (y2 + y1) / 2 + shift * sin(basic_angle)] return midpoint ### def sinus(angle): return float("%4.10f" % sin(radians(angle))) def cosinus(angle): return float("%4.10f" % cos(radians(angle))) def arcCenter(x1, y1, x2, y2, x3, y3): Xs = 0.5 * (x2 * x2 * y3 + y2 * y2 * y3 - x1 * x1 * y3 + x1 * x1 * y2 - y1 * y1 * y3 + y1 * y1 * y2 + y1 * x3 * x3 + y1 * y3 * y3 - y1 * x2 * x2 - y1 * y2 * y2 - y2 * x3 * x3 - y2 * y3 * y3) / (y1 * x3 - y1 * x2 - y2 * x3 - y3 * x1 + y3 * x2 + y2 * x1) Ys = 0.5 * (-x1 * x3 * x3 - x1 * y3 * y3 + x1 * x2 * x2 + x1 * y2 * y2 + x2 * x3 * x3 + x2 * y3 * y3 - x2 * x2 * x3 - y2 * y2 * x3 + x1 * x1 * x3 - x1 * x1 * x2 + y1 * y1 * x3 - y1 * y1 * x2) / (y1 * x3 - y1 * x2 - y2 * x3 - y3 * x1 + y3 * x2 + y2 * x1) return [Xs, Ys] #def arcCenter2(x1, y1, x2, y2, angle): # # point M - center point between p1 and p2 # Mx = (x1 + x2) / 2. # My = (y1 + y2) / 2. # # # p1_M - distance between point p1 and M # p1_M = sqrt((x1 - Mx) ** 2 + (y1 - My) ** 2) # radius = float("%4.9f" % abs(p1_M / sin(radians(angle / 2.)))) # radius of searching circle - line C_p1 # CenterDist = float("%4.9f" % abs(radius * cos(radians(angle / 2.)))) # radius of searching circle - line C_p1 # # return CenterDist def arcRadius(x1, y1, x2, y2, angle): #dx = abs(x2 - x1) #dy = abs(y2 - y1) #d = sqrt(dx ** 2 + dy ** 2) # distance between p1 and p2 # point M - center point between p1 and p2 Mx = (x1 + x2) / 2. My = (y1 + y2) / 2. # p1_M - distance between point p1 and M p1_M = sqrt((x1 - Mx) ** 2 + (y1 - My) ** 2) radius = float("%4.9f" % abs(p1_M / sin(radians(angle / 2.)))) # radius of searching circle - line C_p1 return radius def arcAngles2 (edge,angle): #(xs, ys, xe, ye, cx, cy, angle): #sa = atan2 (ys-cy, xs-cx) #ea = atan2 (ye-cy, xe-cx) ##if angle > 0: ## sa = atan2 (ys-cy, xs-cx) ## ea = sa + radians(angle) #2*pi+angle #atan2 (ye-cy, xe-cx) ##else: ## sa = atan2 (ye-cy, xe-cx) ## ea = sa + radians(abs(angle)) # 2*pi+angle #atan2 (ye-cy, xe-cx) #ea = ea - pi/2 #sa = sa - pi/2 #if ea == sa: # ea = pi/2 if DraftGeomUtils.geomType(edge) == "Circle": Radius = edge.Curve.Radius placement = FreeCAD.Placement(edge.Placement) #delta = edge.Curve.Center.sub(placement.Base) #placement.move(delta) if len(edge.Vertexes) > 1: ref = placement.multVec(FreeCAD.Vector(1,0,0)) v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center) v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center) a1 = -(DraftVecUtils.angle(v1,ref)) a2 = -(DraftVecUtils.angle(v2,ref)) FirstAngle = a1 LastAngle = a2 if angle <0: return [a2, a1] else: return [a1, a2] def arcAngles(x1, y1, x2, y2, Cx, Cy, angle): if angle > 0: startAngle = atan2(y1 - Cy, x1 - Cx) if startAngle < 0.: startAngle = 6.28 + startAngle stopAngle = startAngle + radians(angle) # STOP ANGLE else: startAngle = atan2(y2 - Cy, x2 - Cx) if startAngle < 0.: startAngle = 6.28 + startAngle stopAngle = startAngle + radians(abs(angle)) # STOP ANGLE # startAngle = float("%4.2f" % startAngle) - pi/2 stopAngle = float("%4.2f" % stopAngle) - pi/2 return [startAngle, stopAngle] def shiftPointOnLine(x1, y1, x2, y2, distance): if x2 - x1 == 0: # vertical line x_T1 = x1 y_T1 = y1 - distance else: a = (y2 - y1) / (x2 - x1) if a == 0: # horizontal line x_T1 = x1 - distance y_T1 = y1 else: alfa = atan(a) #alfa = tan(a) x_T1 = x1 - distance * cos(alfa) y_T1 = y1 - distance * sin(alfa) return [x_T1, y_T1] ### def getLine(layer, content, oType): layer=layer.replace('"','') data = [] source = ''.join(content) source=source.replace('"','') # #data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)(\s+\(angle\s+[0-9\.-]*?\)\s+|\s+)\(layer\s+{0}\)\s+\(width\s+([0-9\.]*?)\)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)(\s+\(angle\s+[0-9\.-]*?\)\s+|\s+)\(layer\s+{0}\)\s+\(width\s+([0-9\.]*?)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) #TBD fp_line kv7 #say(data1) for i in data1: x1 = float(i[0]) y1 = float(i[1]) * (-1) x2 = float(i[2]) y2 = float(i[3]) * (-1) width = float(i[5]) data.append([x1, y1, x2, y2, width]) # return data ### def getLineF(layer, content, oType, m=[0,0]): data = [] source = ''.join(content) # #data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)(\s+\(angle\s+[0-9\.-]*?\)\s+|\s+)\(layer\s+{0}\)\s+\(width\s+([0-9\.]*?)\)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)(\s+\(angle\s+[0-9\.-]*?\)\s+|\s+)\(layer\s+{0}\)\s+\(width\s+([0-9\.]*?)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) #say(data1) for i in data1: x1 = float(i[0]) y1 = float(i[1]) * (-1) x2 = float(i[2]) y2 = float(i[3]) * (-1) width = float(i[5]) if [x1, y1] == [x2, y2]: continue if m[0] != 0: x1 += m[0] x2 += m[0] if m[1] != 0: y1 += m[1] y2 += m[1] data.append([x1, y1, x2, y2, width]) # return data def getCircle(layer, content, oType): data = [] # source = ''.join(content) layer=layer.replace('"','') source=source.replace('"','') #data1 = re.findall(r'\({1}\s+\(center\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)\)|)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) data1 = re.findall(r'\({1}\s+\(center\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)\)|)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) #say(source) #say(data1) for i in data1: xs = float(i[0]) ys = float(i[1]) * (-1) x1 = float(i[2]) y1 = float(i[3]) * (-1) radius = sqrt((xs - x1) ** 2 + (ys - y1) ** 2) if i[5] == '': width = 0.01 else: width = float(i[5]) data.append([xs, ys, radius, width]) # #say(data) return data ### def getCircleF(layer, content, oType, m=[0,0]): data = [] # source = ''.join(content) #data1 = re.findall(r'\({1}\s+\(center\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)\)|)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) data1 = re.findall(r'\({1}\s+\(center\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)\)|)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) #say(source) #say(data1) for i in data1: xs = float(i[0]) ys = float(i[1]) * (-1) x1 = float(i[2]) y1 = float(i[3]) * (-1) radius = sqrt((xs - x1) ** 2 + (ys - y1) ** 2) if i[5] == '': width = 0.01 else: width = float(i[5]) if m[0] != 0: xs += m[0] if m[1] != 0: ys += m[1] data.append([xs, ys, radius, width]) # #say(data) return data ### def rotPoint(point, ref, angle): sinKAT = self.sinus(angle) cosKAT = self.cosinus(angle) x1R = (point[0] * cosKAT) - (point[1] * sinKAT) + ref[0] y1R = (point[0] * sinKAT) + (point[1] * cosKAT) + ref[1] return [x1R, y1R] ### def rotPoint2(point, ref, angle): sinKAT = sinus(angle) cosKAT = cosinus(angle) x1R = ((point[0] - ref[0]) * cosKAT) - sinKAT * (point[1] - ref[1]) + ref[0] y1R = ((point[0] - ref[0]) * sinKAT) + cosKAT * (point[1] - ref[1]) + ref[1] return [x1R, y1R] ### def getArc(layer, content, oType): data = [] source = ''.join(content) source = source.replace('"','') layer = layer.replace('"','') #data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(angle\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)\)|)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) #(fp_arc (start 0.015 -0.03) (end 22.348 -2.772) (angle -17) (layer Edge.Cuts) (width 0.16)) data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(angle\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)|)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) for i in data1: xs = float(i[0]) ys = float(i[1]) x1 = float(i[2]) y1 = float(i[3]) curve = float(i[4]) if i[6].strip() != '': width = float(i[6]) else: width = 0 if abs(curve)==360: [x2, y2] = [xs, ys] else: [x2, y2] = rotPoint2([x1, y1], [xs, ys], curve) data.append([x1, y1 * (-1), x2, y2 * (-1), curve, width]) if len (data) == 0: # (fp_arc (start 20.570471 -9.181725) (mid 21.697398 -6.042909) (end 22.348 -2.772) (layer "Edge.Cuts") (width 0.16) (tstamp 30b75c25-1d2c-45e7-83e2-bb3be98f8f83)) data2 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(mid\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)\s+\(width\s+([0-9\.]*?)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) #print(data2,data,data1) for i in data2: xs = float(i[0]) ys = float(i[1]) xm = float(i[2]) ym = float(i[3]) x1 = float(i[4]) y1 = float(i[5]) if i[6].strip() != '': width = float(i[6]) else: width = 0 data.append([xs, ys * (-1), xm, ym * (-1), x1, y1 * (-1), width]) # return data ### def getArcF(layer, content, oType, m=[0,0]): data = [] # source = ''.join(content) #data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(angle\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)\)|)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) data1 = re.findall(r'\({1}\s+\(start\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(end\s+([0-9\.-]*?)\s+([0-9\.-]*?)\)\s+\(angle\s+([0-9\.-]*?)\)\s+\(layer\s+{0}\)(\s+\(width\s+([0-9\.]*?)|)\)'.format(layer, oType), source, re.MULTILINE|re.DOTALL) for i in data1: xs = float(i[0]) ys = float(i[1]) x1 = float(i[2]) y1 = float(i[3]) curve = float(i[4]) if i[6].strip() != '': width = float(i[6]) else: width = 0 [x2, y2] = rotPoint2([x1, y1], [xs, ys], curve) y1 *= -1 y2 *= -1 if m[0] != 0: x1 += m[0] x2 += m[0] if m[1] != 0: y1 += m[1] y2 += m[1] data.append([x1, y1 , x2, y2, curve, width]) #data.append([x1, y1 * (-1), x2, y2 * (-1), curve, width]) # return data def getModName(source): #say("here test0") #for x in source: # x.encode('utf-8') # say(x) #say("here test1") #model = ''.join(u) model = ''.join(source) #sayw("here")#; #sayw("here test2") match = re.search(r'((\(module\s)|(\(footprint\s))+(.+?)\(layer', model, re.MULTILINE|re.DOTALL) if match is not None: model_name = match.groups(0)[3] if ' (version' in model_name: model_name = model_name[:model_name.index(' (version')] model_name = model_name.replace('"','').rstrip()+'-' return model_name ### def getwrlData(source): model = ''.join(source) wrl_pos=['0', '0', '0'] if re.search(r'\(at\s+\(xyz+\s(.+?)\)', model, re.MULTILINE|re.DOTALL) is not None: pos_vrml = re.search(r'\(at\s+\(xyz+\s(.+?)\)', model, re.MULTILINE|re.DOTALL).groups(0)[0] #pos_vrml=pos_vrml[5:] wrl_pos=pos_vrml.split(" ") xp_vrml=wrl_pos[0] #say('alive') yp_vrml=wrl_pos[1] zp_vrml=wrl_pos[2] #say(wrl_pos); #wrl_pos=(xp_vrml,yp_vrml,zp_vrml) #say(wrl_pos); # if re.search(r'\(offset\s+\(xyz+\s(.+?)\)', model, re.MULTILINE|re.DOTALL) is not None: pos_vrml = re.search(r'\(offset\s+\(xyz+\s(.+?)\)', model, re.MULTILINE|re.DOTALL).groups(0)[0] #pos_vrml=pos_vrml[5:] wrl_pos=pos_vrml.split(" ") xp_vrml=wrl_pos[0] #say('alive') yp_vrml=wrl_pos[1] zp_vrml=wrl_pos[2] #say(wrl_pos); #wrl_pos=(xp_vrml,yp_vrml,zp_vrml) # scale_vrml=['1', '1', '1'] if re.search(r'\(scale\s+(.+?)\)', model, re.MULTILINE|re.DOTALL) is not None: sc_vrml = re.search(r'\(scale\s+(.+?)\)', model, re.MULTILINE|re.DOTALL).groups(0)[0] sc_vrml=sc_vrml[5:] scale_vrml=sc_vrml.split(" ") xsc_vrml=scale_vrml[0] ysc_vrml=scale_vrml[1] zsc_vrml=scale_vrml[2] #say(scale_vrml); #say(scale_vrml); # rot_wrl=['0', '0', '0'] zrot_vrml='' if re.search(r'\(rotate\s+(.+?)\)', model, re.MULTILINE|re.DOTALL) is not None: rot_vrml = re.search(r'\(rotate\s+(.+?)\)', model, re.MULTILINE|re.DOTALL).groups(0)[0] rot_vrml=rot_vrml[5:] rot_wrl=rot_vrml.split(" ") xrot_vrml=rot_wrl[0] yrot_vrml=rot_wrl[1] zrot_vrml=rot_wrl[2] #say(rot_wrl); else: rotz_vrml=False #say("hereA") #if rotz_vrml: # zrot_vrml=zrot_vrml # #say("rotz:"+rotz) # ##rotz=rotz[5:] # #say("rotz:"+rotz) # ##temp=rotz.split(" ") # #say("rotz temp:"+temp[2]) # ##rotz=temp[2] # #say("rotate vrml: "+rotz) if zrot_vrml=='': zrot_vrml=0.0 else: zrot_vrml=float(zrot_vrml) rot=zrot_vrml #adding vrml module z-rotation #say(rot_wrl); return wrl_pos, scale_vrml, rot_wrl def getwrlRot(source): model = ''.join(source) if re.search(r'\(rotate\s+(.+?)\)', model, re.MULTILINE|re.DOTALL) is not None: rotz_vrml = re.search(r'\(rotate\s+(.+?)\)', model, re.MULTILINE|re.DOTALL).groups(0)[0] else: rotz_vrml=False #say("hereA") rotz='' if rotz_vrml: rotz=rotz_vrml #say("rotz:"+rotz) rotz=rotz[5:] #say("rotz:"+rotz) temp=rotz.split(" ") #say("rotz temp:"+temp[2]) rotz=temp[2] #say("rotate vrml: "+rotz) if rotz=='': rotz=0.0 else: rotz=float(rotz) rot=rotz #adding vrml module z-rotation return rot ### def getPadsList(content): pads = [] # model = ''.join(content) #model_name = re.search(r'\(module\s+(.+?)\(layer', model, re.MULTILINE|re.DOTALL).groups(0)[0] #say(model_name) found = re.findall(r'\(pad .*', model, re.MULTILINE|re.DOTALL) #found_fp = re.findall(r'\(fp_poly .*', model, re.MULTILINE|re.DOTALL) zones = re.findall(r'\(zone .*', model, re.MULTILINE|re.DOTALL) if len(zones): zones = zones[0].strip().split('(zone ') ## TBD create sketch for zones #print(zones) #for z in zones: # if z != '': if len(found): found = found[0].strip().split('(pad ') #removing extra keepout zones for count, p in enumerate(found): if '(zone ' in p: #print (len(p)) idx = p.index("(zone ") z = p[0:idx] found[count] = z for j in found: if j != '': [x, y, rot] = re.search(r'\(at\s+([0-9\.-]*?)\s+([0-9\.-]*?)(\s+[0-9\.-]*?|)\)', j).groups() pType= re.search(r'^.*?\s+([a-zA-Z_]+?)\s+', j).groups(0)[0] # pad type - SMD/thru_hole/connect pShape = re.search(r'^.+?\s+.+?\s+([a-zA-Z_]+?)\s+', j).groups(0)[0] # pad shape - circle/rec/oval/trapezoid/roundrect pRoundG = re.search(r'\(roundrect_rratio\s+([0-9\.-]+?)\)', j) if pRoundG is not None: pRound = pRoundG.groups(0)[0] else: pRound=None #pCircleG = re.search(r'\(gr_circle+.+?\)\)', j, re.MULTILINE|re.DOTALL) #re.search(r'\(gr_circle\s.+(?=\)\)$)', j) #(?<=^startstr).+(?=stopstr$) pCircleG = re.search(r'(\(gr_circle)\s+(.+?)\)\)', j) #, re.MULTILINE|re.DOTALL) #re.search(r'\(gr_circle\s.+(?=\)\)$)', j) #(?<=^startstr).+(?=stopstr$) #print(pCircleG);print(j);stop if pCircleG is not None: pCircleG = pCircleG.groups(0)[1].split(')') pCircleG[1]=pCircleG[1].lstrip(' ') pCircleG[2]=pCircleG[2].lstrip(' ') #say(pCircleG);stop else: pCircleG=None #sayw(pShape) #sayw(pRound) [dx, dy] = re.search(r'\(size\s+([0-9\.-]+?)\s+([0-9\.-]+?)\)', j).groups(0) # try: layers = re.search(r'\(layers\s+(.+?)\)', j).groups(0)[0] # except: layers = 'F.SilkS' #layers = None sayerr('NO LAYERS on PAD') #test utf-8 test pads # print(layers) # stop data = re.search(r'\(drill(\s+oval\s+|\s+)(.*?)(\s+[-0-9\.]*?|)(\s+\(offset\s+(.*?)\s+(.*?)\)|)\)', j) data_off = re.search(r'\(offset\s+([0-9\.-]+?)\s+([0-9\.-]+?)\)', j) pnts = re.search(r'\(gr_poly\s\(pts(.*?)\)\s\(width', j, re.MULTILINE|re.DOTALL) #pnts_nt = re.search(r'\(fp_poly\s\(pts(.*?)\)\s\(width', j, re.MULTILINE|re.DOTALL) #if pnts_nt is not None: # pnts = pnts_nt #re.search(r'\(fp_poly\s\(pts(.*?)\)\s\(width', j, re.MULTILINE|re.DOTALL) # pShape = 'NetTie' anchor = re.search(r'\(anchor\s(.*?)\)\)', j) if anchor is not None: anchor=anchor.groups()[0] #print anchor #if pnts is not None: # print pnts.groups(0)[0].split('(xy') # x = float(x) y = float(y) * (-1) dx = float(dx) dy = float(dy) if rot == '' or len(rot.strip(' '))==0: rot = 0.0 else: #print rot rot = float(rot) #print(pType) if pType == 'smd' or pType == 'connect' or data is None: drill_x = 0.0 drill_y = 0.0 hType = None if data_off is None: [xOF, yOF] = [0.0, 0.0] else: data_off = data_off.groups() if not data_off[0] or data_off[0].strip() == '': xOF = 0.0 else: xOF = float(data_off[0]) if not data_off[1] or data_off[1].strip() == '': yOF = 0.0 else: yOF = float(data_off[1]) else: data = data.groups() hType = data[0] if hType.strip() == '': hType = 'circle' drill_x = float(data[1]) #/ 2.0 if not data[2] or data[2].strip() == '': drill_y=drill_x else: drill_y = float(data[2]) #/ 2.0 #drill_y=drill_x if not data[4] or data[4].strip() == '': xOF = 0.0 else: xOF = float(data[4]) if not data[5] or data[5].strip() == '': yOF = 0.0 else: yOF = float(data[5]) ## #say(data) pads.append({'x': x, 'y': y, 'rot': rot, 'padType': pType, 'padShape': pShape, 'rx': drill_x, 'ry': drill_y, 'dx': dx, 'dy': dy, \ 'holeType': hType, 'xOF': xOF, 'yOF': yOF, 'layers': layers, 'points': pnts, 'anchor': anchor, 'rratio': pRound, 'geomC':pCircleG}) #say(pads) # return pads ### ### def getPolyList(content): pads = [] # model = ''.join(content) #model_name = re.search(r'\(module\s+(.+?)\(layer', model, re.MULTILINE|re.DOTALL).groups(0)[0] #say(model_name) fp_pnts = [] width = 0.16 found = re.findall(r'\(fp_poly.*', model, re.MULTILINE|re.DOTALL) if len(found): found = found[0].strip().split('(fp_poly ') for j in found: #print('j',j) if j != '': try: layers = re.search(r'\(layer\s+(.+?)\)', j).groups(0)[0] # except: layers = 'F.SilkS' #layers = None sayerr('NO LAYER on NetTie') #test utf-8 test pads #print(layers) # stop #pnts = re.search(r'\(fp_poly\s\(pts(.*?)\)\s\(width', j, re.MULTILINE|re.DOTALL) pnts = re.search(r'\(pts(.*?)(.*?)\(width', j, re.MULTILINE|re.DOTALL) #pnts_nt = re.search(r'\(fp_poly\s\(pts(.*?)\)\s\(width', j, re.MULTILINE|re.DOTALL) #if pnts_nt is not None: # pnts = pnts_nt #re.search(r'\(fp_poly\s\(pts(.*?)\)\s\(width', j, re.MULTILINE|re.DOTALL) # pShape = 'NetTie' #print('pnts',pnts.group()) width = re.search(r'\(width(.*?)\)', j, re.MULTILINE|re.DOTALL) width = float(width[0].strip().split('(width ')[1].strip(')')) #print('width',width) fp_pnts.append({'layers': layers, 'points': pnts}) #say(fp_pnts) # return fp_pnts, width ### def makePoint(self, x, y): wir = [] wir.append(Part.Point(FreeCAD.Base.Vector(x, y, 0))) mainObj = Part.Shape(wir) return mainObj ### def makeFace(mainObj): return Part.Face(mainObj) ### def cutHole(mainObj, hole): if hole[2] > min_val: hole = [Part.Circle(FreeCAD.Vector(hole[0], hole[1]), FreeCAD.Vector(0, 0, 1), hole[2]).toShape()] hole = Part.Wire(hole) hole = Part.Face(hole) mainObj = mainObj.cut(hole) return mainObj ### def cutObj(mainObj, hole): mainObj = mainObj.cut(hole) return mainObj ### def createCircle(x, y, r, w=0): if w > min_val: mainObj = Part.Wire([Part.Circle(FreeCAD.Vector(x, y), FreeCAD.Vector(0, 0, 1), r + w / 2.).toShape()]) mainObj = makeFace(mainObj) mainObj = cutHole(mainObj, [x, y, r - w / 2.]) return mainObj else: mainObj = [Part.Circle(FreeCAD.Vector(x, y), FreeCAD.Vector(0, 0, 1), r).toShape()] return makeFace(Part.Wire(mainObj)) ### def createArc_OLD(p1, p2, curve, width=0.02, cap='round'): try: wir = [] if width <= 0: width = 0.02 width /= 2. [x3, y3] = arcMidPoint(p1, p2, curve) [xs, ys] = arcCenter(p1[0], p1[1], p2[0], p2[1], x3, y3) ## #a = (ys - p1[1]) / (xs - p1[0]) [xT_1, yT_1] = shiftPointOnLine(p1[0], p1[1], xs, ys, width) [xT_4, yT_4] = shiftPointOnLine(p1[0], p1[1], xs, ys, -width) ### [xT_2, yT_2] = rotPoint2([xT_1, yT_1], [xs, ys], curve) [xT_5, yT_5] = rotPoint2([xT_4, yT_4], [xs, ys], curve) ######## ######## wir = [] ## outer arc [xT_3, yT_3] = arcMidPoint([xT_1, yT_1], [xT_2, yT_2], curve) wir.append(Part.Arc(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_3, yT_3, 0), FreeCAD.Base.Vector(xT_2, yT_2, 0))) ## inner arc [xT_6, yT_6] = arcMidPoint([xT_4, yT_4], [xT_5, yT_5], curve) wir.append(Part.Arc(FreeCAD.Base.Vector(xT_4, yT_4, 0), FreeCAD.Base.Vector(xT_6, yT_6, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) ## if cap == 'flat': wir.append(PLine(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0))) wir.append(PLine(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) else: #wir.append(PLine(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0))) #wir.append(PLine(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) #start if xs - p1[0] == 0: # vertical line if curve > 0: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: a = (ys - p1[1]) / (xs - p1[0]) if a == 0: # horizontal line if curve > 0: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) pass else: #a = (ys - p1[1]) / (xs - p1[0]) if curve > 0: if a > 0: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: if a > 0: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) wir.append(Part.Arc(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_7, yT_7, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0))) #end #b = (ys - p2[1]) / (xs - p2[0]) if curve > 0: if xT_2 > xs: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) else: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: if xT_2 > xs: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) wir.append(Part.Arc(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_8, yT_8, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) #### mainObj = Part.Shape(wir) mainObj = Part.Wire(mainObj.Edges) return makeFace(mainObj) except Exception as e: FreeCAD.Console.PrintWarning(u"{0}\n".format(e)) ### ### def createArc(p1, p2, curve, width=0.02, cap='round'): try: #wir = [] # create edges edges = [] if width <= 0: width = 0.02 width /= 2. [x3, y3] = arcMidPoint(p1, p2, curve) [xs, ys] = arcCenter(p1[0], p1[1], p2[0], p2[1], x3, y3) ## #a = (ys - p1[1]) / (xs - p1[0]) [xT_1, yT_1] = shiftPointOnLine(p1[0], p1[1], xs, ys, width) [xT_4, yT_4] = shiftPointOnLine(p1[0], p1[1], xs, ys, -width) ### [xT_2, yT_2] = rotPoint2([xT_1, yT_1], [xs, ys], curve) [xT_5, yT_5] = rotPoint2([xT_4, yT_4], [xs, ys], curve) ######## ######## wir = [] ## outer arc [xT_3, yT_3] = arcMidPoint([xT_1, yT_1], [xT_2, yT_2], curve) edges.append(Part.Edge(Part.Arc(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_3, yT_3, 0), FreeCAD.Base.Vector(xT_2, yT_2, 0)))) #wir.append(Part.Arc(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_3, yT_3, 0), FreeCAD.Base.Vector(xT_2, yT_2, 0))) ## inner arc [xT_6, yT_6] = arcMidPoint([xT_4, yT_4], [xT_5, yT_5], curve) edges.append(Part.Edge(Part.Arc(FreeCAD.Base.Vector(xT_4, yT_4, 0), FreeCAD.Base.Vector(xT_6, yT_6, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0)))) #wir.append(Part.Arc(FreeCAD.Base.Vector(xT_4, yT_4, 0), FreeCAD.Base.Vector(xT_6, yT_6, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) ## if cap == 'flat': edges.append(Part.Edge(PLine(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0)))) edges.append(Part.Edge(PLine(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0)))) #wir.append(PLine(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0))) #wir.append(PLine(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) else: #wir.append(PLine(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0))) #wir.append(PLine(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) #start if xs - p1[0] == 0: # vertical line if curve > 0: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: a = (ys - p1[1]) / (xs - p1[0]) if a == 0: # horizontal line if curve > 0: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) pass else: #a = (ys - p1[1]) / (xs - p1[0]) if curve > 0: if a > 0: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: if a > 0: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) else: if xT_1 > xs: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], -180) else: [xT_7, yT_7] = arcMidPoint([xT_1, yT_1], [xT_4, yT_4], 180) edges.append(Part.Edge(Part.Arc(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_7, yT_7, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0)))) #wir.append(Part.Arc(FreeCAD.Base.Vector(xT_1, yT_1, 0), FreeCAD.Base.Vector(xT_7, yT_7, 0), FreeCAD.Base.Vector(xT_4, yT_4, 0))) #end #b = (ys - p2[1]) / (xs - p2[0]) if curve > 0: if xT_2 > xs: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) else: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: if xT_2 > xs: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: if xT_2 >= xT_5: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], 180) else: [xT_8, yT_8] = arcMidPoint([xT_2, yT_2], [xT_5, yT_5], -180) edges.append(Part.Edge(Part.Arc(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_8, yT_8, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0)))) #wir.append(Part.Arc(FreeCAD.Base.Vector(xT_2, yT_2, 0), FreeCAD.Base.Vector(xT_8, yT_8, 0), FreeCAD.Base.Vector(xT_5, yT_5, 0))) #### sortedEdges = Part.__sortEdges__(edges) wire = Part.Wire(sortedEdges) return Part.Face(wire) # Part.show(wObj) # mainObj = Part.Shape(wir) # mainObj = Part.Wire(mainObj.Edges) # return makeFace(mainObj) except Exception as e: FreeCAD.Console.PrintWarning(u"{0}\n".format(e)) ### ### def addArc_3(p1, p2, curve, width=0, cap='round'): #print curve, ' arc angle' if abs(curve) == 360: #print p1 #print p2 r = sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) #print r return createCircle(p1[0], p1[1], r, width) else: return createArc(p1, p2, curve, width, cap) ### def addLine_2(x1, y1, x2, y2, width=0.01): if x1 == x2 and y1 == y2: return makePoint(x1, y1, 0) else: return createLine(x1, y1, x2, y2, width) ### def addCircle_2(x, y, r, w=0): return createCircle(x, y, r, w) ### def createLine(x1, y1, x2, y2, width=0.01): #say("create line routine") z_silk_offset=0.01 if width <= 0: width = 0.01 # line length length = sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) # angle of inclination if x1 > x2: iang = degrees(atan2(y1 - y2, x1 - x2)) - 90 else: iang = degrees(atan2(y2 - y1, x2 - x1)) - 90 if x1 > x2: iang += 180 # radius of curvature at both ends of the path r = width / 2. # create edges edges = [] edges.append(Part.Edge(PLine(FreeCAD.Base.Vector(0 - r, 0, 0), FreeCAD.Base.Vector(0 - r, length, 0)))) edges.append(Part.Edge(PLine(FreeCAD.Base.Vector(0 + r, 0, 0), FreeCAD.Base.Vector(0 + r, length, 0)))) # create wire #wir = [] #wir.append(PLine(FreeCAD.Base.Vector(0 - r, 0, 0), FreeCAD.Base.Vector(0 - r, length, 0))) #wir.append(PLine(FreeCAD.Base.Vector(0 + r, 0, 0), FreeCAD.Base.Vector(0 + r, length, 0))) p1 = [0 - r, 0] p2 = [0, 0 - r] p3 = [0 + r, 0] edges.append(Part.Edge(Part.Arc(FreeCAD.Base.Vector(p1[0], p1[1], 0), FreeCAD.Base.Vector(p2[0], p2[1], 0), FreeCAD.Base.Vector(p3[0], p3[1], 0)))) #wir.append(Part.Arc(FreeCAD.Base.Vector(p1[0], p1[1], 0), FreeCAD.Base.Vector(p2[0], p2[1], 0), FreeCAD.Base.Vector(p3[0], p3[1], 0))) p1 = [0 - r, length] p2 = [0, length + r] p3 = [0 + r, length] edges.append(Part.Edge(Part.Arc(FreeCAD.Base.Vector(p1[0], p1[1], 0), FreeCAD.Base.Vector(p2[0], p2[1], 0), FreeCAD.Base.Vector(p3[0], p3[1], 0)))) #wir.append(Part.Arc(FreeCAD.Base.Vector(p1[0], p1[1], 0), FreeCAD.Base.Vector(p2[0], p2[1], 0), FreeCAD.Base.Vector(p3[0], p3[1], 0))) sortedEdges = Part.__sortEdges__(edges) #mainObj = Part.Shape(wir) ##mainObj = wir.toShape() ## sayw(wir) #mainObj = Part.Wire(mainObj.Edges) ##mainObj = Part.Face(mainObj) #mainObj=makeFace(Part.Wire(mainObj)) ##mainObj = Part.Wire(wir) ##mainObj = Part.Face(mainObj) wire = Part.Wire(sortedEdges) mainObj = Part.Face(wire) #Part.show(mainObj) pos_1 = FreeCAD.Base.Vector(x1, y1, z_silk_offset) #z offset Front Silk 0.1 center = FreeCAD.Base.Vector(0, 0, 0) rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), iang) mainObj.Placement = FreeCAD.Base.Placement(pos_1, rot, center) #Part.show(mainObj) #stop return mainObj ### def addPadLong2(x, y, dx, dy, perc, typ, z_off, type=None, ratio=None): #pad center x,y pad dimension dx,dy, type, z offset #if ratio is not None: # sayw(type);sayerr(ratio) dx=dx/2. dy=dy/2. curve = 90. if typ == 0: # % if perc > 100.: perc == 100. if dx > dy: e = dy * perc / 100. else: e = dx * perc / 100. else: # mm e = perc if ratio is not None: #rratio=r1/min(sx,sy) e = float(ratio) * 2.0 * min(dx,dy) #sayerr(e) p1 = [x - dx + e, y - dy, z_off] p2 = [x + dx - e, y - dy, z_off] p3 = [x + dx, y - dy + e, z_off] p4 = [x + dx, y + dy - e, z_off] p5 = [x + dx - e, y + dy, z_off] p6 = [x - dx + e, y + dy, z_off] p7 = [x - dx, y + dy - e, z_off] p8 = [x - dx, y - dy + e, z_off] # points = [] if p1 != p2: points.append(PLine(FreeCAD.Base.Vector(p1[0], p1[1], z_off), FreeCAD.Base.Vector(p2[0], p2[1], z_off))) if p2 != p3: p9 = arcMidPoint(p2, p3, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p2[0], p2[1], z_off), FreeCAD.Base.Vector(p9[0], p9[1], z_off), FreeCAD.Base.Vector(p3[0], p3[1], z_off))) if p3 != p4: points.append(PLine(FreeCAD.Base.Vector(p3[0], p3[1], z_off), FreeCAD.Base.Vector(p4[0], p4[1], z_off))) if p4 != p5: p10 = arcMidPoint(p4, p5, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p4[0], p4[1], z_off), FreeCAD.Base.Vector(p10[0], p10[1], z_off), FreeCAD.Base.Vector(p5[0], p5[1], z_off))) if p5 != p6: points.append(PLine(FreeCAD.Base.Vector(p5[0], p5[1], z_off), FreeCAD.Base.Vector(p6[0], p6[1], z_off))) if p6 != p7: p11 = arcMidPoint(p6, p7, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p6[0], p6[1], z_off), FreeCAD.Base.Vector(p11[0], p11[1], z_off), FreeCAD.Base.Vector(p7[0], p7[1], z_off))) if p7 != p8: points.append(PLine(FreeCAD.Base.Vector(p7[0], p7[1], z_off), FreeCAD.Base.Vector(p8[0], p8[1], z_off))) if p8 != p1: p12 = arcMidPoint(p8, p1, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p8[0], p8[1], z_off), FreeCAD.Base.Vector(p12[0], p12[1], z_off), FreeCAD.Base.Vector(p1[0], p1[1], z_off))) if dx==dy and type != "rect" and type != "roundrect": # "circle" r=dx obj=[Part.Circle(FreeCAD.Vector(x, y, z_off), FreeCAD.Vector(0, 0, 1), r).toShape()] obj=Part.Wire(obj) #maui evaluate FC0.17 #obj=Draft.makeWire(obj,closed=True,face=False,support=None) # create the wire #Part.show(obj) #stop else: # obj = Part.Shape(points) #maui evaluate FC0.17 # #obj=[points.toShape()] # obj = Part.Wire(obj.Edges) #maui evaluate FC0.17 objs=[] # asm3 Links compatible way for e in points: objs.append(e.toShape()) obj = Part.Wire(objs) #obj=Draft.makeWire(obj.Edges,closed=True,face=False,support=None) # create the wire #if hole==0: # obj = makeFace(obj) ###return makeFace(obj) ##list=[] ##list.append(obj) ##obj1=Part.makeCompound(list) ##return obj1 return obj ### def addPadLong(x, y, dx, dy, perc, typ, z_off): # center x,y dimension x,y, type, z offset dx=dx/2 dy=dy/2 curve = 90. if typ == 0: # % if perc > 100.: perc == 100. if dx > dy: e = dy * perc / 100. else: e = dx * perc / 100. else: # mm e = perc p1 = [x - dx + e, y - dy, z_off] p2 = [x + dx - e, y - dy, z_off] p3 = [x + dx, y - dy + e, z_off] p4 = [x + dx, y + dy - e, z_off] p5 = [x + dx - e, y + dy, z_off] p6 = [x - dx + e, y + dy, z_off] p7 = [x - dx, y + dy - e, z_off] p8 = [x - dx, y - dy + e, z_off] # points = [] if p1 != p2: points.append(PLine(FreeCAD.Base.Vector(p1[0], p1[1], z_off), FreeCAD.Base.Vector(p2[0], p2[1], z_off))) if p2 != p3: p9 = arcMidPoint(p2, p3, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p2[0], p2[1], z_off), FreeCAD.Base.Vector(p9[0], p9[1], z_off), FreeCAD.Base.Vector(p3[0], p3[1], z_off))) if p3 != p4: points.append(PLine(FreeCAD.Base.Vector(p3[0], p3[1], z_off), FreeCAD.Base.Vector(p4[0], p4[1], z_off))) if p4 != p5: p10 = arcMidPoint(p4, p5, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p4[0], p4[1], z_off), FreeCAD.Base.Vector(p10[0], p10[1], z_off), FreeCAD.Base.Vector(p5[0], p5[1], z_off))) if p5 != p6: points.append(PLine(FreeCAD.Base.Vector(p5[0], p5[1], z_off), FreeCAD.Base.Vector(p6[0], p6[1], z_off))) if p6 != p7: p11 = arcMidPoint(p6, p7, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p6[0], p6[1], z_off), FreeCAD.Base.Vector(p11[0], p11[1], z_off), FreeCAD.Base.Vector(p7[0], p7[1], z_off))) if p7 != p8: points.append(PLine(FreeCAD.Base.Vector(p7[0], p7[1], z_off), FreeCAD.Base.Vector(p8[0], p8[1], z_off))) if p8 != p1: p12 = arcMidPoint(p8, p1, curve) points.append(Part.Arc(FreeCAD.Base.Vector(p8[0], p8[1], z_off), FreeCAD.Base.Vector(p12[0], p12[1], z_off), FreeCAD.Base.Vector(p1[0], p1[1], z_off))) if dx==dy: # "circle" r=dx obj=[Part.Circle(FreeCAD.Vector(x, y, z_off), FreeCAD.Vector(0, 0, 1), r).toShape()] obj=Part.Wire(obj) else: # obj = Part.Shape(points) # obj = Part.Wire(obj.Edges) objs=[] # asm3 Links compatible way for e in points: objs.append(e.toShape()) obj = Part.Wire(objs) obj = makeFace(obj) #return makeFace(obj) list=[] list.append(obj) obj1=Part.makeCompound(list) return obj1 ### def cutHole2(mainObj, holep, holed): if holed[1] > min_val: #hole = [Part.Circle(FreeCAD.Vector(hole[0], hole[1]), FreeCAD.Vector(0, 0, 1), hole[2]).toShape()] z_off=0 hole = addPadLong(holep[0], holep[1], holed[0], holed[1], 100, 0, z_off) mainObj = mainObj.cut(hole) Part.show(mainObj) return mainObj ### ### def createPad2(x,y,sx,sy,dcx,dcy,dx,dy,type,layer): ##pad pos x,y; pad size x,y; drillcenter x,y; drill size x,y, layer z_offset=0 remove=1 if type=="oval": perc=100 tp=0 else: perc=0 tp=0 if layer=="top": thick=-0.01 z_offset=0 else: thick=0.01 z_offset=-1.6 #say(str(x)+"x "+str(y)+"y "+str(sx)+"sx "+str(sy)+"sy ") #say(str(dcx)+"dcx "+str(dcy)+"dcy "+str(dx)+"dx "+str(dy)+"dy ") mypad=addPadLong2(x, y, sx, sy, perc, tp, z_offset) Part.show(mypad) FreeCAD.ActiveDocument.ActiveObject.Label="mypad" pad_name=FreeCAD.ActiveDocument.ActiveObject.Name if dx!=0: perc=100 #drill always oval tp=0 mydrill=addPadLong2(dcx, dcy, dx, dy, perc, tp, 0) Part.show(mydrill) FreeCAD.ActiveDocument.ActiveObject.Label="mydrill" drill_name=FreeCAD.ActiveDocument.ActiveObject.Name myannular=addPadLong2(dcx, dcy, dx+0.01, dy+0.01, perc, tp, 0) Part.show(myannular) FreeCAD.ActiveDocument.ActiveObject.Label="myannular" ann_name=FreeCAD.ActiveDocument.ActiveObject.Name #myhole=addPadLong2(dcx, dcy, dx, dy, perc, tp, z_offset) #Part.show(myhole) #FreeCAD.ActiveDocument.ActiveObject.Label="myhole" # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass wire = [mypad,mydrill] wire2 = [myannular,mydrill] face = Part.Face(wire) face2 = Part.Face(mydrill) face3 = Part.Face(wire2) extr = face.extrude(FreeCAD.Vector(0,0,-.01)) Part.show(extr) FreeCAD.ActiveDocument.ActiveObject.Label="drilled_pad" extr2 = face2.extrude(FreeCAD.Vector(0,0,-1.58)) Part.show(extr2) FreeCAD.ActiveDocument.ActiveObject.Label="hole" extr3 = face3.extrude(FreeCAD.Vector(0,0,-1.58)) Part.show(extr3) FreeCAD.ActiveDocument.ActiveObject.Label="annular" FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.removeObject(drill_name) FreeCAD.ActiveDocument.removeObject(ann_name) FreeCAD.ActiveDocument.recompute() else: face = Part.Face(mypad) extr = face.extrude(FreeCAD.Vector(0,0,-.01)) Part.show(extr) FreeCAD.ActiveDocument.ActiveObject.Label="smd_pad" FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() return extr ### def createPad(x,y,sx,sy,dcx,dcy,dx,dy,type,layer): ##pad pos x,y; pad size x,y; drillcenter x,y; drill size x,y z_offset=0 remove=1 if type=="oval": perc=100 tp=0 else: perc=0 tp=0 if layer=="top": thick=-0.01 z_offset=0 else: thick=0.01 z_offset=-1.6 #say(str(x)+"x "+str(y)+"y "+str(sx)+"sx "+str(sy)+"sy ") #say(str(dcx)+"dcx "+str(dcy)+"dcy "+str(dx)+"dx "+str(dy)+"dy ") mypad=addPadLong(x, y, sx, sy, perc, tp, z_offset) Part.show(mypad) FreeCAD.ActiveDocument.ActiveObject.Label="mypad" pad_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.addObject("Part::Extrusion","Extrude_pad") extrude_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.Extrude_pad.Base = FreeCAD.ActiveDocument.getObject(pad_name) FreeCAD.ActiveDocument.Extrude_pad.Dir = (0,0,thick) FreeCAD.ActiveDocument.Extrude_pad.Solid = (True) FreeCAD.ActiveDocument.Extrude_pad.TaperAngle = (0) FreeCADGui.ActiveDocument.getObject(pad_name).Visibility = False FreeCAD.ActiveDocument.Extrude_pad.Label = 'mypad_solid' extrude_pad_name=FreeCAD.ActiveDocument.ActiveObject.Name #FreeCAD.ActiveDocument.recompute() if dx!=0: perc=100 #drill always oval mydrill=addPadLong(dcx, dcy, dx, dy, perc, tp, z_offset) # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass Part.show(mydrill) FreeCAD.ActiveDocument.ActiveObject.Label="mydrill" drill_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.addObject("Part::Extrusion","Extrude_d") extrude_d_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.Extrude_d.Base = FreeCAD.ActiveDocument.getObject(drill_name) FreeCAD.ActiveDocument.Extrude_d.Dir = (0,0,thick) FreeCAD.ActiveDocument.Extrude_d.Solid = (True) FreeCAD.ActiveDocument.Extrude_d.TaperAngle = (0) FreeCADGui.ActiveDocument.getObject(drill_name).Visibility = False FreeCAD.ActiveDocument.Extrude_d.Label = 'mydrill_solid' extrude_drill_name=FreeCAD.ActiveDocument.ActiveObject.Name #FreeCAD.ActiveDocument.recompute() FreeCAD.activeDocument().addObject("Part::Cut","myCut") cut_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.activeDocument().getObject(cut_name).Base = FreeCAD.activeDocument().Extrude_pad FreeCAD.activeDocument().getObject(cut_name).Tool = FreeCAD.activeDocument().Extrude_d FreeCADGui.activeDocument().Extrude_pad.Visibility=False FreeCADGui.activeDocument().Extrude_d.Visibility=False #FreeCADGui.ActiveDocument.getObject(cut_name).ShapeColor=FreeCADGui.ActiveDocument.Extrude.ShapeColor FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCADGui.ActiveDocument.ActiveObject.DisplayMode=FreeCADGui.ActiveDocument.Extrude_pad.DisplayMode FreeCAD.ActiveDocument.recompute() pad_d_name="TH_Pad" FreeCAD.ActiveDocument.addObject('Part::Feature',pad_d_name).Shape=FreeCAD.ActiveDocument.ActiveObject.Shape FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) myObj=FreeCAD.ActiveDocument.getObject(pad_d_name) if remove==1: FreeCAD.ActiveDocument.removeObject(cut_name) FreeCAD.ActiveDocument.removeObject(extrude_pad_name) FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.removeObject(drill_name) FreeCAD.ActiveDocument.removeObject(extrude_drill_name) FreeCAD.ActiveDocument.recompute() else: FreeCAD.ActiveDocument.recompute() pad_d_name="smdPad" FreeCAD.ActiveDocument.addObject('Part::Feature',pad_d_name).Shape=FreeCAD.ActiveDocument.ActiveObject.Shape myObj=FreeCAD.ActiveDocument.getObject(pad_d_name) FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCAD.ActiveDocument.removeObject(extrude_pad_name) FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() return myObj ### def createPad3(x,y,sx,sy,dcx,dcy,dx,dy,type,layer, ratio=None): ##pad pos x,y; pad size x,y; drillcenter x,y; drill size x,y, type, layer, rratio z_offset=0 remove=1 if type=="oval" or type=="circle": perc=100 tp=0 else: perc=0 tp=0 if layer=="top": thick=-0.01 z_offset=0 else: thick=0.01 z_offset=-1.6 #say(str(x)+"x "+str(y)+"y "+str(sx)+"sx "+str(sy)+"sy ") #say(str(dcx)+"dcx "+str(dcy)+"dcy "+str(dx)+"dx "+str(dy)+"dy ") mypad=addPadLong2(x, y, sx, sy, perc, tp, z_offset, type, ratio) Part.show(mypad) FreeCAD.ActiveDocument.ActiveObject.Label="mypad" pad_name=FreeCAD.ActiveDocument.ActiveObject.Name if dx!=0: perc=100 #drill always oval tp=0 mydrill=addPadLong2(dcx, dcy, dx, dy, perc, tp, z_offset) # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass if test_flag_pads==True: Part.show(mydrill) FreeCAD.ActiveDocument.ActiveObject.Label="mydrill" drill_name=FreeCAD.ActiveDocument.ActiveObject.Name myannular=addPadLong2(dcx, dcy, dx+0.01, dy+0.01, perc, tp, z_offset) if test_flag_pads==True: Part.show(myannular) FreeCAD.ActiveDocument.ActiveObject.Label="myannular" ann_name=FreeCAD.ActiveDocument.ActiveObject.Name myhole=addPadLong2(dcx, dcy, dx, dy, perc, tp, z_offset) if test_flag_pads==True: Part.show(myhole) FreeCAD.ActiveDocument.ActiveObject.Label="myhole" wire = [mypad,mydrill] face = Part.Face(wire) extr = face.extrude(FreeCAD.Vector(0,0,thick)) if test_flag_pads==True: Part.show(extr) FreeCAD.ActiveDocument.ActiveObject.Label="drilled_pad" #Part.show(extr) FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() else: face = Part.Face(mypad) extr = face.extrude(FreeCAD.Vector(0,0,thick)) #Part.show(extr) #FreeCAD.ActiveDocument.ActiveObject.Label="smd_pad" FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() return extr ### def createPad(x,y,sx,sy,dcx,dcy,dx,dy,type,layer): ##pad pos x,y; pad size x,y; drillcenter x,y; drill size x,y z_offset=0 remove=1 if type=="oval": perc=100 tp=0 else: perc=0 tp=0 if layer=="top": thick=-0.01 z_offset=0 else: thick=0.01 z_offset=-1.6 #say(str(x)+"x "+str(y)+"y "+str(sx)+"sx "+str(sy)+"sy ") #say(str(dcx)+"dcx "+str(dcy)+"dcy "+str(dx)+"dx "+str(dy)+"dy ") mypad=addPadLong(x, y, sx, sy, perc, tp, z_offset) Part.show(mypad) FreeCAD.ActiveDocument.ActiveObject.Label="mypad" pad_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.addObject("Part::Extrusion","Extrude_pad") extrude_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.Extrude_pad.Base = FreeCAD.ActiveDocument.getObject(pad_name) FreeCAD.ActiveDocument.Extrude_pad.Dir = (0,0,thick) FreeCAD.ActiveDocument.Extrude_pad.Solid = (True) FreeCAD.ActiveDocument.Extrude_pad.TaperAngle = (0) FreeCADGui.ActiveDocument.getObject(pad_name).Visibility = False FreeCAD.ActiveDocument.Extrude_pad.Label = 'mypad_solid' extrude_pad_name=FreeCAD.ActiveDocument.ActiveObject.Name #FreeCAD.ActiveDocument.recompute() if dx!=0: perc=100 #drill always oval mydrill=addPadLong(dcx, dcy, dx, dy, perc, tp, z_offset) # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass Part.show(mydrill) FreeCAD.ActiveDocument.ActiveObject.Label="mydrill" drill_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.addObject("Part::Extrusion","Extrude_d") extrude_d_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.Extrude_d.Base = FreeCAD.ActiveDocument.getObject(drill_name) FreeCAD.ActiveDocument.Extrude_d.Dir = (0,0,thick) FreeCAD.ActiveDocument.Extrude_d.Solid = (True) FreeCAD.ActiveDocument.Extrude_d.TaperAngle = (0) FreeCADGui.ActiveDocument.getObject(drill_name).Visibility = False FreeCAD.ActiveDocument.Extrude_d.Label = 'mydrill_solid' extrude_drill_name=FreeCAD.ActiveDocument.ActiveObject.Name #FreeCAD.ActiveDocument.recompute() FreeCAD.activeDocument().addObject("Part::Cut","myCut") cut_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.activeDocument().getObject(cut_name).Base = FreeCAD.activeDocument().Extrude_pad FreeCAD.activeDocument().getObject(cut_name).Tool = FreeCAD.activeDocument().Extrude_d FreeCADGui.activeDocument().Extrude_pad.Visibility=False FreeCADGui.activeDocument().Extrude_d.Visibility=False #FreeCADGui.ActiveDocument.getObject(cut_name).ShapeColor=FreeCADGui.ActiveDocument.Extrude.ShapeColor FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCADGui.ActiveDocument.ActiveObject.DisplayMode=FreeCADGui.ActiveDocument.Extrude_pad.DisplayMode FreeCAD.ActiveDocument.recompute() pad_d_name="TH_Pad" FreeCAD.ActiveDocument.addObject('Part::Feature',pad_d_name).Shape=FreeCAD.ActiveDocument.ActiveObject.Shape FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) myObj=FreeCAD.ActiveDocument.getObject(pad_d_name) if remove==1: FreeCAD.ActiveDocument.removeObject(cut_name) FreeCAD.ActiveDocument.removeObject(extrude_pad_name) FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.removeObject(drill_name) FreeCAD.ActiveDocument.removeObject(extrude_drill_name) FreeCAD.ActiveDocument.recompute() else: FreeCAD.ActiveDocument.recompute() pad_d_name="smdPad" FreeCAD.ActiveDocument.addObject('Part::Feature',pad_d_name).Shape=FreeCAD.ActiveDocument.ActiveObject.Shape myObj=FreeCAD.ActiveDocument.getObject(pad_d_name) FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCAD.ActiveDocument.removeObject(extrude_pad_name) FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() return myObj ### def createHole(x,y,dx,dy,type): if type=="oval": perc=100 tp=0 else: perc=0 tp=0 mydrill=addPadLong(x, y, dx, dy, perc, tp, 0) Part.show(mydrill) FreeCAD.ActiveDocument.ActiveObject.Label="mydrill" drill_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.addObject("Part::Extrusion","Extrude_d") FreeCAD.ActiveDocument.Extrude_d.Base = FreeCAD.ActiveDocument.getObject(drill_name) FreeCAD.ActiveDocument.Extrude_d.Dir = (0,0,-1.6) FreeCAD.ActiveDocument.Extrude_d.Solid = (True) FreeCAD.ActiveDocument.Extrude_d.TaperAngle = (0) FreeCADGui.ActiveDocument.getObject(drill_name).Visibility = False FreeCAD.ActiveDocument.Extrude_d.Label = 'mydrill_hole' extrude_hole_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.recompute() hole_name="hole" FreeCAD.ActiveDocument.addObject('Part::Feature',hole_name).Shape=FreeCAD.ActiveDocument.getObject(extrude_hole_name).Shape FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.67,1.00,0.50) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 70 myObj=FreeCADGui.ActiveDocument.ActiveObject FreeCAD.ActiveDocument.removeObject(drill_name) FreeCAD.ActiveDocument.removeObject(extrude_hole_name) FreeCAD.ActiveDocument.recompute() return myObj ### def createHole2(x,y,dx,dy,type): if type=="oval" or type=="circle": perc=100 tp=0 else: perc=0 tp=0 #mydrill=addPadLong(x, y, dx, dy, perc, tp, -0.01) mydrill=addPadLong(x, y, dx, dy, perc, tp, .01) # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass #Part.show(mydrill) #hole = mydrill.extrude(FreeCAD.Base.Vector(0, 0, -1.58)) hole = mydrill.extrude(FreeCAD.Base.Vector(0, 0, -1.61)) holeModel=[] holeModel.append(hole) holeModel = Part.makeCompound(holeModel) #say("hereHole") #FreeCAD.ActiveDocument.recompute() return holeModel ### def createHole3(x,y,dx,dy,type,height): if type=="oval": perc=100 tp=0 else: perc=0 tp=0 #mydrill=addPadLong(x, y, dx, dy, perc, tp, -0.01) mydrill=addPadLong(x, y, dx, dy, perc, tp, 0.1) #Part.show(mydrill) #hole = mydrill.extrude(FreeCAD.Base.Vector(0, 0, -1.58)) hole = mydrill.extrude(FreeCAD.Base.Vector(0, 0, -(height+0.2))) holeModel=[] holeModel.append(hole) holeModel = Part.makeCompound(holeModel) #say("hereHole") #FreeCAD.ActiveDocument.recompute() return holeModel ### ### def createHole4(x,y,dx,dy,type): global show_shapes if type=="oval": perc=100 tp=0 else: perc=0 tp=0 #mydrill=addPadLong(x, y, dx, dy, perc, tp, -0.01) #mydrill=addPadLong(x, y, dx, dy, perc, tp, .01) mydrill=addPadLong2(x, y, dx, dy, perc, tp, 0) holeModel=[] #holeModel.append(hole) #holeModel.append(mydrill) ##holeModel = Part.makeCompound(holeModel) #holeModel = Part.Face(holeModel) face = OSCD2Dg_edgestofaces(mydrill.Edges,3 , edge_tolerance) #algo 0 #face = OSCD2Dg_edgestofaces(mydrill.Edges,3 , edge_tolerance) face.fix(0,0,0) if show_shapes: Part.show(face) holeModel = face #sayerr('x'+str(x)+' y'+str(y)+' dx'+str(dx)+' dy'+str(dy)+' perc'+str(perc)+' tp'+str(tp)+'0') #stop #say("hereHole") #FreeCAD.ActiveDocument.recompute() #return holeModel return Part.makeCompound(holeModel) ### def createTHPlate(x,y,dx,dy,type): if type=="oval" or type=="circle": perc=100 tp=0 else: perc=0 tp=0 #mydrill=addPadLong(x, y, dx, dy, perc, tp, -0.01) mydrill=addPadLong2(x, y, dx, dy, perc, tp, -0.01) # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass myannular=addPadLong2(x, y, dx+0.01, dy+0.01, perc, tp, -0.01) wire2 = [myannular,mydrill] face3 = Part.Face(wire2) THP = face3.extrude(FreeCAD.Vector(0,0,-1.58)) #Part.show(extr3) #FreeCAD.ActiveDocument.ActiveObject.Label="annular" ##hole = mydrill.extrude(FreeCAD.Base.Vector(0, 0, -1.58)) #THP = myannular.extrude(FreeCAD.Base.Vector(0, 0, -1.58)) THPModel=[] THPModel.append(THP) THPModel = Part.makeCompound(THPModel) #say("hereHole") #FreeCAD.ActiveDocument.recompute() return THPModel ### def createGeomC(cx, cy, radius, layer, width): #createGeomC(Gcx, Gcy, GRad,'top', Gw) if layer == 'top': #if top==True: thick=-0.01 z_offset=0 else: thick=0.01 z_offset=-1.6 bv = Base.Vector circ = Part.makeCircle(radius+width/2, bv(cx,cy,z_offset)) mw = Part.Wire(circ.Edges) myp= Part.Face(mw) #Part.show(mypad) circ = Part.makeCircle(radius-width/2, bv(cx,cy,z_offset)) mw2 = Part.Wire(circ.Edges) myp2 = Part.Face(mw2) mypad=myp.cut(myp2) Part.show(mypad) FreeCAD.ActiveDocument.ActiveObject.Label="mypad" pad_name=FreeCAD.ActiveDocument.ActiveObject.Name # face = Part.Face(mypad) # Part.show(face) extr = mypad.extrude(FreeCAD.Vector(0,0,thick)) #Part.show(extr) #FreeCAD.ActiveDocument.ActiveObject.Label="smd_pad" FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() #extr = sface.extrude(FreeCAD.Vector(0,0,-.01)) #Part.show(extr) #stop return extr def createPoly(x, y, sx, sy, dcx,dcy,dx,dy,pShape, layer, poly_points): #createPad3(x1, y1, dx, dy, xs, ys,rx,ry,pShape,'top') #createPad3(x, y, sx,sy, dcx,dcy,dx,dy,type,layer): pts=[] bv = Base.Vector p0 = poly_points[0].split(' ') if layer == 'top': #if top==True: thick=-0.01 z_offset=0 else: thick=0.01 z_offset=-1.6 for p in poly_points: pc = p.split(' ') pts.append(bv(float(pc[1])+x,-1*float(pc[2][0:pc[2].index(')')])+y,z_offset)) # print (float(pc[1])+x1,-1*float(pc[2][0:pc[2].index(')')])+y1,z_offset) # closing poly pts.append(bv(float(p0[1])+x,-1*float(p0[2][0:p0[2].index(')')])+y,z_offset)) # print (float(p0[1])+x1,-1*float(p0[2][0:p0[2].index(')')])+y1,z_offset) # f = Draft.makeWire(pts,closed=True) # obj=f.Shape.copy() lshape_wire = Part.makePolygon(pts) mypad = Part.Face(lshape_wire) Part.show(mypad) FreeCAD.ActiveDocument.ActiveObject.Label="mypad" pad_name=FreeCAD.ActiveDocument.ActiveObject.Name if dx!=0: perc=100 #drill always oval tp=0 mydrill=addPadLong2(dcx, dcy, dx, dy, perc, tp, z_offset) # workaround FC 0.17 OCC 7 try: if float(Part.OCC_VERSION.split('.')[0]) >= 7: mydrill.reverse() except: pass if test_flag_pads==True: Part.show(mydrill) FreeCAD.ActiveDocument.ActiveObject.Label="mydrill" drill_name=FreeCAD.ActiveDocument.ActiveObject.Name myannular=addPadLong2(dcx, dcy, dx+0.01, dy+0.01, perc, tp, z_offset) if test_flag_pads==True: Part.show(myannular) FreeCAD.ActiveDocument.ActiveObject.Label="myannular" ann_name=FreeCAD.ActiveDocument.ActiveObject.Name myhole=addPadLong2(dcx, dcy, dx, dy, perc, tp, z_offset) if test_flag_pads==True: Part.show(myhole) FreeCAD.ActiveDocument.ActiveObject.Label="myhole" #wire = [mypad,mydrill] wire = [mydrill] drl = Part.Face(wire) #Part.show(drl) face = mypad.cut(drl) extr = face.extrude(FreeCAD.Vector(0,0,thick)) #Part.show(extr);stop if test_flag_pads==True: Part.show(extr) FreeCAD.ActiveDocument.ActiveObject.Label="drilled_pad" FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() else: face = Part.Face(mypad) extr = face.extrude(FreeCAD.Vector(0,0,thick)) #Part.show(extr) #FreeCAD.ActiveDocument.ActiveObject.Label="smd_pad" FreeCAD.ActiveDocument.removeObject(pad_name) FreeCAD.ActiveDocument.recompute() #extr = sface.extrude(FreeCAD.Vector(0,0,-.01)) #Part.show(extr) #stop return extr ### def createArcW (layer, content, arc_prim, layer_list): fpArc = getArc(layer, content, arc_prim) #print(layer,fpArc) for i in fpArc: if len(i) == 6: #kv5 x1 = i[0] #+ X1 y1 = i[1] #+ Y1 x2 = i[2] #+ X1 y2 = i[3] #+ Y1 _angle = i[4] # print('center=',x1,y1,' end=',x2,y2,' angle=',i[4],' width=',i[5]) arc1=addArc_3([x1, y1], [x2, y2], i[4], i[5]) # kv5 fp_arc start=center coord, end=arc_end coord, angle=delta angle # getArc -> [x2, y2] = rotPoint2([x1, y1], [xc, yc], curve) xm=(x1+x2)/2 ym=(y1+y2)/2 rotateObj(arc1, [xm, ym, 180]) layer_list.append(arc1) elif len(i) == 7: #kv6 # kv6 fp_arc start=arc_start coord, mid=arc_mid coord, end=arc_end coord # start, mid, end xs = i[0]; ys = i[1] #+ Y1 xm = i[2]; ym = i[3] #+ Y1 xe = i[4]; ye = i[5] #+ Y1 edge_arc = Part.ArcOfCircle(kicad_parser.makeVect([xs,-ys]), kicad_parser.makeVect([xm,-ym]), kicad_parser.makeVect([xe,-ye])).toShape() #Draft.makeSketch(edge_arc) # e => arc Edge delta_angle=degrees(edge_arc.LastParameter-edge_arc.FirstParameter) center = edge_arc.Curve.Center xc,yc,zc= center[0],center[1],center[2] #print (center); print(delta_angle) # arc1_kv5=addArc_3([x1, y1], [x2, y2], i[4], i[5]) # kv5 fp_arc start=center coord, end=arc_end coord, angle=delta angle [x1,y1] = [xe, ye] [x2,y2] = [xs,ys] #rotPoint2([x1, y1], [xs, ys], -curve) # if curve <0: # curve*=-1 #if curve >90: # curve=(curve-90)/4 # arc1 = addArc_3([x1, y1], [x2, y2], curve, i[6]) arc1 = addArc_3([x1, y1], [x2, y2], delta_angle, i[6]) #arc1 = addArc_3([center[0], -center[1]], [x2, y2], -curve+180/2, i[6]) xm=(x1+x2)/2 ym=(y1+y2)/2 #rotateObj(arc1, [xm, ym, 180]) #.rotate(Vector(),Vector(0,0,1),180) layer_list.append(arc1) #else: # print(len(i),str(i)) #if mksketch: # skt = Draft.makeSketch(arc1) ### def routineDrawFootPrint(content,name): global rot_wrl, zfit #for item in content: # say(item) # x1, y1, x2, y2, width say("FootPrint Loader "+name) footprint_name=getModName(content) rot_wrl=getwrlRot(content) posiz, scale, rot = getwrlData(content) #say(posiz);say(scale);say(rot); error_mod=False #if scale!=['1', '1', '1']: xsc_vrml_val=scale[0] ysc_vrml_val=scale[1] zsc_vrml_val=scale[2] # if scale_vrml!='1 1 1': #sayw(scale) if float(xsc_vrml_val)!=1.0 or float(ysc_vrml_val)!=1.0 or float(zsc_vrml_val)!=1.0: sayw('wrong scale!!! set scale to (1 1 1)\n') error_mod=True if posiz!=['0', '0', '0']: sayw('wrong xyx position!!! set xyz to (0 0 0)\n') error_mod=True if rot[0]!='0' or rot[1]!='0': sayw('wrong rotation!!! set rotate x and y to (0 0 z)\n') error_mod=True if error_mod: msg="""Error in '.kicad_mod' footprint
""" msg+="
reset values to:
" msg+="(at (xyz 0 0 0))
" msg+="(scale (xyz 1 1 1))
" msg+="(rotate (xyz 0 0 z))
" msg+="

Only z rotation is allowed!" reply = QtGui.QMessageBox.information(None,"info", msg) #stop #say(footprint_name+" wrl rotation:"+str(rot_wrl)) if FreeCAD.activeDocument(): doc=FreeCAD.activeDocument() else: doc=FreeCAD.newDocument() #doc.UndoMode = 1 #doc.openTransaction() doc.openTransaction('opening_kicad_footprint') say('opening Transaction \'opening_kicad_footprint\'') for obj in FreeCAD.ActiveDocument.Objects: FreeCADGui.Selection.removeSelection(obj) TopPadList=[] BotPadList=[] HoleList=[] THPList=[] TopNetTieList=[] BotNetTieList=[] for pad in getPadsList(content): # sayerr(pad) # # pads.append({'x': x, 'y': y, 'rot': rot, 'padType': pType, 'padShape': pShape, 'rx': drill_x, 'ry': drill_y, 'dx': dx, 'dy': dy, 'holeType': hType, 'xOF': xOF, 'yOF': yOF, 'layers': layers}) pType = pad['padType'] pShape = pad['padShape'] pRratio = pad['rratio'] pGeomC = pad['geomC'] xs = pad['x'] #+ X1 ys = pad['y'] #+ Y1 dx = pad['dx'] dy = pad['dy'] hType = pad['holeType'] drill_x = pad['rx'] drill_y = pad['ry'] xOF = pad['xOF'] yOF = pad['yOF'] rot = pad['rot'] rx=drill_x ry=drill_y numberOfLayers = pad['layers'].replace('"', '').split(' ') # fixing double quotes in layers & pads # print(numberOfLayers) # print('F.Cu' in numberOfLayers) pnts = pad['points'] anchor = pad['anchor'] #if pnts is not None: # sayw(pnts.groups(0)[0].split('(xy')) #sayw(pnts) #say(str(rx)) #say(numberOfLayers) #if pType=="thru_hole": #pad shape - circle/rec/oval/trapezoid perc=0 if pShape=="circle" or pShape=="oval": ##pShape="oval" perc=100 # pad type - SMD/thru_hole/connect #say(pType+"here") if dx>rx and dy>ry: #say(pType) #say(str(dx)+"+"+str(rx)+" dx,rx") #say(str(dy)+"+"+str(ry)+" dy,ry") #say(str(xOF)+"+"+str(yOF)+" xOF,yOF") #def addPadLong(x, y, dx, dy, perc, typ, z_off): #say(str(x1)+"+"+str(y1)+" x1,y1") top=False bot=False if 'F.Cu' in numberOfLayers: top=True if '*.Cu' in numberOfLayers: top=True bot=True if 'B.Cu' in numberOfLayers: bot=True # print(numberOfLayers) pattern = 'In+([0-9]*?).Cu' result = re.search(pattern, str(numberOfLayers)) if result is not None: sayerr('internal layers not supported!') sayw(result.group()) if top==True: x1=xs+xOF y1=ys-yOF #yoffset opposite #mypad=addPadLong(x1, y1, dx, dy, perc, 0, 0) mypad2 = None skip = False if pShape=='custom' and pGeomC is None: #if (pShape=='custom' or pShape=='NetTie') and pGeomC is None: #sayw(pnts.groups(0)[0].split('(xy')) #print(pGeomC) try: poly_points=pnts.groups(0)[0].split('(xy')[1:] mypad=createPoly(x1, y1, dx, dy, xs,ys,rx,ry,pShape,'top', poly_points) if anchor is not None: if anchor[0]=="circle": perc=100 #print 'anchor ',anchor[0] mypad2=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,anchor,'top') #Part.show(mypad2) #print anchor #stop except: sayerr('geometry unsupported') skip = True elif pShape=='custom' and pGeomC is not None: #sayerr(pGeomC) #print('pGeomC',(pGeomC)) #print ('x1',x1,'y1',y1) Gc=pGeomC[0].split(' ') #Gcx=-float(Gc[1])-x1;Gcy=float(Gc[2])-y1 Gr=pGeomC[1].split(' ') GRad=abs(float(Gr[1])-float(Gc[1])) Gcx=x1+float(Gc[1]) Gcy=float(Gc[2])-y1 #print('Gr',Gr,'GR',GRad,'Gc1',Gc[1],'Gc2',Gc[2],'Gcx',Gcx) Gw=pGeomC[2].split(' ') Gw=float(Gw[1]) #print (Gcx,Gcy,GRad,Gw) mypad=createGeomC(Gcx, Gcy, GRad,'top', Gw) if anchor is not None: if anchor[0]=="circle": perc=100 #print 'anchor ',anchor[0] mypad2=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,anchor,'top') #Part.show(mypad2) #print TopPadList #stop else: #mypad=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,pShape,'top') mypad=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,pShape,'top',pRratio) ##pad pos x,y; pad size x,y; drillcenter x,y; drill size x,y, layer obj=mypad if rot != 0 and not skip: rotateObj(obj, [xs, ys, rot]) if mypad2 is not None: rotateObj(mypad2, [xs, ys, rot]) if not skip: TopPadList.append(obj) if mypad2 is not None: TopPadList.append(mypad2) if bot==True: #on Bot layer offset is opposite x1=xs-xOF y1=ys+yOF #yoffset opposite #mypad=addPadLong(x1, y1, dx, dy, perc, 0, -1.6) mypad2=None; skip = False #if pShape=='custom': if pShape=='custom' and pGeomC is None: #sayw(pnts.groups(0)[0].split('(xy')) try: poly_points=pnts.groups(0)[0].split('(xy')[1:] mypad=createPoly(x1, y1, dx, dy, xs,ys,rx,ry,pShape,'bot', poly_points) if anchor is not None: if anchor=="circle": perc=100 mypad2=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,anchor,'bot') except: sayerr('geometry unsupported') skip = True elif pShape=='custom' and pGeomC is not None: #sayerr(pGeomC) #print('pGeomC',(pGeomC)) #print ('x1',x1,'y1',y1) Gc=pGeomC[0].split(' ') #Gcx=-float(Gc[1])-x1;Gcy=float(Gc[2])-y1 Gr=pGeomC[1].split(' ') GRad=abs(float(Gr[1])-float(Gc[1])) Gcx=x1+float(Gc[1]) Gcy=float(Gc[2])-y1 #print('Gr',Gr,'GR',GRad,'Gc1',Gc[1],'Gc2',Gc[2],'Gcx',Gcx) Gw=pGeomC[2].split(' ') Gw=float(Gw[1]) #print (Gcx,Gcy,GRad,Gw) mypad=createGeomC(Gcx, Gcy, GRad,'bot', Gw) if anchor is not None: if anchor[0]=="circle": perc=100 #print 'anchor ',anchor[0] mypad2=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,anchor,'bot') Part.show(mypad2) #stop else: mypad=createPad3(x1, y1, dx, dy, xs,ys,rx,ry,pShape,'bot',pRratio) ##pad pos x,y; pad size x,y; drillcenter x,y; drill size x,y, layerobj=mypad obj=mypad if rot!=0 and not skip: rotateObj(obj, [xs, ys, +rot+180]) #rotateObj(obj, [xs, ys, -rot+180]) if mypad2 is not None: rotateObj(mypad2, [xs, ys, +rot+180]) #rotateObj(mypad2, [xs, ys, -rot+180]) if not skip: BotPadList.append(obj) if mypad2 is not None: BotPadList.append(mypad2) if rx!=0: #obj=createHole2(xs,ys,rx,ry,"oval") #need to be separated instructions #print pShape #stop hole_tp=hType.strip() #sayw(hole_tp) obj=createHole2(xs,ys,rx,ry,hole_tp) #need to be separated instructions ##obj=createHole2(xs,ys,rx,ry,pShape) #need to be separated instructions #say(HoleList) if rot!=0: rotateObj(obj, [xs, ys, rot]) HoleList.append(obj) #obj2=createTHPlate(xs,ys,rx,ry,"oval") obj2=createTHPlate(xs,ys,rx,ry,hole_tp) ##obj2=createTHPlate(xs,ys,rx,ry,pShape) THPList.append(obj2) if rot!=0: rotateObj(obj2, [xs, ys, rot]) #say(pType+"here") ### cmt- #da gestire: pad type trapez FrontSilk = [] FCrtYd = [] FFab = [] EdgeCuts = [] BotSilk = [] BCrtYd = [] BFab = [] layer_names = ['F.SilkS','F.CrtYd','F.Fab','Edge.Cuts','B.SilkS','B.CrtYd','B.Fab'] layers_name_list = [FrontSilk,FCrtYd,FFab,EdgeCuts,BotSilk,BCrtYd,BFab] fp_list,width = getPolyList(content) for fp in fp_list: pnts = fp['points'] layers = fp['layers'] #print(layers) pShape = 'NetTie' skip = False if 'B.Cu' in layers or 'B.Mask' in layers: lyr = 'bot' elif 'F.Cu' in layers: lyr = 'top' elif 'Edge.Cuts' in layers: lyr = 'Edge.Cuts' else: lyr = None sayw('geometry unsupported') if pnts is not None and lyr is not None and lyr != 'Edge.Cuts': # minimal closed shape points #sayw(pnts.groups(0)[0].split('(xy')) #print(pGeomC) try: poly_points=pnts.groups(0)[0].split('(xy')[1:] #print(poly_points) mypad=createPoly(0.0, 0.0, 0.0, 0.0, 0.0,0.0,0.0,0.0,pShape,lyr, poly_points) except: sayerr('geometry unsupported') skip = True if not skip: if lyr == 'top': #TopPadList.append(mypad) TopNetTieList.append(mypad) else: #BotPadList.append(mypad) BotNetTieList.append(mypad) # polyline fp_poly elif lyr == 'Edge.Cuts': #print(pnts.groups(0)[1]) poly_points=pnts.groups(0)[1].split('(xy')[1:] #print(poly_points) for i,p in enumerate (poly_points[:-1]): p=p[1:].split(')')[0].split(' ') p1 = poly_points[i+1][1:].split(')')[0].split(' ') x1 = float(p[0]) #+ X1 y1 = -float(p[1]) #+ Y1 x2 = float(p1[0]) #+ X1 y2 = -float(p1[1]) #+ Y1 obj = addLine_2(x1, y1, x2, y2, 0.12) layers_name_list[3].append(addLine_2(x1, y1, x2, y2, width)) #closing poly p = poly_points[0][1:].split(')')[0].split(' ') x1 = float(p[0]) #+ X1 y1 = -float(p[1]) #+ Y1 obj = addLine_2(x2, y2, x1, y1, width) layers_name_list[3].append(addLine_2(x1, y1, x2, y2, width)) #stop ## #FrontSilk = [] #FCrtYd = [] #FFab = [] #EdgeCuts = [] #BotSilk = [] #BCrtYd = [] #BFab = [] # #layer_names = ['F.SilkS','F.CrtYd','F.Fab','Edge.Cuts','B.SilkS','B.CrtYd','B.Fab'] #layers_name_list = [FrontSilk,FCrtYd,FFab,EdgeCuts,BotSilk,BCrtYd,BFab] #TBD #for n,lay in enumerate (layer_names): # getPolyList # line #getLine('F.SilkS', content, 'fp_line') for n,lay in enumerate (layer_names): for i in getLine(lay, content, 'fp_line'): x1 = i[0] #+ X1 y1 = i[1] #+ Y1 x2 = i[2] #+ X1 y2 = i[3] #+ Y1 obj = addLine_2(x1, y1, x2, y2, i[4]) layers_name_list[n].append(addLine_2(x1, y1, x2, y2, i[4])) for n,lay in enumerate (layer_names): for i in getLine(lay, content, 'fp_rect'): x1 = i[0] #+ X1 y1 = i[1] #+ Y1 x2 = i[2] #+ X1 y2 = i[3] #+ Y1 obj = addLine_2(x1, y1, x2, y2, i[4]) layers_name_list[n].append(addLine_2(x1, y1, x2, y1, i[4])) layers_name_list[n].append(addLine_2(x2, y1, x2, y2, i[4])) layers_name_list[n].append(addLine_2(x2, y2, x1, y2, i[4])) layers_name_list[n].append(addLine_2(x1, y2, x1, y1, i[4])) # circle for n,lay in enumerate (layer_names): for i in getCircle(lay, content, 'fp_circle'): xs = i[0] #+ X1 ys = i[1] #+ Y1 layers_name_list[n].append(addCircle_2(xs, ys, i[2], i[3])) # arc for n,lay in enumerate (layer_names): # print(l,lay,content) arc1 = createArcW (lay, content, 'fp_arc', layers_name_list[n]) if len(FCrtYd)>0: #FSilk_lines = Part.makeCompound(FrontSilk) #Part.show(FSilk_lines) FCrtYd_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","FCrtYd_lines") #FSilk_lines.Label="FSilk_lines" #FSilk_lines_name=FSilk_lines.Name FCrtYd_lines.addProperty("App::PropertyBool","fixedPosition","importPart") FCrtYd_lines.Shape = Part.makeCompound(FCrtYd) #TopPadsBase.Shape.copy() FCrtYd_lines.ViewObject.Proxy=0 FCrtYd_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="FCrtYd" FCrtYd_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.0000,0.0000,1.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 # if len(BCrtYd)>0: BCrtYd_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BCrtYd_lines") BCrtYd_lines.addProperty("App::PropertyBool","fixedPosition","importPart") BCrtYd_lines.Shape = Part.makeCompound(BCrtYd) #TopPadsBase.Shape.copy() BCrtYd_lines.ViewObject.Proxy=0 BCrtYd_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="BCrtYd" BCrtYd_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.0000,0.0000,1.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 FreeCAD.ActiveDocument.ActiveObject.Placement.Base.z-=1.6 # if len(FFab)>0: #FSilk_lines = Part.makeCompound(FrontSilk) #Part.show(FSilk_lines) FFab_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","FFab_lines") #FSilk_lines.Label="FSilk_lines" #FSilk_lines_name=FSilk_lines.Name FFab_lines.addProperty("App::PropertyBool","fixedPosition","importPart") FFab_lines.Shape = Part.makeCompound(FFab) #TopPadsBase.Shape.copy() FFab_lines.ViewObject.Proxy=0 FFab_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="FFab" FFab_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.0000,1.0000,0.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 # if len(BFab)>0: BFab_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BFab_lines") BFab_lines.addProperty("App::PropertyBool","fixedPosition","importPart") BFab_lines.Shape = Part.makeCompound(BFab) #TopPadsBase.Shape.copy() BFab_lines.ViewObject.Proxy=0 BFab_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="BFab" FFab_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.0000,1.0000,0.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 FreeCAD.ActiveDocument.ActiveObject.Placement.Base.z-=1.6 # if len(FrontSilk)>0: #FSilk_lines = Part.makeCompound(FrontSilk) #Part.show(FSilk_lines) FSilk_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","FSilk_lines") #FSilk_lines.Label="FSilk_lines" #FSilk_lines_name=FSilk_lines.Name FSilk_lines.addProperty("App::PropertyBool","fixedPosition","importPart") FSilk_lines.Shape = Part.makeCompound(FrontSilk) #TopPadsBase.Shape.copy() FSilk_lines.ViewObject.Proxy=0 FSilk_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="FrontSilk" FSilk_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (1.0000,1.0000,1.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 # if len(BotSilk)>0: #FSilk_lines = Part.makeCompound(FrontSilk) #Part.show(FSilk_lines) BSilk_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BSilk_lines") #FSilk_lines.Label="FSilk_lines" #FSilk_lines_name=FSilk_lines.Name BSilk_lines.addProperty("App::PropertyBool","fixedPosition","importPart") BSilk_lines.Shape = Part.makeCompound(BotSilk) #TopPadsBase.Shape.copy() BSilk_lines.ViewObject.Proxy=0 BSilk_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="BotSilk" BSilk_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (1.0000,1.0000,1.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 FreeCAD.ActiveDocument.ActiveObject.Placement.Base.z-=1.6 # if len(EdgeCuts)>0: #FSilk_lines = Part.makeCompound(FrontSilk) #Part.show(FSilk_lines) ECuts_lines = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","EdgeCuts_lines") #FSilk_lines.Label="FSilk_lines" #FSilk_lines_name=FSilk_lines.Name ECuts_lines.addProperty("App::PropertyBool","fixedPosition","importPart") ECuts_lines.Shape = Part.makeCompound(EdgeCuts) #TopPadsBase.Shape.copy() ECuts_lines.ViewObject.Proxy=0 ECuts_lines.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="EdgeCuts" ECuts_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (1.0000,0.0000,0.0000) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 30 # if len(TopPadList)>0: # TopPadsBase = Part.makeCompound(TopPadList) # Part.show(TopPadsBase) # FreeCAD.ActiveDocument.ActiveObject.Label="TopPadsBase" # TopPadsBase_name=FreeCAD.ActiveDocument.ActiveObject.Name TopPads = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","TopPads") TopPads.Label="TopPads" TopPads_name=TopPads.Name TopPads.addProperty("App::PropertyBool","fixedPosition","importPart") TopPads.Shape = Part.makeCompound(TopPadList) #TopPadsBase.Shape.copy() TopPads.ViewObject.Proxy=0 TopPads.fixedPosition = True #fp_group.addObject(TopPads) #FreeCAD.ActiveDocument.removeObject(TopPadsBase.Name) FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 if len(BotPadList)>0: #BotPads = Part.makeCompound(BotPadList) #Part.show(BotPads) BotPads = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BotPads") #BotPads.Label="BotPads" BotPads_name=BotPads.Name BotPads.addProperty("App::PropertyBool","fixedPosition","importPart") BotPads.Shape = Part.makeCompound(BotPadList) #TopPadsBase.Shape.copy() BotPads.ViewObject.Proxy=0 BotPads.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="BotPads" BotPads_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 # if len(TopNetTieList)>0: TopNetTie = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","TopNetTie") TopNetTie.Label="TopNetTie" TopNetTie_name=TopNetTie.Name TopNetTie.addProperty("App::PropertyBool","fixedPosition","importPart") TopNetTie.Shape = Part.makeCompound(TopNetTieList) #TopPadsBase.Shape.copy() TopNetTie.ViewObject.Proxy=0 TopNetTie.fixedPosition = True FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 if len(BotNetTieList)>0: BotNetTie = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","BotNetTie") BotNetTie.Label="BotNetTie" BotNetTie_name=BotNetTie.Name BotNetTie.addProperty("App::PropertyBool","fixedPosition","importPart") BotNetTie.Shape = Part.makeCompound(BotNetTieList) #TopPadsBase.Shape.copy() BotNetTie.ViewObject.Proxy=0 BotNetTie.fixedPosition = True FreeCAD.ActiveDocument.ActiveObject.Label="BotNetTie" BotNetTie_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.81,0.71,0.23) #(0.85,0.53,0.10) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 60 # if len(HoleList)>0: Holes = Part.makeCompound(HoleList) Holes = Part.makeSolid(Holes) Part.show(Holes) #say(FreeCAD.ActiveDocument.ActiveObject.Name) FreeCAD.ActiveDocument.ActiveObject.Label="Holes" Holes_name=FreeCAD.ActiveDocument.ActiveObject.Name #say(Holes_name) FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.67,1.00,0.50) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 70 #THPs = Part.makeCompound(THPList) #THPs = Part.makeSolid(THPs) ##evaluate solid #Part.show(THPs) THPs = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","THPs") #THPs.Label="THPs" THPs_name=THPs.Name THPs.addProperty("App::PropertyBool","fixedPosition","importPart") THPs.Shape = Part.makeCompound(THPList) #TopPadsBase.Shape.copy() #THPs = Part.makeSolid(THPs) THPs.ViewObject.Proxy=0 THPs.fixedPosition = True #say(FreeCAD.ActiveDocument.ActiveObject.Name) FreeCAD.ActiveDocument.ActiveObject.Label="PTHs" THPs_name=FreeCAD.ActiveDocument.ActiveObject.Name #say(Holes_name) FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.67,1.00,0.50) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 70 fp_group=FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", footprint_name+'fp') say(fp_group.Label) some_element = False list=[] if len(FCrtYd)>0: obj6 = FreeCAD.ActiveDocument.getObject(FCrtYd_name) list.append(FCrtYd_name) fp_group.addObject(obj6) some_element = True if len(FFab)>0: obj7 = FreeCAD.ActiveDocument.getObject(FFab_name) list.append(FFab_name) fp_group.addObject(obj7) some_element = True if len(FrontSilk)>0: obj2 = FreeCAD.ActiveDocument.getObject(FSilk_name) list.append(FSilk_name) fp_group.addObject(obj2) some_element = True if len(BCrtYd)>0: obj6 = FreeCAD.ActiveDocument.getObject(BCrtYd_name) list.append(BCrtYd_name) fp_group.addObject(obj6) some_element = True if len(BFab)>0: obj7 = FreeCAD.ActiveDocument.getObject(BFab_name) list.append(BFab_name) fp_group.addObject(obj7) some_element = True if len(BotSilk)>0: obj2 = FreeCAD.ActiveDocument.getObject(BSilk_name) list.append(BSilk_name) fp_group.addObject(obj2) some_element = True if len(EdgeCuts)>0: obj2 = FreeCAD.ActiveDocument.getObject(ECuts_name) list.append(ECuts_name) fp_group.addObject(obj2) some_element = True if len(TopPadList)>0: obj3 = FreeCAD.ActiveDocument.getObject(TopPads_name) fp_group.addObject(obj3) list.append(TopPads_name) some_element = True if len(BotPadList)>0: obj4 = FreeCAD.ActiveDocument.getObject(BotPads_name) fp_group.addObject(obj4) list.append(BotPads_name) some_element = True if len(TopNetTieList)>0: obj_3 = FreeCAD.ActiveDocument.getObject(TopNetTie_name) fp_group.addObject(obj_3) list.append(TopNetTie_name) some_element = True if len(BotNetTieList)>0: obj_4 = FreeCAD.ActiveDocument.getObject(BotNetTie_name) fp_group.addObject(obj_4) list.append(BotNetTie_name) some_element = True #objFp=Part.makeCompound(list) #Part.show(objFp) #say(list) if some_element: doc=FreeCAD.ActiveDocument fp_objs=[] list1=[] for obj in fp_group.Group: #if (obj.Label==fp_group.Label): #FreeCADGui.Selection.addSelection(obj) shape=obj.Shape.copy() #shape_name=FreeCAD.ActiveDocument.ActiveObject.Name list1.append(shape) #Part.show(shape) fp_objs.append(obj) #say("added") # #fp_objs.copy #objFp=Part.makeCompound(shape) objFp=Part.makeCompound(list1) Part.show(objFp) obj = FreeCAD.ActiveDocument.ActiveObject #say("h") FreeCADGui.Selection.addSelection(obj) # select the object createSolidBBox2(obj) bbox=FreeCAD.ActiveDocument.ActiveObject FreeCAD.ActiveDocument.ActiveObject.Label ="Pcb_solid" pcb_solid_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.removeObject(obj.Name) #FreeCADGui.ActiveDocument.getObject(bbox.Name).BoundingBox = True FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.664,0.664,0.496) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 #obj6 = FreeCAD.ActiveDocument.getObject(bbox.Name) fp_group.addObject(bbox) if len(HoleList)>0: cut_base = FreeCAD.ActiveDocument.getObject(pcb_solid_name).Shape for drill in HoleList: #Holes = Part.makeCompound(HoleList) hole = Part.makeSolid(drill) #Part.show(hole) #hole_name=FreeCAD.ActiveDocument.ActiveObject.Name #cutter = FreeCAD.ActiveDocument.getObject(hole_name).Shape cut_base=cut_base.cut(hole) Part.show(cut_base) pcb_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label ="Pcb-base" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.664,0.664,0.496) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 #say("cut") pcb=FreeCAD.ActiveDocument.ActiveObject fp_group.addObject(pcb) #say("added") #FreeCAD.activeDocument().recompute() FreeCAD.ActiveDocument.removeObject(pcb_solid_name) if len(TopPadList)>0: FreeCAD.ActiveDocument.getObject(TopPads_name).Label = "TopPads_" cut_base = FreeCAD.ActiveDocument.getObject(TopPads_name).Shape holes=FreeCAD.ActiveDocument.getObject(Holes_name) cut_base=cut_base.cut(holes.Shape) Part.show(cut_base) Pads_top_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label = "TopPads" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.664,0.664,0.496) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 #say("cut") Pads_top=FreeCAD.ActiveDocument.ActiveObject fp_group.addObject(Pads_top) #say("added") #FreeCAD.activeDocument().recompute() FreeCAD.ActiveDocument.removeObject(TopPads_name) if len(BotPadList)>0: cut_base = FreeCAD.ActiveDocument.getObject(BotPads_name).Shape FreeCAD.ActiveDocument.getObject(BotPads_name).Label = "BotPads_" holes=FreeCAD.ActiveDocument.getObject(Holes_name) cut_base=cut_base.cut(holes.Shape) Part.show(cut_base) Pads_bot_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label = "BotPads" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.664,0.664,0.496) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 #say("cut") Pads_bot=FreeCAD.ActiveDocument.ActiveObject fp_group.addObject(Pads_bot) #say("added") #FreeCAD.activeDocument().recompute() FreeCAD.ActiveDocument.removeObject(BotPads_name) if len(TopNetTieList)>0: FreeCAD.ActiveDocument.getObject(TopNetTie_name).Label = "TopNetTie_" cut_base = FreeCAD.ActiveDocument.getObject(TopNetTie_name).Shape holes=FreeCAD.ActiveDocument.getObject(Holes_name) cut_base=cut_base.cut(holes.Shape) Part.show(cut_base) NetTie_top_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label = "TopNetTie" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.664,0.664,0.496) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 #say("cut") NetTie_top=FreeCAD.ActiveDocument.ActiveObject fp_group.addObject(NetTie_top) #say("added") #FreeCAD.activeDocument().recompute() FreeCAD.ActiveDocument.removeObject(TopNetTie_name) if len(BotNetTieList)>0: FreeCAD.ActiveDocument.getObject(BotNetTie_name).Label = "BotNetTie_" cut_base = FreeCAD.ActiveDocument.getObject(BotNetTie_name).Shape holes=FreeCAD.ActiveDocument.getObject(Holes_name) cut_base=cut_base.cut(holes.Shape) Part.show(cut_base) NetTie_bot_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label = "BotNetTie" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (0.664,0.664,0.496) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 #say("cut") NetTie_bot=FreeCAD.ActiveDocument.ActiveObject fp_group.addObject(NetTie_bot) #say("added") #FreeCAD.activeDocument().recompute() FreeCAD.ActiveDocument.removeObject(BotNetTie_name) obj5 = FreeCAD.ActiveDocument.getObject(Holes_name) fp_group.addObject(obj5) list.append(Holes_name) obj6 = FreeCAD.ActiveDocument.getObject(THPs_name) fp_group.addObject(obj6) list.append(THPs_name) FreeCAD.ActiveDocument.removeObject(Holes_name) else: pcb=FreeCAD.ActiveDocument.ActiveObject # copying pcb to FeaturePython to assign fixedPosition for assembly2 Pcb_obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","newPCB") Pcb_obj.Label="Pcb" Pcb_obj.addProperty("App::PropertyBool","fixedPosition","importPart") Pcb_obj.Shape = pcb.Shape.copy() Pcb_obj.ViewObject.Proxy=0 # for p in pcb.ViewObject.PropertiesList: #assuming that the user may change the appearance of parts differently depending on the assembly. # if hasattr(Pcb_obj.ViewObject, p) and p not in ['DiffuseColor']: # setattr(Pcb_obj.ViewObject, p, getattr(pcb.ViewObject, p)) Pcb_obj.ViewObject.DiffuseColor = pcb.ViewObject.DiffuseColor Pcb_obj.fixedPosition = True fp_group.addObject(Pcb_obj) # workaround for FC 0.17 OCC 7 (double change transparency) # FreeCADGui.ActiveDocument.getObject("newPCB").Transparency = 79 # FreeCADGui.ActiveDocument.getObject("newPCB").Transparency = 80 # workaround for FC 0.17 OCC 7 (double change transparency) FreeCADGui.ActiveDocument.ActiveObject.Transparency = 79 FreeCADGui.ActiveDocument.ActiveObject.Transparency = 80 FreeCAD.ActiveDocument.removeObject(pcb.Name) list2=[] list2_objs=[] for obj in fp_group.Group: # do what you want to automate #if (obj.Label==fp_group.Label): #FreeCADGui.Selection.addSelection(obj) shape=obj.Shape.copy() #shape_name=FreeCAD.ActiveDocument.ActiveObject.Name list2.append(shape) #Part.show(shape) list2_objs.append(obj) #say("added") #say(list2) #say('here1') #Draft.rotate(list2_objs,90.0,FreeCAD.Vector(0.0,0.0,0.0),axis=FreeCAD.Vector(-0.0,-0.0,1.0),copy=False) #say('here1') rot=[0,0,rot_wrl] rotateObjs(list2_objs, rot) for obj in fp_group.Group: FreeCADGui.Selection.removeSelection(obj) #say('here2') FreeCAD.activeDocument().recompute() if len(sys.argv)<4: #sayerr("view fitting3") #sayerr(sys.argv) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") #pads_found=getPadsList(content) else: sayerr('internal layers not supported or fotprint empty') doc.commitTransaction() say('closing Transaction \'opening_kicad_footprint\'') ### def routineDrawIDF(doc,filename): """process_emn(document, filename)-> adds emn geometry from emn file""" global start_time msg='IDF_ImporterVersion='+IDF_ImporterVersion say(msg) #emnfile=pythonopen(filename, "r") emnfile=pythonopen(filename, "rb") emn_unit=1.0 #presume millimeter like emn unit emn_version=2 #presume emn_version 2 board_thickness=0 #presume 0 board height board_outline=[] #no outline drills=[] #no drills placement=[] #no placement place_item=[] #empty place item emnlines=emnfile.readlines() emnfile.close() passed_sections=[] current_section="" section_counter=0 ignore_hole_size=min_drill_size #say((emnlines)) for emnline in emnlines: emnrecords=split_records(emnline) if len( emnrecords )==0 : continue if len( emnrecords[0] )>4 and emnrecords[0][0:4]==".END": passed_sections.append(current_section) current_section="" elif emnrecords[0][0]==".": current_section=emnrecords[0] section_counter=0 section_counter+=1 if current_section==".HEADER" and section_counter==2: emn_version=int(float(emnrecords[1])) say("Emn version: "+emnrecords[1]) if current_section==".HEADER" and section_counter==3 and emnrecords[1]=="THOU": emn_unit=0.0254 say("UNIT THOU" ) if current_section==".HEADER" and section_counter==3 and emnrecords[1]=="TNM": emn_unit=0.000010 say("TNM" ) if current_section==".BOARD_OUTLINE" and section_counter==2: board_thickness=emn_unit*float(emnrecords[0]) say("Found board thickness "+emnrecords[0]) if current_section==".BOARD_OUTLINE" and section_counter>2: board_outline.append([int(emnrecords[0]),float(emnrecords[1])*emn_unit,float(emnrecords[2])*emn_unit,float(emnrecords[3])]) if current_section==".DRILLED_HOLES" and section_counter>1 and float(emnrecords[0])*emn_unit>ignore_hole_size: drills.append([float(emnrecords[0])*emn_unit,float(emnrecords[1])*emn_unit,float(emnrecords[2])*emn_unit]) if current_section==".PLACEMENT" and section_counter>1 and fmod(section_counter,2)==0: place_item=[] place_item.append(emnrecords[2]) #Reference designator place_item.append(emnrecords[1]) #Component part number place_item.append(emnrecords[0]) #Package name if current_section==".PLACEMENT" and section_counter>1 and fmod(section_counter,2)==1: place_item.append(float(emnrecords[0])*emn_unit) #X place_item.append(float(emnrecords[1])*emn_unit) #Y if emn_version==3: place_item.append(float(emnrecords[2])*emn_unit) #Z maui #say("\nZ="+(str(float(emnrecords[2])))) place_item.append(float(emnrecords[emn_version])) #Rotation place_item.append(emnrecords[emn_version+1]) #Side place_item.append(emnrecords[emn_version+2]) #Place Status say(str(place_item)) placement.append(place_item) say("\n".join(passed_sections)) #say(board_outline) say("Proceed "+str(Process_board_outline(doc,board_outline,drills,board_thickness))+" outlines") ## place_steps(doc,placement,board_thickness) ### def Process_board_outline(doc,board_outline,drills,board_thickness): """Process_board_outline(doc,board_outline,drills,board_thickness)-> number proccesed loops adds emn geometry from emn file""" global start_time, use_AppPart, force_oldGroups, use_Links, use_LinkGroups vertex_index=-1; #presume no vertex lines=-1 #presume no lines out_shape=[] out_face=[] for point in board_outline: vertex=Base.Vector(point[1],point[2],0) vertex_index+=1 if vertex_index==0: lines=point[0] elif lines==point[0]: if point[3]!=0 and point[3]!=360: out_shape.append(Part.Arc(prev_vertex,mid_point(prev_vertex,vertex,point[3]),vertex)) #say("mid point "+str(mid_point)) elif point[3]==360: per_point=Per_point(prev_vertex,vertex) out_shape.append(Part.Arc(per_point,mid_point(per_point,vertex,point[3]/2),vertex)) out_shape.append(Part.Arc(per_point,mid_point(per_point,vertex,-point[3]/2),vertex)) else: out_shape.append(PLine(prev_vertex,vertex)) else: out_shape=Part.Shape(out_shape) out_shape=Part.Wire(out_shape.Edges) out_face.append(Part.Face(out_shape)) out_shape=[] vertex_index=0 lines=point[0] prev_vertex=vertex if lines!=-1: out_shape=Part.Shape(out_shape) out_shape=Part.Wire(out_shape.Edges) out_face.append(Part.Face(out_shape)) outline=out_face[0] say("Added outline") if len(out_face)>1: say("Cutting shape inside outline") for otl_cut in out_face[1: ]: outline=outline.cut(otl_cut) #say("Cutting shape inside outline") if len(drills)>0: say("Cutting holes inside outline") for drill in drills: #say("Cutting hole inside outline") out_shape=Part.makeCircle(drill[0]/2, Base.Vector(drill[1],drill[2],0)) out_shape=Part.Wire(out_shape.Edges) outline=outline.cut(Part.Face(out_shape)) doc_outline=doc.addObject("Part::Feature","Pcb") doc_outline.Shape=outline #FreeCADGui.Selection.addSelection(doc_outline) #FreeCADGui.runCommand("Draft_Upgrade") #outline=FreeCAD.ActiveDocument.getObject("Union").Shape #FreeCAD.ActiveDocument.removeObject("Union") #doc_outline=doc.addObject("Part::Feature","Board_outline") doc_outline.Shape=outline.extrude(Base.Vector(0,0,-board_thickness)) if use_AppPart and not force_oldGroups: #sayw("creating hierarchy") ## to evaluate to add App::Part hierarchy doc.Tip = doc.addObject('App::Part','Board_Geoms') doc.Board_Geoms.Label = 'Board_Geoms' try: doc.Board_Geoms.License = '' doc.Board_Geoms.LicenseURL = '' except: pass grp=doc.Board_Geoms #FreeCADGui.activeView().setActiveObject('Board_Geoms', doc.Board_Geoms) ## end hierarchy else: #sayerr("creating flat groups") grp=doc.addObject("App::DocumentObjectGroup", "Board_Geoms") grp.addObject(doc_outline) #grp.addObject(Sketch) doc.Pcb.ViewObject.ShapeColor = (colr,colg,colb) say_time() #say(str(start_time));say('*'+str(end_milli_time)+'start-end') #FreeCADGui.activeDocument().activeView().viewAxometric() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") #doc.Pcb.ViewObject.ShapeColor=(0.0, 0.5, 0.0, 0.0) return lines+1 ### def split_records(line_record): """split_records(line_record)-> list of strings(records) standard separator list separator is space, records containing encapsulated by " """ split_result=[] quote_pos=line_record.find('"') while quote_pos!=-1: if quote_pos>0: split_result.extend(line_record[ :quote_pos].split()) line_record=line_record[quote_pos: ] quote_pos=line_record.find('"',1) else: quote_pos=line_record.find('"',1) if quote_pos!=-1: split_result.append(line_record[ :quote_pos+1]) line_record=line_record[quote_pos+1: ] else: split_result.append(line_record) line_record="" quote_pos=line_record.find('"') split_result.extend(line_record.split()) return split_result ### def findWires(edges): def verts(shape): return [shape.Vertexes[0].Point,shape.Vertexes[-1].Point] def group(shapes): shapesIn = shapes[:] pointTst = [] pointOut =[] for s in shapesIn : pointTst=pointTst+[s.Vertexes[0].Point] pointTst=pointTst+[s.Vertexes[-1].Point] say( pointTst ) changed = False for s in shapesIn: if len(s.Vertexes) < 2: say( "one vertex, its a circle, just add" ) else: for v in verts(s): twoDot=0 for vv in pointTst: if v == vv: twoDot=twoDot+1 if v==vv and twoDot==2 : changed = True say( "found matching vert" ) break if twoDot<2: say( "didn't find any matching vert..." ) pointOut.append(v) say( "Dots non connected"); say(pointOut) return(changed,pointOut) def joint(point): for p in range(len(point)/2) : say(point) deltI=Part.Vertex(100,100,100).Point pos=1 for pp in range(len(point)-1) : say( "position:") ;say( pp+1 ) if len(point)-1>1: deltN=(point[0]-point[pp+1]) if deltN.Length 0 or len(l2) > 0 @staticmethod def dofacesoverlapboolean(bigface,smallface): #import FreeCAD,FreeCADGui #FreeCAD.Console.PrintLog('intersecting %d %d\n'%(bigfacei,smallfacei)) #FreeCADGui.updateGui() return bigface.common(smallface).Area > 0 def builddepdict(self): import Part import itertools #isinsidelist = [] self.isinsidedict = {} #for bigface, smallface in itertools.combinations(sortedfaces,2): for bigfacei, smallfacei in\ itertools.combinations(range(len(self.sortedfaces)),2): try: overlap = OSCD2Dg_Overlappingfaces.dofacesoverlapproximity(\ self.sortedfaces[bigfacei],self.sortedfaces[smallfacei]) except (NotImplementedError, Part.OCCError) as e: try: overlap = OSCD2Dg_Overlappingfaces.dofacesoverlapboolean(\ self.sortedfaces[bigfacei],\ self.sortedfaces[smallfacei]) except Part.OCCError: overlap = OSCD2Dg_Overlappingfaces.dofacesoverlapallverts(\ self.sortedfaces[bigfacei],\ self.sortedfaces[smallfacei]) if overlap: #isinsidelist.append((bigfacei,smallfacei)) smallinbig = self.isinsidedict.get(bigfacei,[]) smallinbig.append(smallfacei) if len(smallinbig) == 1: self.isinsidedict[bigfacei] = smallinbig @staticmethod def finddepth(dict1,faceidx,curdepth=0): if faceidx not in dict1: return curdepth+1 else: #print dict1[faceidx],[(finddepth(dict1,childface,curdepth)) for childface in dict1[faceidx]] return max([(OSCD2Dg_Overlappingfaces.finddepth(dict1,childface,curdepth+1)) for childface in dict1[faceidx]]) def findrootdepth(self): return max([OSCD2Dg_Overlappingfaces.finddepth(self.isinsidedict,fi) for fi in range(len(self.sortedfaces))]) def hasnoparent(self,faceindex): return OSCD2Dg_Overlappingfaces.hasnoparentstatic(self.isinsidedict,faceindex) @staticmethod def hasnoparentstatic(isinsidedict,faceindex): if (sys.version_info > (3, 0)): #py3 for smalllist in isinsidedict.values(): if faceindex in smalllist: return False else: #py2 for smalllist in isinsidedict.itervalues(): if faceindex in smalllist: return False return True #@staticmethod #def subtreedict(rootface,parantdict): # '''biuld a subtree dictinary''' # newdict = parantdict.copy() # del newdict[rootface] # return newdict @staticmethod def directchildren(isinsidedict,parent): #return [child for child in isinsidedict.get(parent,[]) if child not in isinsidedict] dchildren=[] for child in isinsidedict.get(parent,[]): direct = True import sys if sys.version_info[0] == 2: #if py2: #print('py2') py2=True else: py2=False if py2: for key, value in isinsidedict.iteritems(): if key != parent and child in value and parent not in value: direct = False else: for key, value in isinsidedict.items(): if key != parent and child in value and parent not in value: direct = False if direct: dchildren.append(child) return dchildren #@staticmethod #def indirectchildren(isinsidedict,parent): # return [child for child in isinsidedict.get(parent,[]) if child in isinsidedict] @staticmethod def printtree(isinsidedict,facenum): def printtreechild(isinsidedict,facenum,parent): children=OSCD2Dg_Overlappingfaces.directchildren(isinsidedict,parent) #say 'parent %d directchild %s' % (parent,children) if children: subdict=isinsidedict.copy() del subdict[parent] for child in children: printtreechild(subdict,facenum,child) rootitems=[fi for fi in range(facenum) if OSCD2Dg_Overlappingfaces.hasnoparentstatic(isinsidedict,fi)] for rootitem in rootitems: printtreechild(isinsidedict,facenum,rootitem) def makefeatures(self,doc): import FreeCAD def addshape(faceindex): obj=doc.addObject('Part::Feature','facefromedges_%d' % faceindex) obj.Shape = self.sortedfaces[faceindex] obj.ViewObject.hide() return obj def addfeature(faceindex,isinsidedict): directchildren = OSCD2Dg_Overlappingfaces.directchildren(isinsidedict,faceindex) if len(directchildren) == 0: obj=addshape(faceindex) else: subdict=isinsidedict.copy() del subdict[faceindex] obj=doc.addObject("Part::Cut","facesfromedges_%d" % faceindex) obj.Base= addshape(faceindex) #we only do subtraction if len(directchildren) == 1: obj.Tool = addfeature(directchildren[0],subdict) else: obj.Tool = doc.addObject("Part::MultiFuse",\ "facesfromedges_union") obj.Tool.Shapes = [addfeature(child,subdict)\ for child in directchildren] obj.Tool.ViewObject.hide() obj.ViewObject.hide() return obj rootitems = [fi for fi in range(len(self.sortedfaces)) if self.hasnoparent(fi)] for rootitem in rootitems: addfeature(rootitem,self.isinsidedict).ViewObject.show() def makeshape(self): def removefaces(rfaces): for tfi in directchildren[::-1]: finishedwith.append(tfi) #del faces[tfi] if tfi in isinsidedict: del isinsidedict[tfi] if (sys.version_info > (3, 0)): #py3 for key,value in isinsidedict.items(): if tfi in value: newlist=value[:] #we work on a shallow copy of isinsidedict newlist.remove(tfi) isinsidedict[key]=newlist else: #py2 for key,value in isinsidedict.iteritems(): if tfi in value: newlist=value[:] #we work on a shallow copy of isinsidedict newlist.remove(tfi) isinsidedict[key]=newlist def hasnoparent(faceindex): if (sys.version_info > (3, 0)): #py3 for smalllist in self.isinsidedict.values(): if faceindex in smalllist: return False else: #py2 for smalllist in self.isinsidedict.itervalues(): if faceindex in smalllist: return False return True faces=self.sortedfaces[:] isinsidedict=self.isinsidedict.copy() finishedwith=[] while not all([OSCD2Dg_Overlappingfaces.hasnoparentstatic(isinsidedict,fi) for fi in range(len(faces))]): #print [(Overlappingfaces.hasnoparentstatic(isinsidedict,fi),\ #Overlappingfaces.directchildren(isinsidedict,fi)) for fi in range(len(faces))] for fi in range(len(faces))[::-1]: directchildren = OSCD2Dg_Overlappingfaces.directchildren(isinsidedict,fi) if not directchildren: continue elif len(directchildren) == 1: faces[fi]=faces[fi].cut(faces[directchildren[0]]) #print fi,'-' ,directchildren[0], faces[fi],faces[directchildren[0]] removefaces(directchildren) else: toolface=OSCD2Dg_fusefaces([faces[tfi] for tfi in directchildren]) faces[fi]=faces[fi].cut(toolface) #print fi, '- ()', directchildren, [faces[tfi] for tfi in directchildren] removefaces(directchildren) #print fi,directchildren faces =[face for index,face in enumerate(faces) if index not in finishedwith] # return faces return OSCD2Dg_fusefaces(faces) # def OSCD2Dg_superWireReverse(debuglist,closed=False): '''superWireReverse(debuglist,[closed]): forces a wire between edges that don't necessarily have coincident endpoints. If closed=True, wire will always be closed. debuglist has a tuple for every edge.The first entry is the edge, the second is the flag 'does not need to be inverted' ''' #taken from draftlibs sayerr('edges not closed... trying to solve it') def median(v1,v2): vd = v2.sub(v1) vd.scale(.5,.5,.5) return v1.add(vd) try: from DraftGeomUtils import findMidpoint except ImportError: #workaround for Version 0.12 from draftlibs.fcgeo import findMidpoint #workaround for Version 0.12 import Part #edges = sortEdges(edgeslist) # print "here 7" # print debuglist newedges = [] edge_added=False for i in range(len(debuglist)): curr = debuglist[i] if i == 0: if closed: prev = debuglist[-1] else: prev = None else: prev = debuglist[i-1] #print "prev=",prev if i == (len(debuglist)-1): if closed: nexte = debuglist[0] else: nexte = None else: nexte = debuglist[i+1] # print i,prev,curr,nexte # print "here loop" if prev: if curr[0].Vertexes[-1*(not curr[1])].Point == \ prev[0].Vertexes[-1*prev[1]].Point: p1 = curr[0].Vertexes[-1*(not curr[1])].Point else: p1 = median(curr[0].Vertexes[-1*(not curr[1])].Point,\ prev[0].Vertexes[-1*prev[1]].Point) else: p1 = curr[0].Vertexes[-1*(not curr[1])].Point if nexte: if curr[0].Vertexes[-1*curr[1]].Point == \ nexte[0].Vertexes[-1*(not nexte[1])].Point: p2 = nexte[0].Vertexes[-1*(not nexte[1])].Point else: p2 = median(curr[0].Vertexes[-1*(curr[1])].Point,\ nexte[0].Vertexes[-1*(not nexte[1])].Point) else: p2 = curr[0].Vertexes[-1*(curr[1])].Point # print "here 8" # print "curr[0].Curve ",curr[0].Curve if hasattr(Part,"LineSegment"): if isinstance(curr[0].Curve,Part.Line) or isinstance(curr[0].Curve,Part.LineSegment): #print "line",p1,p2 newedges.append(Part.LineSegment(p1,p2).toShape()) edge_added=True elif hasattr(Part,"Line"): if isinstance(curr[0].Curve,Part.Line): #print "line",p1,p2 newedges.append(Part.Line(p1,p2).toShape()) edge_added=True if isinstance(curr[0].Curve,Part.Circle): p3 = findMidpoint(curr[0]) #print "arc",p1,p3,p2 newedges.append(Part.Arc(p1,p3,p2).toShape()) edge_added=True #else: if not edge_added: say( "Cannot superWire edges that are not lines or arcs" ) return None # print newedges return Part.Wire(newedges) # def OSCD2Dg_endpointdistance(edges): '''return the distance of of vertices in path (list of edges) as maximum, minimum and distance between start and endpoint it expects the edges to be traversed forward from starting from Vertex 0''' numedges=len(edges) if numedges == 1 and len(edges[0].Vertexes) == 1: return 0.0,0.0,0.0 outerdistance = edges[0].Vertexes[0].Point.sub(\ edges[-1].Vertexes[-1].Point).Length if numedges > 1: innerdistances=[edges[i].Vertexes[-1].Point.sub(edges[i+1].\ Vertexes[0].Point).Length for i in range(numedges-1)] return max(innerdistances),min(innerdistances),outerdistance else: return 0.0,0.0,outerdistance def OSCD2Dg_endpointdistancedebuglist(debuglist): '''return the distance of of vertices in path (list of edges) as maximum, minimum and distance between start and endpoint it it expects a 'not reversed' flag for every edge''' numedges=len(debuglist) if numedges == 1 and len(debuglist[0][0].Vertexes) == 1: return 0.0,0.0,0.0 outerdistance = debuglist[0][0].Vertexes[(not debuglist[0][1])*-1].\ Point.sub(debuglist[-1][0].Vertexes[(debuglist[-1][1])*-1].\ Point).Length if numedges > 1: innerdistances=[debuglist[i][0].Vertexes[debuglist[i][1]*-1].\ Point.sub(debuglist[i+1][0].Vertexes[(not debuglist[i+1][1])*\ -1].Point).Length for i in range(numedges-1)] return max(innerdistances),min(innerdistances),outerdistance else: return 0.0,0.0,outerdistance # def OSCD2Dg_findConnectedEdges(edgelist,eps=1e-6,debug=False): '''returns a list of list of connected edges''' def vertequals(v1,v2,eps=1e-6): '''check two vertices for equality''' #return all([abs(c1-c2) 0.000001: #OCC wont like it if maxd > 0.02: # print 'endpointdistance max:%f min:%f, ends:%f' %(maxd,mind,outerd) # print "here 5" if True: tobeclosed = outerd < eps*2 # OpenSCAD uses 0.001 for corase grid #from draftlibs import fcvec, fcgeo #w2=fcgeo.superWire(path,tobeclosed) #print "here 6a" w2=OSCD2Dg_superWireReverse(debug,tobeclosed) if w2 is not None: wirelist.append(w2) else:#this locks up FreeCAD #print "here 6b" comp=Part.Compound(path) wirelist.append(comp.connectEdgesToWires(False,eps).Wires[0]) #wirelist.append(comp.connectEdgesToWires(False,0.1).Wires[0]) else: done = False try: wire=Part.Wire(path) #if not close or wire.isClosed or outerd > 0.0001: wirelist.append(Part.Wire(path)) done = True except Part.OCCError: pass if not done: comp=Part.Compound(path) wirelist.append(comp.connectEdgesToWires(False,eps).Wires[0]) return wirelist # def OSCD2Dg_edgestofaces(edges,algo=3,eps=0.001): #edges=[] #for shapeobj in (objs): # edges.extend(shapeobj.Shape.Edges) #taken from Drafttools #from draftlibs import fcvec, fcgeo import Part #wires = fcgeo.findWires(edges) #print "edges: " # for e in edges: # print "e.Vertexes: ", e.Vertexes # for p in e.Vertexes: # print "points", p.Point # print "here 4" wires = OSCD2Dg_edgestowires(edges,eps) facel=[] for w in wires: #assert(len(w.Edges)>1) if not w.isClosed(): p0 = w.Vertexes[0].Point p1 = w.Vertexes[-1].Point # print "p0",p0," ";print "p1",p1 edges2 = w.Edges[:] try: if hasattr(Part,"LineSegment"): edges2.append(Part.LineSegment(p1,p0).toShape()) else: edges2.append(Part.Line(p1,p0).toShape()) #edges2.append(Part.LineSegment(p1,p0).toShape()) w = Part.Wire(edges2) #w = Part.Wire(fcgeo.sortEdges(edges2)) except Part.OCCError: comp=Part.Compound(edges2) w = comp.connectEdgesToWires(False,eps).Wires[0] facel.append(Part.Face(w)) #if w.isValid: #debugging # facel.append(Part.Face(w)) #else: # Part.show(w) if algo is None: return facel elif algo == 1: #stabale behavior return subtractfaces(facel) elif algo == 0: #return all faces return Part.Compound(facel) elif algo == 2: return subtractfaces2(facel) elif algo == 3: return OSCD2Dg_Overlappingfaces(facel).makeshape() # ### def DrawPCB(mypcb,lyr=None,rmv_container=None,keep_sketch=None): global start_time, use_AppPart, force_oldGroups, min_drill_size global addVirtual, load_sketch, off_x, off_y, aux_orig, grid_orig global running_time, conv_offs, use_Links, apply_edge_tolerance, simplifyComSolid global zfit, use_LinkGroups, fname_sfx, missingHeight global extrude_holes, ply_lines, bklist_dnp,blacklisted_model_elements def simu_distance(p0, p1): return max (abs(p0[0] - p1[0]), abs(p0[1] - p1[1])) import PySide import FreeCAD, Part from PySide import QtGui, QtCore from math import pi say("PCB Loader ") ## NB use always float() to guarantee number not string!!! max_edges_admitted = 1500 # after this number, no sketcher would be created if lyr is None: lyr = 'Edge.Cuts' #load_sketch=True get_time() t0=(running_time) #say(start_time) doc=FreeCAD.activeDocument() for obj in FreeCAD.ActiveDocument.Objects: FreeCADGui.Selection.removeSelection(obj) EdgeCuts = [] EdgeCuts_face = [] EdgeCuts_shape = [] PCB = [] PCB_Models = [] PCB_Geo = [] FpEdges_Geo = [] edges=[] PCBs = [] #print (mypcb.general) #maui errorchecking if hasattr(mypcb, 'general'): totalHeight=float(mypcb.general.thickness) else: totalHeight=1.6 missingHeight = False if totalHeight == 0: totalHeight = 1.6 missingHeight = True sayerr('pcb thickness = 0mm! CHANGED to 1.6mm Please fix your pcb design!') say('pcb thickness '+str(totalHeight)+'mm') version=mypcb.version say('kicad_pcb version ' +str(version)) if version < 3: QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Error ...","... KICAD pcb version "+ str(version)+" not supported \r\n"+"\r\nplease open and save your board with the latest kicad version") sys.exit("pcb version not supported") elif version==3: Edge_Cuts_lvl=28 Top_lvl=15 elif version>=4: Edge_Cuts_lvl=44 Top_lvl=0 conv_offs=1.0 if version >= 20171114: conv_offs=25.4 #getting pcb version for the internal use of 'virtual' modules pcbv = 4 if version == 20171130: pcbv = 5 elif version == 20211014: pcbv = 6 elif version >= 20220000: pcbv = 6.99 #load_sketch=False # sayerr(len(mypcb.gr_line)) # say(len(mypcb.gr_arc)) #edg_segms = len(mypcb.gr_line)+len(mypcb.gr_arc) edg_segms = 0 sayw('parsing') sk_label = lyr if 'Mask' in lyr: lyr = lyr[:-4] #print(lyr) keepout=False if 'Fill' in lyr or 'KeepOut' in lyr: if 'KeepOut' in lyr: keepout=True lyr = lyr[:2]+'Cu' #print(lyr) for ln in mypcb.gr_line: if lyr in ln.layer: #say(ln.layer) edg_segms+=1 for ar in mypcb.gr_arc: if lyr in ar.layer: #say(ln.layer) edg_segms+=1 for lp in mypcb.gr_poly: #print(lp) #print(lp.layer) #print(lp.pts) if lyr in lp.layer: #sayerr(lp.layer) try: for p in lp.pts.xy: edg_segms+=1 #sayerr(p) except: for a in lp.pts.arc: edg_segms+=1 #stop #edg_segms+=1 for bs in mypcb.gr_curve: if lyr in bs.layer: #sayerr(bs.layer) for p in bs.pts.xy: edg_segms+=1 #edg_segms+=1 for r in mypcb.gr_rect: if lyr in r.layer: #sayerr(bs.layer) edg_segms+=4 #edg_segms+=1 for zn in mypcb.zone: #print (zn,zn.layer,zn.polygon) if hasattr(zn,'layer'): zlayer=zn.layer else: zlayer=zn.layers for i,l in enumerate(zlayer): l=l.replace('"','') if isinstance(zlayer, list): zlayer[i]=l else: zlayer=l if not keepout: if lyr in zlayer and not hasattr(zn,'keepout'): for p in zn.polygon.pts.xy: edg_segms+=1 else: #print(zlayer,lyr,lyr in zlayer, hasattr(zn,'keepout')) if lyr in zlayer and hasattr(zn,'keepout'): for p in zn.polygon.pts.xy: edg_segms+=1 sayw(str(edg_segms)+' edge segments') #for lp in mypcb.gr_poly: #pcb polylines # if lp.layer != 'Edge.Cuts': # continue if edg_segms > max_edges_admitted: sayerr('too many segments ('+str(edg_segms)+'), skipping sketches & constraints') # load_sketch = False if load_sketch: PCB_Sketch_draft= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PCB_Sketch_draft') FreeCAD.activeDocument().PCB_Sketch_draft.Placement = FreeCAD.Placement(FreeCAD.Vector(0.000000,0.000000,0.000000),FreeCAD.Rotation(0.000000,0.000000,0.000000,1.000000)) #stop #sayerr(mypcb.layers['0']) if hasattr(mypcb, 'layers'): for lynbr in mypcb.layers: #getting layers name if float(lynbr) == Top_lvl: LvlTopName=(mypcb.layers['{0}'.format(str(lynbr))][0]) if float(lynbr) == Edge_Cuts_lvl: LvlEdgeName=(mypcb.layers['{0}'.format(str(lynbr))][0]) else: LvlTopName = 'F.Cu' LvlEdgeName = 'Edge.Cuts' # # sayerr(lyr[0]) # # sayerr('top') if hasattr(mypcb, 'general'): if hasattr(mypcb.general, 'area'): say('board area '+str(mypcb.general.area)) #sayerr('aux_axis_origin' + str(mypcb.setup.aux_axis_origin)) #stop origin = None if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'grid_origin'): say('grid_origin' + str(mypcb.setup.grid_origin)) origin = 'grid origin' #say(mypcb.setup.aux_axis_origin) #xp=mypcb.setup.aux_axis_origin[0]; yp=-mypcb.setup.aux_axis_origin[1] elif hasattr(mypcb.setup, 'aux_axis_origin'): say('aux_axis_origin' + str(mypcb.setup.aux_axis_origin)) origin = 'aux origin' #say(mypcb.setup.aux_axis_origin) #xp=mypcb.setup.aux_axis_origin[0]; yp=-mypcb.setup.aux_axis_origin[1] else: say('aux or grid origin not found') origin = 'grid origin' #temp workaround for kv6 missing aux origin else: say('grid origin not set\ndefault value on top left corner') origin = 'grid origin' #temp workaround for kv6 missing aux origin #if hasattr(mypcb.setup, 'aux origin'): # say('aux origin' + str(mypcb.setup.aux_axis_origin)) #else: # say('aux origin not used') ## NB use always float() to guarantee number not string!!! def get_mod_Ref(mod): #if hasattr(m,'property'): try: Ref = m.property[0][1] #kv8 fp reference #elif hasattr(m,'fp_text'): except: Ref = m.fp_text[0][1] return Ref def get_mod_dnp(m,rf): # print('reference fp_text',rf) if hasattr(m,'property'): for p in m.property: #kv8 fp field if 'dnp' in str(p[0]).lower() or 'dnf' in str(p[0]).lower(): if 'dnp' in str(p[1]).lower() or 'dnf' in str(p[1]).lower(): return True if hasattr(m,'fp_text'): for t in m.fp_text: #kv7 fp reference # print("t[0]",t[0]) if str(t[0]).lower()=='dnp' or str(t[0]).lower()=='dnf': if 'dnp' in str(t[1]).lower() or 'dnf' in str(t[1]).lower(): return True if str(t[0]).lower()=='user': #kv5 user added text #print("t[0], t[1]",t[0], t[1]) if 'dnp' in str(t[1]).lower() or 'dnf' in str(t[1]).lower(): return True return False class _ln: def __init__(myline, xs,ys,xe,ye): myline.start = [xs,ys] myline.end = [xe,ye] ln=_ln(0,0,0,0) ply_lines=[] def make_gr_line_obj(l, add_ply=None): global ply_lines if simu_distance((l.start[0],-l.start[1],0), ((l.end[0],-l.end[1],0))) > edge_tolerance: #non coincident points #if (Base.Vector(l.start[0],-l.start[1],0)) != (Base.Vector(l.end[0],-l.end[1],0)): #non coincident points line1=Part.Edge(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) if add_ply==True: ply_lines.append(line1) if load_sketch: if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0]-off_x,-l.start[1]-off_y,0), Base.Vector(l.end[0]-off_x,-l.end[1]-off_y,0))) PCB_Geo.append(PLine(Base.Vector(l.start[0]-off_x,-l.start[1]-off_y,0), Base.Vector(l.end[0]-off_x,-l.end[1]-off_y,0))) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) PCB_Geo.append(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) edges.append(line1); PCB.append(['Line', l.start[0], -l.start[1], l.end[0], -l.end[1]]) if show_border: Part.show(line1) for l in mypcb.gr_line: #pcb lines #if l.layer != 'Edge.Cuts': if lyr not in l.layer: continue #edges.append(Part.makeLine(makeVect(l.start),makeVect(l.end))) #say(l.start);say(l.end) #edge_tolerance_warning make_gr_line_obj(l) for r in mypcb.gr_rect: #pcb lines from rect #if l.layer != 'Edge.Cuts': if lyr not in r.layer: continue #segms = [r.start[0],r.start[1]][r.end[0],r.start[1]] line1=Part.Edge(PLine(Base.Vector(r.start[0],-r.start[1],0), Base.Vector(r.end[0],-r.start[1],0))) if load_sketch: if aux_orig ==1 or grid_orig ==1: PCB_Geo.append(PLine(Base.Vector(r.start[0]-off_x,-r.start[1]-off_y,0), Base.Vector(r.end[0]-off_x,-r.start[1]-off_y,0))) else: PCB_Geo.append(PLine(Base.Vector(r.start[0],-r.start[1],0), Base.Vector(r.end[0],-r.start[1],0))) edges.append(line1); PCB.append(['Line', r.end[0], -r.start[1], r.end[0], -r.end[1]]) if show_border: Part.show(line1) #segms = [r.end[0],r.start[1]][r.end[0],r.end[1]] line1=Part.Edge(PLine(Base.Vector(r.end[0],-r.start[1],0), Base.Vector(r.end[0],-r.end[1],0))) if load_sketch: if aux_orig ==1 or grid_orig ==1: PCB_Geo.append(PLine(Base.Vector(r.end[0]-off_x,-r.start[1]-off_y,0), Base.Vector(r.end[0]-off_x,-r.end[1]-off_y,0))) else: PCB_Geo.append(PLine(Base.Vector(r.end[0],-r.start[1],0), Base.Vector(r.end[0],-r.end[1],0))) edges.append(line1); PCB.append(['Line', r.end[0], -r.start[1], r.end[0], -r.end[1]]) if show_border: Part.show(line1) #segms = [r.end[0],r.end[1]][r.start[0],r.end[1]] line1=Part.Edge(PLine(Base.Vector(r.end[0],-r.end[1],0), Base.Vector(r.start[0],-r.end[1],0))) if load_sketch: if aux_orig ==1 or grid_orig ==1: PCB_Geo.append(PLine(Base.Vector(r.end[0]-off_x,-r.end[1]-off_y,0), Base.Vector(r.start[0]-off_x,-r.end[1]-off_y,0))) else: PCB_Geo.append(PLine(Base.Vector(r.end[0],-r.end[1],0), Base.Vector(r.start[0],-r.end[1],0))) edges.append(line1); PCB.append(['Line', r.end[0], -r.end[1], r.start[0], -r.end[1]]) if show_border: Part.show(line1) #segms = [r.start[0],r.end[1]][r.start[0],r.start[1]] line1=Part.Edge(PLine(Base.Vector(r.start[0],-r.end[1],0), Base.Vector(r.start[0],-r.start[1],0))) if load_sketch: if aux_orig ==1 or grid_orig ==1: PCB_Geo.append(PLine(Base.Vector(r.start[0]-off_x,-r.end[1]-off_y,0), Base.Vector(r.start[0]-off_x,-r.start[1]-off_y,0))) else: PCB_Geo.append(PLine(Base.Vector(r.start[0],-r.end[1],0), Base.Vector(r.start[0],-r.start[1],0))) edges.append(line1); PCB.append(['Line', r.start[0], -r.end[1], r.start[0], -r.start[1]]) if show_border: Part.show(line1) k_index = 0 for zn in mypcb.zone: #print(zn.layer) if hasattr(zn,'layer'): zlayer=zn.layer else: zlayer=zn.layers for i,l in enumerate(zlayer): l=l.replace('"','') if isinstance(zlayer, list): zlayer[i]=l else: zlayer=l # print(lyr[0],zlayer,lyr[0] in zlayer,lyr[0]+'.Cu' in zlayer) zl_found = False for zl in zlayer: if lyr[0]+'.Cu' in zlayer: zl_found = True if not zl_found: continue if 'Mask' in lyr and 'Mask' not in zlayer: continue #print(zn.polygon.pts.xy) if not keepout: if hasattr(zn,'keepout'): continue else: if not hasattr(zn,'keepout'): continue ind = 0 l = len(zn.polygon.pts.xy) z_lines = [] for p in zn.polygon.pts.xy: if ind == 0: line1=Part.Edge(PLine(Base.Vector(zn.polygon.pts.xy[l-1][0],-zn.polygon.pts.xy[l-1][1],0), Base.Vector(zn.polygon.pts.xy[0][0],-zn.polygon.pts.xy[0][1],0))) edges.append(line1); if load_sketch: if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0]-off_x,-l.start[1]-off_y,0), Base.Vector(l.end[0]-off_x,-l.end[1]-off_y,0))) PCB_Geo.append(PLine(Base.Vector(zn.polygon.pts.xy[l-1][0]-off_x,-zn.polygon.pts.xy[l-1][1]-off_y,0), Base.Vector(zn.polygon.pts.xy[0][0]-off_x,-zn.polygon.pts.xy[0][1]-off_y,0))) line2=Part.Edge(PLine(Base.Vector(zn.polygon.pts.xy[l-1][0]-off_x,-zn.polygon.pts.xy[l-1][1]-off_y,0), Base.Vector(zn.polygon.pts.xy[0][0]-off_x,-zn.polygon.pts.xy[0][1]-off_y,0))) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) PCB_Geo.append(PLine(Base.Vector(zn.polygon.pts.xy[l-1][0],-zn.polygon.pts.xy[l-1][1],0), Base.Vector(zn.polygon.pts.xy[0][0],-zn.polygon.pts.xy[0][1],0))) line2=Part.Edge(PLine(Base.Vector(zn.polygon.pts.xy[l-1][0],-zn.polygon.pts.xy[l-1][1],0), Base.Vector(zn.polygon.pts.xy[0][0],-zn.polygon.pts.xy[0][1],0))) z_lines.append(line2) else: line1=Part.Edge(PLine(Base.Vector(zn.polygon.pts.xy[ind-1][0],-zn.polygon.pts.xy[ind-1][1],0), Base.Vector(zn.polygon.pts.xy[ind][0],-zn.polygon.pts.xy[ind][1],0))) edges.append(line1); if load_sketch: if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0]-off_x,-l.start[1]-off_y,0), Base.Vector(l.end[0]-off_x,-l.end[1]-off_y,0))) PCB_Geo.append(PLine(Base.Vector(zn.polygon.pts.xy[ind-1][0]-off_x,-zn.polygon.pts.xy[ind-1][1]-off_y,0), Base.Vector(zn.polygon.pts.xy[ind][0]-off_x,-zn.polygon.pts.xy[ind][1]-off_y,0))) line2=Part.Edge(PLine(Base.Vector(zn.polygon.pts.xy[ind-1][0]-off_x,-zn.polygon.pts.xy[ind-1][1]-off_y,0), Base.Vector(zn.polygon.pts.xy[ind][0]-off_x,-zn.polygon.pts.xy[ind][1]-off_y,0))) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) PCB_Geo.append(PLine(Base.Vector(zn.polygon.pts.xy[ind-1][0],-zn.polygon.pts.xy[ind-1][1],0), Base.Vector(zn.polygon.pts.xy[ind][0],-zn.polygon.pts.xy[ind][1],0))) line2=Part.Edge(PLine(Base.Vector(zn.polygon.pts.xy[ind-1][0],-zn.polygon.pts.xy[ind-1][1],0), Base.Vector(zn.polygon.pts.xy[ind][0],-zn.polygon.pts.xy[ind][1],0))) z_lines.append(line2) ind+=1 Draft.makeSketch(z_lines) ndsk = FreeCAD.ActiveDocument.ActiveObject ndsk.Label = sk_label + '_' + str(k_index) ndsk.ViewObject.LineColor = (1.00,1.00,1.00) ndsk.ViewObject.PointColor = (1.00,1.00,1.00) k_index += 1 #closing edge def make_gr_arc_obj (a,pa): global load_sketch, aux_orig, grid_orig, ply_lines [xs, ys] = a.start [x1, y1] = a.end try: if hasattr (a, 'mid'): [xm, ym] = a.mid arc1 = Part.Edge(Part.Arc(Base.Vector(xs,-ys,0),Base.Vector(xm,-ym,0),Base.Vector(x1,-y1,0))) curve = arc1.Curve.AngleXU/pi*180 #curve = arc1.AngleXU/pi*180 #print(curve) if curve > 0: curve = -1*curve #print('inverting') #arc1.reverse(); #Part.show(arc1);print(curve) #;stop [x2, y2] = rotPoint2([x1, y1], [xs, ys], curve) else: curve = a.angle [x2, y2] = rotPoint2([x1, y1], [xs, ys], curve) arc1 = Part.Edge(Part.Arc(Base.Vector(x2,-y2,0),mid_point(Base.Vector(x2,-y2,0),Base.Vector(x1,-y1,0),curve),Base.Vector(x1,-y1,0))) # if curve>0: # arc = Part.makeCircle(r,center,Vector(0,0,1),a-angle,a) # arc.reverse(); # else: # arc = Part.makeCircle(r,center,Vector(0,0,1),a,a-angle) Cntr = arc1.Curve.Center #Cntr = arc1.Center cx=Cntr.x;cy=Cntr.y #print cx,cy r = arc1.Curve.Radius #r = arc1.Radius #r=arcRadius(xs, ys, x1, y1, curve) #sa = arc1.Curve.FirstAngle #ea = arc1.Curve.LastAngle #sa,ea = arcAngles2(xs, ys, x1, y1, cx, cy, curve) sa,ea = arcAngles2(arc1,curve) #print sa,';',ea #print mid_point(Base.Vector(x2,-y2,0),Base.Vector(x1,-y1,0),curve) #[mx,my]=arcMidPoint([xs,ys], [x1,y1], curve) #c=arc1.Curve.Center #print c #App.ActiveDocument.PCB_SketchN.addGeometry(Part.Arc(Base.Vector(x2,-y2,0),mid_point(Base.Vector(x2,-y2,0),Base.Vector(x1,-y1,0),curve),Base.Vector(x1,-y1,0))) if load_sketch: line1="" if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx-off_x,cy-off_y,0),FreeCAD.Vector(0,0,1),r),sa,ea),False) #PCB_Geo.append(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx-off_x,cy-off_y,0),FreeCAD.Vector(0,0,1),r),sa,ea)) if hasattr (a, 'mid'): PCB_Geo.append(Part.ArcOfCircle(kicad_parser.makeVect([a.start[0]-off_x,a.start[1]+off_y]), kicad_parser.makeVect([a.mid[0]-off_x,a.mid[1]+off_y]), kicad_parser.makeVect([a.end[0]-off_x,a.end[1]+off_y]))) # print('a.start=',a.start,'a.mid=',a.mid,'a.end=',a.end, 'off_x=',off_x, 'off_y=',off_y) # Part.show(Part.ArcOfCircle(kicad_parser.makeVect([a.start[0]-off_x,a.start[1]+off_y]), # kicad_parser.makeVect([a.mid[0]-off_x,a.mid[1]+off_y]), # kicad_parser.makeVect([a.end[0]-off_x,a.end[1]+off_y])).toShape()) # print(pa) if pa!="": if not(pa.end == a.start): #PCB_Geo.append(PLine(Base.Vector(r.end[0]-off_x,-r.end[1]-off_y,0), Base.Vector(r.start[0]-off_x,-r.end[1]-off_y,0))) # PCB_Geo.append(PLine(kicad_parser.makeVect([pa.end[0]-off_x,pa.end[1]+off_y]),(kicad_parser.makeVect([a.start[0]-off_x,a.start[1]+off_y])))) ln=_ln(0,0,0,0) #class _ln #ln.start = [pa.end[0]-off_x,pa.end[1]+off_y] ln.start = [pa.end[0],pa.end[1]] #ln.end = [a.start[0]-off_x,a.start[1]+off_y] ln.end = [a.start[0],a.start[1]] make_gr_line_obj(ln,add_ply=True) # line1=Part.Edge(PLine(kicad_parser.makeVect([pa.end[0]-off_x,pa.end[1]+off_y]),PLine(kicad_parser.makeVect([a.start[0]-off_x,a.start[1]+off_y])))) else: PCB_Geo.append(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx-off_x,cy-off_y,0),FreeCAD.Vector(0,0,1),r),sa,ea)) else: if hasattr (a, 'mid'): PCB_Geo.append(Part.ArcOfCircle(kicad_parser.makeVect(a.start), kicad_parser.makeVect(a.mid), kicad_parser.makeVect(a.end))) if pa!="": if not(pa.end == a.start): # PCB_Geo.append(PLine(kicad_parser.makeVect(pa.end),PLine(kicad_parser.makeVect(a.start)),0)) # line1=Part.Edge(PLine(kicad_parser.makeVect(pa.end),PLine(kicad_parser.makeVect(a.start)))) ln=_ln(0,0,0,0) #class _ln ln.start = [pa.end[0],pa.end[1]] ln.end = [a.start[0],a.start[1]] make_gr_line_obj(ln,add_ply=True) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx,cy,0),FreeCAD.Vector(0,0,1),r),sa,ea),False) PCB_Geo.append(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx,cy,0),FreeCAD.Vector(0,0,1),r),sa,ea)) #mp=mid_point(Base.Vector(x2,-y2,0),Base.Vector(x1,-y1,0),curve) #msg1= "App.ActiveDocument.PCB_SketchN.addGeometry(Part.Arc(Base.Vector({0},-{1},0),{4},Base.Vector({2},-{3},0)))".format(x2,y2,x1,y1,mp) #print msg1 #App.ActiveDocument.Sketch.addGeometry(Part.Arc(App.Vector(33.0,66.5,0.3),App.Vector(32.85857864376269,66.44142135623731,0.3),App.Vector(32.8,66.3,0.3))) edges.append(arc1) if line1!="": edges.append(line1) PCB.append(['Arc',x1, -y1, x2, -y2, curve]) if show_border: Part.show(arc1) if line1!="": Part.show(line1) except: sayw('skipping wrong geometry') pass # k_index = 0 for lp in mypcb.gr_poly: #pcb polylines if lyr not in lp.layer: # if lp.layer != 'Edge.Cuts': continue ply_lines = [] ind = 0 if hasattr(lp.pts,"xy"): l = len(lp.pts.xy) for p in lp.pts.xy: if ind == 0: line1=Part.Edge(PLine(Base.Vector(lp.pts.xy[l-1][0],-lp.pts.xy[l-1][1],0), Base.Vector(lp.pts.xy[0][0],-lp.pts.xy[0][1],0))) edges.append(line1); if load_sketch: if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0]-off_x,-l.start[1]-off_y,0), Base.Vector(l.end[0]-off_x,-l.end[1]-off_y,0))) PCB_Geo.append(PLine(Base.Vector(lp.pts.xy[l-1][0]-off_x,-lp.pts.xy[l-1][1]-off_y,0), Base.Vector(lp.pts.xy[0][0]-off_x,-lp.pts.xy[0][1]-off_y,0))) if lp.layer != 'Edge.Cuts': line2=Part.Edge(PLine(Base.Vector(lp.pts.xy[l-1][0]-off_x,-lp.pts.xy[l-1][1]-off_y,0), Base.Vector(lp.pts.xy[0][0]-off_x,-lp.pts.xy[0][1]-off_y,0))) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) PCB_Geo.append(PLine(Base.Vector(lp.pts.xy[l-1][0],-lp.pts.xy[l-1][1],0), Base.Vector(lp.pts.xy[0][0],-lp.pts.xy[0][1],0))) if lp.layer != 'Edge.Cuts': line2=Part.Edge(PLine(Base.Vector(lp.pts.xy[l-1][0],-lp.pts.xy[l-1][1],0), Base.Vector(lp.pts.xy[0][0],-lp.pts.xy[0][1],0))) if lp.layer != 'Edge.Cuts': ply_lines.append(line2) else: line1=Part.Edge(PLine(Base.Vector(lp.pts.xy[ind-1][0],-lp.pts.xy[ind-1][1],0), Base.Vector(lp.pts.xy[ind][0],-lp.pts.xy[ind][1],0))) edges.append(line1); if load_sketch: if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0]-off_x,-l.start[1]-off_y,0), Base.Vector(l.end[0]-off_x,-l.end[1]-off_y,0))) PCB_Geo.append(PLine(Base.Vector(lp.pts.xy[ind-1][0]-off_x,-lp.pts.xy[ind-1][1]-off_y,0), Base.Vector(lp.pts.xy[ind][0]-off_x,-lp.pts.xy[ind][1]-off_y,0))) if lp.layer != 'Edge.Cuts': line2=Part.Edge(PLine(Base.Vector(lp.pts.xy[ind-1][0]-off_x,-lp.pts.xy[ind-1][1]-off_y,0), Base.Vector(lp.pts.xy[ind][0]-off_x,-lp.pts.xy[ind][1]-off_y,0))) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(PLine(Base.Vector(l.start[0],-l.start[1],0), Base.Vector(l.end[0],-l.end[1],0))) PCB_Geo.append(PLine(Base.Vector(lp.pts.xy[ind-1][0],-lp.pts.xy[ind-1][1],0), Base.Vector(lp.pts.xy[ind][0],-lp.pts.xy[ind][1],0))) if lp.layer != 'Edge.Cuts': line2=Part.Edge(PLine(Base.Vector(lp.pts.xy[ind-1][0],-lp.pts.xy[ind-1][1],0), Base.Vector(lp.pts.xy[ind][0],-lp.pts.xy[ind][1],0))) if lp.layer != 'Edge.Cuts': ply_lines.append(line2) ind+=1 else: # hasattr(params.pts,"arc") pa="" a0="" for a in lp.pts.arc: if a0=="": a0=a make_gr_arc_obj(a,pa) pa = a if a0.start!=a.end: # and len(lp.pts.arc)>2: print('edge to add') ln.start=a.end ln.end=a0.start make_gr_line_obj(ln,add_ply=True) #print(lp.layer) if 0: #'Edge.Cuts' not in lp.layer: Draft.makeSketch(ply_lines) #TBD allow gr_arcs also on different layers ndsk = FreeCAD.ActiveDocument.ActiveObject ndsk.Label = sk_label + '_Poly_' + str(k_index) ndsk.ViewObject.LineColor = (1.00,1.00,1.00) ndsk.ViewObject.PointColor = (1.00,1.00,1.00) k_index += 1 #closing edge #bsplines for bs in mypcb.gr_curve: # if bs.layer != 'Edge.Cuts': if lyr not in bs.layer: continue ind = 0 #sayerr(bs.layer) poles = [] for p in bs.pts.xy: # sayerr(p) poles.append(FreeCAD.Vector (p[0]-off_x,-p[1]-off_y,0.0)) # sayerr(poles) spline=Part.BSplineCurve() spline.buildFromPoles(poles, False, 3) edges.append(Part.Edge(spline)) #stop #edges.append(Part.Edge(spline2)) # Part.show(spline.toShape()) # import kicadStepUptools; import importlib; importlib.reload(kicadStepUptools);kicadStepUptools.open(u"C:/Temp/bspline.kicad_pcb") if load_sketch: if aux_orig ==1 or grid_orig ==1: PCB_Geo.append(spline) # pi = 0 #for p in bs.pts.xy: # if (pi == 1) or (pi == 2): # PCB_Geo.append(Part.Circle (FreeCAD.Vector(p[0]-off_x, -p[1]-off_y), FreeCAD.Vector(0, 0, 1), 0.5)) # l = len(PCB_Geo) # print(PCB_Geo[l-1].Construction) # PCB_Geo[l-1].Construction = True # #PCB_Geo.append(Part.Circle (0.5, Base.Vector(p[0]-off_x, -p[1]-off_y, 0.0), Base.Vector(1,0,0))) # pi+=1 #for p in bs.pts.xy: # if (pi == 0) or (pi == 4): # PCB_Geo.append(Part.Point (FreeCAD.Vector(p[0]-off_x, -p[1]-off_y, 0.0))) # l = len(PCB_Geo) # print(PCB_Geo[l-1].Construction) # PCB_Geo[l-1].Construction = True # #PCB_Geo.append(Part.Circle (0.5, Base.Vector(p[0]-off_x, -p[1]-off_y, 0.0), Base.Vector(1,0,0))) # pi+=1 else: PCB_Geo.append(spline) # pi = 0 # for p in bs.pts.xy: # if (pi == 1) or (pi == 2): # PCB_Geo.append(Part.makeCircle (0.5, Base.Vector(p[0]-off_x, -p[1]-off_y, 0.0), Base.Vector(1,0,0))) # pi+=1 #stop ## NB use always float() to guarantee number not string!!! pa="" for a in mypcb.gr_arc: #pcb arcs # if a.layer != 'Edge.Cuts': if lyr not in a.layer: continue # for gr_arc, 'start' is actual the center, and 'end' is the start #edges.append(makeArc(makeVect(l.start),makeVect(l.end),l.angle)) make_gr_arc_obj(a,pa) # pa=a ## NB use always float() to guarantee number not string!!! for c in mypcb.gr_circle: #pcb circles # if c.layer != 'Edge.Cuts': if lyr not in c.layer: continue [xs, ys] = c.center [x1, y1] = c.end ys=-ys;y1=-y1 #say(xs); say(ys) r = sqrt((xs - x1) ** 2 + (ys - y1) ** 2) #sayerr(r) if r != 0.0: circle1=Part.Edge(Part.Circle(Base.Vector(xs, ys,0), Base.Vector(0, 0, 1), r)) if load_sketch: if aux_orig ==1 or grid_orig ==1: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(Part.Circle(Base.Vector(xs-off_x, ys-off_y,0), Base.Vector(0, 0, 1), r)) PCB_Geo.append(Part.Circle(Base.Vector(xs-off_x, ys-off_y,0), Base.Vector(0, 0, 1), r)) else: #FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(Part.Circle(Base.Vector(xs, ys,0), Base.Vector(0, 0, 1), r)) PCB_Geo.append(Part.Circle(Base.Vector(xs, ys,0), Base.Vector(0, 0, 1), r)) if show_border: Part.show(circle1) circle1=Part.Wire(circle1) circle1=Part.Face(circle1) if show_shapes: Part.show(circle1) say('2d circle closed path') PCBs.append(circle1) PCB.append(['Circle', xs, ys, r]) else: sayw('circle with 0 radius at '+str(c.center)) #say(PCBs) get_time() say('parsing&building time ' +str(round(running_time-t0,3))) if 0: new_cpy_skt = FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.PCB_Sketch_draft, True) FreeCAD.ActiveDocument.addObject("Part::Face", "Face_PCB_Sketch_draft").Sources = (new_cpy_skt, ) FreeCAD.ActiveDocument.recompute() s_PCB_Sketch_draft = FreeCAD.ActiveDocument.getObject("Face_PCB_Sketch_draft").Shape Part.show(s_PCB_Sketch_draft) FreeCAD.ActiveDocument.removeObject("Face_PCB_Sketch_draft") FreeCAD.ActiveDocument.recompute() make_face = True #getting PCB from Sketch use_PCB_Sketch_E = False #getting PCB from Sketch and Fp Edges dont_use_constraints = False create_pcb_from_edges = False create_pcb_basic = False fcv = getFCversion() if fcv[0]==0 and fcv[1] <17: make_face = False create_pcb_from_edges =True if edg_segms > max_edges_admitted: #sayerr('too many segments, skipping sketches & constraints') sayerr('too many segments, skipping ALL constraints') if FreeCAD.GuiUp: from PySide import QtGui QtGui.QApplication.restoreOverrideCursor() d = QtGui.QMessageBox() d.setText("""Warning: High number of entities to join (> """+str(max_edges_admitted)+""")
Constraints will not be applied to PCB Sketch""") d.setInformativeText("This might take a long time or even freeze your computer. Are you sure?") d.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) d.setDefaultButton(QtGui.QMessageBox.Cancel) res = d.exec_() if res == QtGui.QMessageBox.Cancel: FreeCAD.Console.PrintMessage("Aborted\n") stop if 1: dont_use_constraints = True FreeCAD.ActiveDocument.PCB_Sketch_draft.Geometry=PCB_Geo else: make_face = False create_pcb_basic = True else: #say (PCB_Geo) #for g in PCB_Geo: # FreeCAD.ActiveDocument.PCB_Sketch_draft.addGeometry(g) FreeCAD.ActiveDocument.PCB_Sketch_draft.Geometry=PCB_Geo #get_time() #say('adding Geo time ' +str(running_time-t0)) #FreeCAD.ActiveDocument.addObject("Part::Face", "Face").Sources = (FreeCAD.ActiveDocument.getObject(new_skt.Name), ) #FreeCADGui.SendMsgToActiveView("ViewFit") #stop HoleList=[] if lyr == 'Edge.Cuts': TopPadList=[] BotPadList=[] HoleList=[] THPList=[] ## NB use always float() to guarantee number not string!!! warn="" PCB_Models = [] for m in mypcb.module: #parsing modules #check top/bottom for placing 3D models #print(m.tstamp);print(m.fp_text[0][1]) #stop if len(m.at)==2: m_angle=0 else: m_angle=m.at[2] m_at=[m.at[0],-m.at[1]] #y reversed #say(m.layer);stop #HoleList = getPads(board_elab,pcbThickness) # pad virtual=0 dnp = False # say(str(pcbv)) if pcbv <= 5: Ref = get_mod_Ref(m) #print("Ref",Ref) if hasattr(m, 'attr'): if 'virtual' in m.attr: #say('virtual module') virtual=1 else: virtual=0 if get_mod_dnp(m,Ref): #print(Ref, 'has DNP or DNF option as field!') dnp=True elif pcbv > 5: Ref = get_mod_Ref(m) #print("Ref",Ref) if hasattr(m, 'attr'): if 'virtual' in m.attr: #say('virtual module') virtual=1 if 'dnp' in str(m.attr).lower(): #print(Ref, 'has DNP or DNF option as attribute!') dnp=True if get_mod_dnp(m,Ref): #print(Ref, 'has DNP or DNF option as field!') dnp=True if 'smd' not in str(m.attr) and 'through_hole' not in str(m.attr): # 'exclude_from_pos_files' or 'exclude_from_bom' # say('virtual module k>5') virtual=1 else: #say('non virtual module') virtual=0 else: # missing attribute (old 'virtual') -> 'other' #say('virtual module') virtual=1 m_x = float(m.at[0]) m_y = float(m.at[1]) * (-1) m_rot = float(m_angle) #sayw(m.layer);sayerr(LvlTopName) if m.layer == LvlTopName: # top side = "Top" #sayw('top ' + m.layer) else: side = "Bottom" m_rot *= -1 ##bottom 3d model rotation #sayw('bot ' + m.layer) n_md=1 for md in m.model: #parsing 3d model(s) #say (md[0]) #model name #say(md.at.xyz) #say(md.scale.xyz) #say(md.rotate.xyz) error_scale_module=False #say('scale ');sayw(scale_vrml)#; #error_scale_module=False xsc_vrml_val=md.scale.xyz[0] ysc_vrml_val=md.scale.xyz[1] zsc_vrml_val=md.scale.xyz[2] # if scale_vrml!='1 1 1': if float(xsc_vrml_val)!=1 or float(ysc_vrml_val)!=1 or float(zsc_vrml_val)!=1: if "box_mcad" not in md[0] and "cylV_mcad" not in md[0] and "cylH_mcad" not in md[0]: sayw('wrong scale!!! set scale to (1 1 1)') error_scale_module=True #model_list.append(mdl_name[0]) #model=model_list[j]+'.wrl' #if py2: if sys.version_info[0] == 2: #py2 model=md[0].decode("utf-8") #stop else: #py3 model=md[0] # py3 .decode("utf-8") #print (model, ' MODEL', type(model)) #maui test py3 md_hide = False # print('md value',md, len(md)) #hide attribute on 3d model kv6+ try: if len(md) >4: #hide attribute on 3d model if md[1] == 'hide': md_hide = True #print(md[0],'hidden') except: sayerr ('hide attribute on 3d model missing') pass if (virtual==1 and addVirtual==0): model_name='no3Dmodel' side='noLayer' if model: sayw("virtual model "+model+" skipped") #virtual found warning if (dnp and bklist_dnp): model_name='no3Dmodel' side='noLayer' if model: sayw("Reference "+Ref+" skipped for DNP") #DNP found warning blacklisted_model_elements+=Ref+';' else: if model: model_name=model #sayw(model_name) warn="" if "box_mcad" not in model_name and "cylV_mcad" not in model_name and "cylH_mcad" not in model_name: if error_scale_module: sayw('wrong scale!!! for '+model_name+' Set scale to (1 1 1)') msg="""Error in '.kicad_pcb' model footprint
""" msg+="
reset values of
"+model_name+"
to:
" msg+="(scale (xyz 1 1 1))
" #warn+=("reset values of scale to (xyz 1 1 1)") warn=("reset values of scale to (xyz 1 1 1)") ##reply = QtGui.QMessageBox.information(None,"info", msg) #stop #model_name=model_name[1:] #say(model_name) #sayw("here") else: model_name='no3Dmodel' #to do how to manage no3Dmodel side='noLayer' sayerr('no3Dmodel') mdl_name=model_name # re.findall(r'(.+?)\.wrl',params) #if virtual == 1: # sayerr("virtual model(s)");sayw(mdl_name) # sayw(mdl_name) # sayerr(params) if len(mdl_name) > 0: # model_name, rot_comb, warn, pos_vrml, rotz_vrml, scale_vrml = get3DParams(mdl_name,params, rot, virtual) #sayerr(md.at.xyz) if conv_offs != 1: #pcb version >= 20171114 (offset wrl in mm) if hasattr(md,'at'): ofs=[md.at.xyz[0]/conv_offs,md.at.xyz[1]/conv_offs,md.at.xyz[2]/conv_offs] if hasattr(md,'offset'): ofs=[md.offset.xyz[0]/conv_offs,md.offset.xyz[1]/conv_offs,md.offset.xyz[2]/conv_offs] else: ofs=md.at.xyz line = [] line.append(model_name) line.append(m_x) line.append(m_y) line.append(m_rot-md.rotate.xyz[2]) line.append(side) line.append(warn) line.append(ofs) #(md.at.xyz) #pos_vrml) line.append(md.rotate.xyz) #rotz_vrml) #sayerr(rotz_vrml) line.append(md.scale.xyz) #scale_vrml) line.append(virtual) if hasattr(m,'tstamp'): line.append(m.tstamp) # fp tstamp else: sayw('missing \'TimeStamp\'') line.append('null') try: #print(m.fp_text[0]) line.append(m.fp_text[0][1]) #fp reference except: #print(m.property[0]) line.append(m.property[0][1]) #fp reference kv8 line.append(n_md) #number of models in module line.append(md_hide) PCB_Models.append(line) n_md+=1 pads = [] for p in m.pad: if 'drill' not in p: continue #say('drill present') #say (p.at) if len(p.at)>2: #say ('angle '+str(p.at[2])) p_angle=p.at[2] else: p_angle=0.0 #say('drill');say(p.drill) #say('drill size'); if hasattr(p,'drill'): if 'offset' in p.drill: #say('offset');say(p.drill.offset) offset=p.drill.offset else: offset=[0,0] else: sayw('drill size missing'); #say('offset not present') offset=[0,0] #print p.drill.oval #if p.drill.oval: # if p.drill[0] < min_drill_size and p.drill[1] < min_drill_size: # continue #else: # if p.drill[0] < min_drill_size: # continue #say ( p) #if 'circle' in p[2]: ## NB use always float() to guarantee number not string!!! if hasattr(p,'drill'): #sayerr(p.drill); #sayw(p.drill.oval) #if p.drill.oval is not None #if len(p.drill)>1: # if p.drill[1] == 'oval': # drill_oval=True # drill_oval=False # myidx=0 # for dct in p.drill: # #print(dct,' ',myidx) # if dct == 'oval' and myidx == 0: # drill_oval=True # myidx+=1 # if drill_oval: #print (p.drill);print(p.drill.oval);print(str(p.drill).split(',')[0]) #if p.drill.oval is not None: if 'oval' in str(p.drill).split(',')[0]: #py3 dict workaround #if p.drill.oval: #maui temp workaround errorchecking #sayw(str(p.drill.oval)) #sayw('drill oval') # print (str(p.drill).split(',')) try: if p.drill[0] >= min_drill_size or p.drill[1] >= min_drill_size: xs=p.at[0]+m.at[0];ys=-p.at[1]-m.at[1] #x1=mc.end[0]+m.at[0];y1=-mc.end[1]-m.at[1] #radius = float(p.drill[0])/2 #sqrt((xs - x1) ** 2 + (ys - y1) ** 2) rx=float(p.drill[0]) #print (p.drill) if len(p.drill)>2: try: #print (p.drill[1]) #stop ry=float(p.drill[1]) except: ry=rx else: ry=rx #print(p.at[0],p.at[1], p.drill[0]) [x1, y1] = rotPoint2([xs, ys], [m.at[0], -m.at[1]], m_angle) #sayw('holes solid '+str(holes_solid)) if holes_solid: obj=createHole3(x1,y1,rx,ry,"oval",totalHeight) #need to be separated instructions else: obj=createHole4(x1,y1,rx,ry,"oval") #need to be separated instructions if p_angle!=0: rotateObj(obj, [x1, y1, p_angle]) HoleList.append(obj) except: try: sayw('missing drill value on pad for module '+str(m.fp_text[0][1])) except: sayw('missing drill value on pad for module '+str(m.property[0][1])) #elif p.drill[0]!=0: #circle drill hole else: try: # [0] >= min_drill_size: #isinstance(p.drill,list): # for t in m.fp_text: # print(t[1]) if p.drill[0] >= min_drill_size: #xs=p.at[0]+offset[0]+m.at[0];ys=-p.at[1]-offset[1]-m.at[1] xs=p.at[0]+m.at[0];ys=-p.at[1]-m.at[1] #x1=mc.end[0]+m.at[0];y1=-mc.end[1]-m.at[1] radius = float(p.drill[0])#/2 #sqrt((xs - x1) ** 2 + (ys - y1) ** 2) rx=radius;ry=radius #print(p.at[0],p.at[1], p.drill[0]) [x1, y1] = rotPoint2([xs, ys], [m.at[0], -m.at[1]], m_angle) if holes_solid: obj=createHole3(x1,y1,rx,ry,"oval",totalHeight) #need to be separated instructions else: obj=createHole4(x1,y1,rx,ry,"oval") #need to be separated instructions #say(HoleList) #if p_angle!=0: # rotateObj(obj, [x1, y1, p_angle]) #rotateObj(obj, [m.at[0], m.at[1], m_angle]) HoleList.append(obj) except: try: sayw('missing drill value on pad for module '+str(m.fp_text[0][1])) except: sayw('missing drill value on pad for module '+str(m.property[0][1])) ##pads.append({'x': x, 'y': y, 'rot': rot, 'padType': pType, 'padShape': pShape, 'rx': drill_x, 'ry': drill_y, 'dx': dx, 'dy': dy, 'holeType': hType, 'xOF': xOF, 'yOF': yOF, 'layers': layers}) #stop if hasattr(m, 'fp_poly'): for lp in m.fp_poly: #print(lp.layer) if 'Edge.Cuts' not in lp.layer: continue # print(m.layer) # if m.layer != 'Edge.Cuts': # print(lp) # print(lp.pts) # print(lp.pts.xy) # for p in lp.pts.xy: # print(p) #if lp.layer != 'F.Cu': if 'F.Cu' not in m.layer: continue #print ml.start,ml.end ind = 0 l = len(lp.pts.xy) #print(lp) for p in lp.pts.xy: #print('p',p) if ind == 0: line1=Part.Edge(PLine(Base.Vector(lp.pts.xy[l-1][0],-lp.pts.xy[l-1][1],0), Base.Vector(lp.pts.xy[0][0],-lp.pts.xy[0][1],0))) edges.append(line1); else: line1=Part.Edge(PLine(Base.Vector(lp.pts.xy[ind-1][0],-lp.pts.xy[ind-1][1],0), Base.Vector(lp.pts.xy[ind][0],-lp.pts.xy[ind][1],0))) edges.append(line1); ind+=1 EdgeCuts.append(line1) #print(line1.Vertexes[0].Point.x,line1.Vertexes[0].Point.y) #line1.Vertexes[1].Point) pt1 = (line1.Vertexes[0].Point.x,-line1.Vertexes[0].Point.y) pt2 = (line1.Vertexes[1].Point.x,-line1.Vertexes[1].Point.y) x1=pt1[0]+m.at[0];y1=-pt1[1]-m.at[1] x2=pt2[0]+m.at[0];y2=-pt2[1]-m.at[1] [x1, y1] = rotPoint2([x1,y1], [m.at[0], -m.at[1]], m_angle) [x2, y2] = rotPoint2([x2,y2], [m.at[0], -m.at[1]], m_angle) if aux_orig ==1 or grid_orig ==1: FpEdges_Geo.append(PLine(Base.Vector(x1-off_x,y1-off_y,0), Base.Vector(x2-off_x,y2-off_y,0))) else: FpEdges_Geo.append(PLine(Base.Vector(x1,y1,0), Base.Vector(x2,y2,0))) PCB.append(['Line', x1, y1, x2, y2]) #closing edge if show_border: #0: #SHOW POLY BORDER Part.show(line1) #stop if min_drill_size == -1: for v in mypcb.via: # based on code above for pads if 'drill' not in v: continue if hasattr(v,'drill'): #if drill_present: if v.drill >= min_drill_size: x1=v.at[0];y1=-v.at[1] radius = float(v.drill) rx=radius;ry=radius if holes_solid: obj=createHole3(x1,y1,rx,ry,"oval",totalHeight) #need to be separated instructions else: obj=createHole4(x1,y1,rx,ry,"oval") #need to be separated instructions HoleList.append(obj) else: sayw('drill size missing'); for ml in m.fp_line: # if ml.layer != 'Edge.Cuts': if lyr not in ml.layer: continue #print ml.start,ml.end x1=ml.start[0]+m.at[0];y1=-ml.start[1]-m.at[1] x2=ml.end[0]+m.at[0];y2=-ml.end[1]-m.at[1] [x1, y1] = rotPoint2([x1,y1], [m.at[0], -m.at[1]], m_angle) [x2, y2] = rotPoint2([x2,y2], [m.at[0], -m.at[1]], m_angle) if (Base.Vector(x1,y1,0)) != (Base.Vector(x2,y2,0)): #non coincident points line1=Part.Edge(PLine(Base.Vector(x1,y1,0), Base.Vector(x2,y2,0))) edges.append(line1); EdgeCuts.append(line1) if aux_orig ==1 or grid_orig ==1: FpEdges_Geo.append(PLine(Base.Vector(x1-off_x,y1-off_y,0), Base.Vector(x2-off_x,y2-off_y,0))) else: FpEdges_Geo.append(PLine(Base.Vector(x1,y1,0), Base.Vector(x2,y2,0))) PCB.append(['Line', x1, y1, x2, y2]) if show_border: Part.show(line1) for mc in m.fp_circle: # if mc.layer != 'Edge.Cuts': if lyr not in mc.layer: continue xs=mc.center[0]+m.at[0];ys=-mc.center[1]-m.at[1] x1=mc.end[0]+m.at[0];y1=-mc.end[1]-m.at[1] radius = sqrt((xs - x1) ** 2 + (ys - y1) ** 2) [xc, yc] = rotPoint2([xs, ys], [m.at[0], -m.at[1]], m_angle) circle1=Part.Edge(Part.Circle(Base.Vector(xc, yc,0), Base.Vector(0, 0, 1), radius)) # print (mc.center,mc.end) # print (xs,ys) # print(radius) circle2=circle1 if show_border: Part.show(circle1) circle1=Part.Wire(circle1) circle1=Part.Face(circle1) if show_shapes: Part.show(circle1) say('2d circle closed path') PCBs.append(circle1) EdgeCuts.append(circle2) if aux_orig ==1 or grid_orig ==1: FpEdges_Geo.append(Part.Circle(Base.Vector(xc-off_x, yc-off_y,0), Base.Vector(0, 0, 1), radius)) else: FpEdges_Geo.append(Part.Circle(Base.Vector(xc, yc,0), Base.Vector(0, 0, 1), radius)) PCB.append(['Circle', x1, y1, radius]) #mod_circles.append (['Circle', x1, y1, e[2]]) #PCB.append(['Circle', x1, y1, radius]) for ma in m.fp_arc: # if ma.layer != 'Edge.Cuts': if lyr not in ma.layer: continue #print ma.start, ma.end, ma.angle #xs=ma.start[0]+m.at[0];ys=-ma.start[1]-m.at[1] #x1=ma.end[0]+m.at[0];y1=-ma.end[1]-m.at[1] xs=ma.start[0];ys=ma.start[1] x1=ma.end[0];y1=ma.end[1] if hasattr (ma, 'mid'): [xm, ym] = ma.mid #arc2 = Part.Edge(Part.Arc(Base.Vector(xs,-ys,0),Base.Vector(xm,-ym,0),Base.Vector(x1,-y1,0))) arc1 = Part.Edge(Part.ArcOfCircle(kicad_parser.makeVect(ma.start), kicad_parser.makeVect(ma.mid), kicad_parser.makeVect(ma.end)).toShape()) arc1.rotate(Vector(),Vector(0,0,1),m_angle) if aux_orig ==1 or grid_orig ==1: mat_t=(m.at[0]-off_x,-m.at[1]-off_y,0) else: mat_t=(m.at[0],-m.at[1],0) arc1.translate(mat_t) edges.append(arc1) EdgeCuts.append(arc1) delta_angle=degrees(arc1.LastParameter-arc1.FirstParameter) curve = delta_angle [x2, y2] = [xs,ys] #rotPoint2([x1, y1], [xs, ys], curve) sa,ea = arcAngles2(arc1,curve) Cntr = arc1.Curve.Center cx=Cntr.x;cy=Cntr.y #print cx,cy r = arc1.Curve.Radius skt = Draft.makeSketch(arc1) FpEdges_Geo.append(skt.Geometry[0]) FreeCAD.ActiveDocument.removeObject(skt.Name) else: curve = ma.angle [x2, y2] = rotPoint2([x1, y1], [xs, ys], curve) y1=-y1-m.at[1]; y2=-y2-m.at[1] x1+=m.at[0];x2+=m.at[0] [x1, y1] = rotPoint2([x1, y1], [m.at[0], -m.at[1]], m_angle) [x2, y2] = rotPoint2([x2, y2], [m.at[0], -m.at[1]], m_angle) arc1=Part.Edge(Part.Arc(Base.Vector(x2,y2,0),mid_point(Base.Vector(x2,y2,0),Base.Vector(x1,y1,0),curve),Base.Vector(x1,y1,0))) edges.append(arc1) EdgeCuts.append(arc1) sa,ea = arcAngles2(arc1,curve) Cntr = arc1.Curve.Center cx=Cntr.x;cy=Cntr.y #print cx,cy r = arc1.Curve.Radius if aux_orig ==1 or grid_orig ==1: FpEdges_Geo.append(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx-off_x,cy-off_y,0),FreeCAD.Vector(0,0,1),r),sa,ea)) else: FpEdges_Geo.append(Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(cx,cy,0),FreeCAD.Vector(0,0,1),r),sa,ea)) if show_border: Part.show(arc1) PCB.append(['Arc', x1, y1, x2, y2, curve]) if 0: #this is not needed anymoore because we have built a PCB_Sketch_draft from Edges if len(EdgeCuts) >0 and not create_pcb_from_edges: try: s_PCB_Cuts = OSCD2Dg_edgestofaces(EdgeCuts,3 , edge_tolerance) HoleList.append(s_PCB_Cuts) except: sayerr('error in making footprint Edge Cuts') elif len(EdgeCuts) > 0: try: s_PCB_Cuts = OSCD2Dg_edgestofaces(EdgeCuts,3 , edge_tolerance) #Part.show(s_PCB_Cuts) except: sayerr('error in making PCB from footprint Edge Cuts') #creating a sketch with fp edges if len(EdgeCuts) > 0: PCB_Sketch_draft_E= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PCB_Sketch_draft_E') FreeCAD.activeDocument().PCB_Sketch_draft_E.Placement = FreeCAD.Placement(FreeCAD.Vector(0.000000,0.000000,0.000000),FreeCAD.Rotation(0.000000,0.000000,0.000000,1.000000)) FreeCAD.ActiveDocument.PCB_Sketch_draft_E.Geometry=PCB_Geo+FpEdges_Geo use_PCB_Sketch_E = True if lyr == 'Edge.Cuts': if make_face: #creation pcb from PCB sketch draft if use_PCB_Sketch_E: PCB2Sketch=FreeCAD.ActiveDocument.PCB_Sketch_draft_E else: PCB2Sketch=FreeCAD.ActiveDocument.PCB_Sketch_draft if len(PCB2Sketch.Geometry)>0: if addConstraints!='none' and not dont_use_constraints: say('start adding constraints to pcb sketch') get_time() t0=(running_time) if hasattr (FreeCAD.ActiveDocument.getObject(PCB2Sketch.Name), "autoconstraint"): if addConstraints=='full': FreeCAD.ActiveDocument.getObject("PCB_Sketch_draft").autoconstraint(edge_tolerance*5,0.01) if use_PCB_Sketch_E: FreeCAD.ActiveDocument.getObject("PCB_Sketch_draft_E").autoconstraint(edge_tolerance*5,0.01) else: add_constraints("PCB_Sketch_draft") if use_PCB_Sketch_E: add_constraints("PCB_Sketch_draft_E") else: add_constraints("PCB_Sketch_draft") if use_PCB_Sketch_E: add_constraints("PCB_Sketch_draft_E") get_time() #say('adding constraints time ' +str(running_time-t0)) say('adding constraints time ' + "{0:.3f}".format(running_time-t0)) if 0: #dont_use_constraints: sayw('adding missing geometry') add_missing_geo("PCB_Sketch_draft") #stop make_face_mode = 1 FreeCAD.ActiveDocument.recompute() if make_face_mode == 1: ## face creation method shsk = PCB2Sketch.Shape.copy() #Part.show(shsk) edgs = shsk.Edges #print(edgs) if len(edgs)== 0: sayerr('No edges found for pcb!!!') stop if 1: s_PCB_Sketch_draft = OSCD2Dg_edgestofaces(edgs,3 , edge_tolerance) if 0: import OpenSCAD2Dgeom s_PCB_Sketch_draft = OpenSCAD2Dgeom.edgestofaces(Part.__sortEdges__(edgs)) Part.show(s_PCB_Sketch_draft) Face_PCB_Sketch_draft = FreeCAD.ActiveDocument.ActiveObject s_PCB_Sketch_draft = Face_PCB_Sketch_draft.Shape.copy() ## OpenSCAD2Dgeom.edgestofaces(edgs) else: FreeCAD.ActiveDocument.addObject("Part::Face", "Face_PCB_Sketch_draft").Sources = (PCB2Sketch, ) #(FreeCAD.ActiveDocument.PCB_Sketch_draft, ) FreeCAD.ActiveDocument.recompute() s_PCB_Sketch_draft = FreeCAD.ActiveDocument.getObject("Face_PCB_Sketch_draft").Shape.copy() #s_PCB_Sketch_draft = s.copy() sayw ('created PCB face w/ edge tolerance -> '+str(edge_tolerance)+' mm') if aux_orig ==1 or grid_orig ==1: s_PCB_Sketch_draft.translate((off_x, off_y,0)) #else: # s_PCB_Sketch_draft.translate(xs, ys,0) #if use_Links: #Part.show(s_PCB_Sketch_draft) if make_face_mode == 1: ## face creation method #if 0: FreeCAD.ActiveDocument.removeObject(Face_PCB_Sketch_draft.Name) if use_PCB_Sketch_E: FreeCAD.ActiveDocument.removeObject(PCB_Sketch_draft_E.Name) FreeCAD.ActiveDocument.recompute() else: FreeCAD.ActiveDocument.removeObject("Face_PCB_Sketch_draft") if use_PCB_Sketch_E: FreeCAD.ActiveDocument.removeObject(PCB_Sketch_draft_E.Name) FreeCAD.ActiveDocument.recompute() #FreeCAD.ActiveDocument.addObject("Part::Face", "Face").Sources = (FreeCAD.ActiveDocument.PCB_Sketch_draft001, ) if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") cut_base = s_PCB_Sketch_draft else: sayerr('empty sketch; module edge board: creating PCB from Footprint Edge.Cuts') create_pcb_from_edges = True LCS = None #if aux_orig ==1 or grid_orig ==1: if origin is not None: #adding LCS only on aux or grid origin try: FreeCAD.ActiveDocument.addObject('PartDesign::CoordinateSystem','Local_CS'+fname_sfx) LCS = FreeCAD.ActiveDocument.ActiveObject FreeCADGui.ActiveDocument.getObject(LCS.Name).Visibility = False FreeCADGui.Selection.clearSelection() #FreeCADGui.Selection.addSelection(LCS) #FreeCADGui.runCommand('Std_HideSelection',0) #FreeCADGui.runCommand('Std_ToggleVisibility',0) #FreeCADGui.Selection.clearSelection() except: sayw('LCS not supported') if 0: Part.show(s_PCB_Cuts) fc_PCB_Cuts = FreeCAD.ActiveDocument.ActiveObject face_PCB_Cuts = fc_PCB_Cuts.Shape.copy() if aux_orig ==1 or grid_orig ==1: face_PCB_Cuts.translate((-off_x, -off_y,0)) Part.show(face_PCB_Cuts) #obj = FreeCAD.ActiveDocument.ActiveObject #stop #HoleList.append(face_PCB_Cuts) #stop PCB_Cuts #sayw(len(HoleList)) #say (PCB_Models) #stop #g = App.ActiveDocument.PCB_Sketch.Geometry #g = App.ActiveDocument.PCB_Sketch # For each sketch geometry type, map a list of points to move. # Direct access to sketch.Geometry[index] does not work. This would, # however prevent repeated recompute. #for point_index in point_indexes: # point = PCB_Sketch.getPoint(geom_index, point_index) # sayerr (point) # sayw(point[0]);sayw(point[1]) # ## ckeck point coincidence for sketch constrains # #sketch.movePoint(geom_index, point_index, point) # #for i, pidx in enumerate(point_indexes): # for pidx2 in point_indexes[(i + 1):]: # point = PCB_Sketch.getPoint(geom_index, pidx) # point2 = PCB_Sketch.getPoint(geom_index, pidx2) # sayerr(pidx);sayw(pidx2) # sayerr('points') # sayw(point[0]);sayw(point[1]);sayw(point2[0]);sayw(point2[1]) # if point[0] == point2[0]: # say('found 00') # PCB_Sketch.addConstraint(Sketcher.Constraint('Coincident',idx,1,idx2,1)) # if point[1] == point2[0]: # say('found 10') # PCB_Sketch.addConstraint(Sketcher.Constraint('Coincident',idx,2,idx2,1)) # if point[0] == point2[1]: # say('found 01') # PCB_Sketch.addConstraint(Sketcher.Constraint('Coincident',idx,1,idx2,2)) # if point[1] == point2[1]: # say('found 11') # PCB_Sketch.addConstraint(Sketcher.Constraint('Coincident',idx,2,idx2,2)) # FreeCAD.ActiveDocument.recompute() #add_constraints(PCB_Sketch_draft.Name) #t_name=cpy_sketch(PCB_Sketch_draft.Name) ##s_name=shift_sketch(PCB_Sketch_draft.Name, [-100,-100]) ##add_constraints(s_name) ##FreeCADGui.SendMsgToActiveView("ViewFit") ##stop #stop #Sketch.addConstraint(Sketcher.Constraint('Coincident',LineFixed,PointOfLineFixed,LineMoving,PointOfLineMoving)) #for geom in PCB_Sketch.Geometry: # #if isinstance(geom, Part.Line): # # bbox.enlarge_line(geom) # if isinstance(geom, Part.Circle): # say("Circle") # elif isinstance(geom, Part.ArcOfCircle): # say("Arc") #for i, e1 in enumerate(g): # for e2 in g[(i + 1):]: # sayw(e2.SubObjects[0].Curve) #sort edges to form a single closed 2D shape loopcounter = 0 #sayw((edges)) #stop # for f in PCBs: # Part.show(f) # stop if create_pcb_from_edges: #if not test_face: #sayerr('doing') if (len(edges)==0) and (len(PCBs)==0): sayw("no PCBs found") else: sayerr('creating pcb from edges instead of sketch') newEdges = []; if (len(edges)>0): newEdges.append(edges.pop(0)) #say(newEdges[0]) #print [newEdges[0].Vertexes[0].Point] #print [newEdges[0].Vertexes[-1].Point] #say(str(len(newEdges[0].Vertexes))) nextCoordinate = newEdges[0].Vertexes[0].Point firstCoordinate = newEdges[0].Vertexes[-1].Point #nextCoordinate = newEdges[0].Curve.EndPoint #firstCoordinate = newEdges[0].Curve.StartPoint if apply_edge_tolerance: for e in edges: for v in e.Vertexes: v.setTolerance(edge_tolerance) #adding tolerance to vertex if show_data: # print findWires(edges) sayw(len(edges)) for e in edges: sayw(e.Vertexes[0].Point);sayw(e.Vertexes[-1].Point) for e in edges: sayw("geomType") say(DraftGeomUtils.geomType(e)) #if show_data: # sayw(enumerate(edges)); while(len(edges)>0 and loopcounter < 2): loopcounter = loopcounter + 1 #print "nextCoordinate: ", nextCoordinate #if len(newEdges[0].Vertexes) > 1: # not circle for j, edge in enumerate(edges): #for j in range (len(edges)): #print "compare to: ", edges[j].Curve.StartPoint, "/" , edges[j].Curve.EndPoint #if edges[j].Curve.StartPoint == nextCoordinate: if show_data: say(distance(edges[j].Vertexes[-1].Point, nextCoordinate)) say(distance(edges[j].Vertexes[0].Point, nextCoordinate)) #if edges[j].Vertexes[-1].Point == nextCoordinate: # sayw(distance(edges[j].Vertexes[-1].Point, nextCoordinate)) # sayw(distance(edges[j].Vertexes[0].Point, nextCoordinate)) if distance(edges[j].Vertexes[-1].Point, nextCoordinate)<=edge_tolerance: #if edges[j].Vertexes[-1].Point != nextCoordinate: if distance(edges[j].Vertexes[-1].Point, nextCoordinate)>edge_tolerance_warning: sayerr('non coincident edges:\n'+str(nextCoordinate)+';'+str(edges[j].Vertexes[-1].Point)) nextCoordinate = edges[j].Vertexes[0].Point newEdges.append(edges.pop(j)) loopcounter = 0 break #elif edges[j].Vertexes[0].Point == nextCoordinate: elif distance(edges[j].Vertexes[0].Point, nextCoordinate)<=edge_tolerance: #if edges[j].Vertexes[0].Point != nextCoordinate: if distance(edges[j].Vertexes[0].Point, nextCoordinate)>edge_tolerance_warning: sayerr('non coincident edges:\n'+str(nextCoordinate)+';'+str(edges[j].Vertexes[0].Point)) nextCoordinate = edges[j].Vertexes[-1].Point newEdges.append(edges.pop(j)) loopcounter = 0 break if show_data: say ("first c" + str(firstCoordinate)); say(' '); say ("last c" + str(nextCoordinate)) #if nextCoordinate == firstCoordinate: if distance(firstCoordinate, nextCoordinate)<=edge_tolerance: say('2d closed path') try: # maui #say('\ntrying wire & face') #newEdges_old=newEdges ## newEdges = Part.Wire(newEdges) #say('trying face') ## newEdges = Part.Face(newEdges) #newEdges = OpenSCAD2DgeomMau.edgestofaces(newEdges) newEdges = OSCD2Dg_edgestofaces(newEdges,3 , edge_tolerance) newEdges.check() # reports errors newEdges.fix(0,0,0) #say('done') #newEdges.translate(Base.Vector(0,0,-totalHeight)) if show_shapes: Part.show(newEdges) #newEdges = newEdges.extrude(Base.Vector(0,0,totalHeight)) PCBs.append(newEdges) if (len(edges)>0): newEdges = []; newEdges.append(edges.pop(0)) nextCoordinate = newEdges[0].Vertexes[0].Point firstCoordinate = newEdges[0].Vertexes[-1].Point except Part.OCCError: # Exception: # say("error in creating PCB") stop if loopcounter == 2: say("*** omitting PCBs because there was a not closed loop in your edge lines ***") say("*** have a look at position x=" + str(nextCoordinate.x) + "mm, y=" + str(nextCoordinate.y) + "mm ***") say('pcb edge not closed') QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, 'Error in creating Board Edge ."+"\r\n"', """pcb edge not closed
review your Board Edges in Kicad!
position x=""" + str(nextCoordinate.x) + 'mm, y=' + str(-nextCoordinate.y) + 'mm') diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #maui if disable_cutting: FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #maui #say (PCBs) ## doc = FreeCAD.activeDocument() ## #outline = [] ## for f in PCBs: ## Part.show(f) ## PCB_Sketch= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Sketch') ## FreeCAD.activeDocument().Sketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) ## for s in doc.Objects: ## #for f in PCBs: ## if 'Part' in s.TypeId: #Part.show(s) ## wires,_faces = Draft.downgrade(s,delete=False) ## #Draft.downgrade(FreeCADGui.Selection.getSelection(),delete=True) ## #sketch = Draft.makeSketch(wires[0:1]) ## #sketch.Label = "Sketch_pcb" ## for wire in wires[0:1]: ## Draft.makeSketch([wire],addTo=PCB_Sketch) ## for wire in wires: ## FreeCAD.ActiveDocument.removeObject(wire.Name) ## FreeCAD.ActiveDocument.recompute() #for f in PCBs: # Part.hide(f) ##FreeCADGui.SendMsgToActiveView("ViewFit") ##stop maxLenght=0 idx=0 external_idx=idx for extruded in PCBs: #search for orientation of each pcb in 3d space, save it (no transformation yet!) angle = 0; axis = Base.Vector(0,0,1) position = Base.Vector(0,0,0) if show_shapes: Part.show(extruded) #extrude_XLenght=FreeCAD.ActiveDocument.ActiveObject.Shape.BoundBox.XLength # extrude_XLenght=extruded.Length #perimeter extrude_XLenght=extruded.BoundBox.XLength #extrude_XLenght=FreeCAD.ActiveDocument.ActiveObject.Shape.Edges.Length if maxLenght < extrude_XLenght: maxLenght = extrude_XLenght external_idx=idx #say('XLenght='+str(extrude_XLenght)) idx=idx+1 say('max Length='+str(maxLenght)+' index='+str(external_idx)) try: cut_base=PCBs[external_idx] except: say("*** omitting PCBs because there was a not closed loop in your edge lines ***") say('pcb edge not closed') QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, 'Error in creating Board Edge ."+"\r\n"', """pcb edge not closed
review your Board Edges in Kicad!
""") diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #maui i=0 for i in range (len(PCBs)): if i!=external_idx: cutter=PCBs[i] cut_base=cut_base.cut(cutter) if test_extrude: cut_base = cut_base.extrude(Base.Vector(0,0,totalHeight)) if show_shapes: Part.show(cut_base) #cut_base_name=FreeCAD.ActiveDocument.ActiveObject.Name #say('Alive1') if len(PCBs)==1: cut_base = PCBs[0] if test_extrude: cut_base = cut_base.extrude(Base.Vector(0,0,totalHeight)) if show_shapes: Part.show(cut_base) if show_shapes: FreeCAD.activeDocument().removeObject("Shape") ###FreeCAD.ActiveDocument.recompute() if len(PCBs)==0: say('pcb edge not found') QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, 'Error in creating Board Edge ."+"\r\n"', """pcb edge not closed
review your Board Edges in Kicad!
""") diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #maui FreeCAD.ActiveDocument.recompute() FreeCADGui.activeDocument().activeView().viewTop() ##FreeCADGui.SendMsgToActiveView("ViewFit") if create_pcb_basic: ## experimental technique for getting the pcb edge in case of large quantity of segments ## To be completed if (len(edges)==0) and (len(PCBs)==0): sayw("no PCBs found") stop else: sayw('creating pcb from edges without constraints') #N_edges = [] #for s in edges: # N_edges.extend(s.Edges) #if len(edges) > (100): # FreeCAD.Console.PrintMessage(str(len(edges))+" edges to join\n") # if FreeCAD.GuiUp: # from PySide import QtGui # d = QtGui.QMessageBox() # d.setText("Warning: High number of entities to join (>100)") # d.setInformativeText("This might take a long time or even freeze your computer. Are you sure? You can also disable the \"join geometry\" setting in DXF import preferences") # d.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) # d.setDefaultButton(QtGui.QMessageBox.Cancel) # res = d.exec_() # if res == QtGui.QMessageBox.Cancel: # FreeCAD.Console.PrintMessage("Aborted\n") # return if 0: newEdges = OSCD2Dg_edgestofaces(edges,3 , edge_tolerance) Part.show(newEdges) stop if 0: ## from importDXF shapes = DraftGeomUtils.findWires(edges) #N_edges) def addObject(shape,name="Shape",layer=None): "adds a new object to the document with passed arguments" if isinstance(shape,Part.Shape): #say(doc.Name) #stop newob=doc.addObject("Part::Feature",name) newob.Shape = shape else: newob = shape #if layer: # lay=locateLayer(layer) # lay.addObject(newob) #formatObject(newob) return newob shapes_list=[] for s in shapes: newob = addObject(s) shapes_list.append(newob) WireSketch = FreeCAD.activeDocument().addObject('Sketcher::SketchObject','WireSketch') shapes = Draft.makeSketch(shapes,autoconstraints=True,addTo=WireSketch) FreeCAD.ActiveDocument.addObject("Part::Face", "Face_WireSketch").Sources = (FreeCAD.ActiveDocument.WireSketch, ) if 0: FreeCAD.activeDocument().addObject("Part::Compound","ShapesCompound") FreeCAD.activeDocument().ShapesCompound.Links = shapes_list FreeCAD.ActiveDocument.addObject("Part::Face", "Face_Compound").Sources = (FreeCAD.ActiveDocument.ShapesCompound, ) FreeCAD.ActiveDocument.recompute() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #fusion_wire = edges[0] #for no, e in enumerate(edges): # no += 1 # if no > 1: # fusion_wire = fusion_wire.fuse(e) # Part.show(fusion_wire) for e in edges: Part.show(e) stop #w_pcb = Part.Wire(edges) #Part.show(w_pcb) say_time() #cut_base = cut_base.extrude(Base.Vector(0,0,totalHeight)) # test_face #Part.show(cut_base) #test Sketch #stop #PCB_Sketch= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PCB_Sketch') #FreeCAD.activeDocument().PCB_Sketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) doc = FreeCAD.activeDocument() #wires,_faces = Draft.downgrade(cut_base,delete=False) #edges=[] #for f in cut_base.Faces: # #print f.Edges # edges.append(f.Edges) #for e in edges: # print e # print e.Vertexes.Point[0] # print e.Vertexes.Point[-1] ##for w in cut_base.Wires: ## print w ## Draft.makeSketch([w], autoconstraints = True, addTo=PCB_Sketch) ## PCB_Sketch.touch() #Part.Wire (edges) #print edges #for wire in wires: # sayw(wire.Label) # Draft.makeSketch([wire], autoconstraints = True, addTo=PCB_Sketch) # #Draft.makeSketch([wire], autoconstraints = False, addTo=PCB_Sketch) # PCB_Sketch.touch() if 0: #old Sketch method FreeCAD.ActiveDocument.recompute() wires,_faces = Draft.downgrade(FreeCAD.ActiveDocument.Shape,delete=False) FreeCAD.ActiveDocument.recompute() for wire in wires: Draft.makeSketch([wire],autoconstraints=True, addTo=PCB_Sketch) #makeSketch_FC_016([wire],autoconstraints=True, addTo=PCB_Sketch) PCB_Sketch.touch() #stop for wire in wires: FreeCAD.ActiveDocument.removeObject(wire.Name) #Draft.makeSketch(FreeCAD.ActiveDocument.Wire,autoconstraints=False, addTo=PCB_Sketch) #FreeCAD.ActiveDocument.recompute() #FreeCAD.ActiveDocument.removeObject(FreeCAD.ActiveDocument.Wire.Name) FreeCAD.ActiveDocument.recompute() #s_name=shift_sketch(PCB_Sketch_draft.Name, [-100,-100]) #add_constraints(s_name) #stop #for s in doc.Objects: ##for f in PCBs: # if 'Part' in s.TypeId: # #if cut_base.Name in s.Name: # #Part.show(s) # wires,_faces = Draft.downgrade(s,delete=False) # #wires,_faces = Draft.downgrade(s,delete=True) # #Draft.downgrade(FreeCADGui.Selection.getSelection(),delete=True) # #sketch = Draft.makeSketch(wires[0:1]) # #sketch.Label = "Sketch_pcb" # #for f in _faces: # # print f.Edges # for f in s.Faces: # print f.Edges # # for wire in wires: # #sayw(wire.Label) # Draft.makeSketch([wire], autoconstraints = True, addTo=PCB_Sketch) # #Draft.makeSketch([wire], autoconstraints = False, addTo=PCB_Sketch) # PCB_Sketch.touch() # stop # for wire in wires: # FreeCAD.ActiveDocument.removeObject(wire.Name) # FreeCAD.ActiveDocument.recompute() #Part.show(cut_base) #stop #maui ## to check to load models inside loop modules #if m.layer == 'F.Cu': # top # side = "Top" #else: # side = "Bottom" # m_angle *= -1 ##bottom 3d model rotation # say(m_angle) say("start cutting") get_time() t1=(running_time) if not use_AppPart: #old method slower for FC016,015 if holes_solid: #HoleList = getPads(board_elab,pcbThickness) say('generating solid holes') else: say('generating flat holes') ##HoleList = getPads_flat(board_elab) #say('alive-getting holes') #stop if len(HoleList)>0: #cut_base = FreeCAD.ActiveDocument.getObject(cut_base_name).Shape #cut_base_name=FreeCAD.ActiveDocument.ActiveObject #cut_base_name=FreeCAD.ActiveDocument.ActiveObject.Name #say(cut_base) for drill in HoleList: #say("Cutting hole inside outline") #Part.show(drill) #say(drill) if holes_solid: drill = Part.makeSolid(drill) if show_shapes: Part.show(drill) cut_base=cut_base.cut(drill) else: #face = cut_base cut_base = cut_base else: sayw('using hierarchy container') if len(HoleList)>0: if holes_solid: #HoleList = getPads(board_elab,pcbThickness) say('generating solid holes') else: say('generating flat holes') dlo=[] shapes=[];s_names=[] #for drill in HoleList: # if holes_solid: # drill = Part.makeSolid(drill) #Part.show(drill) #dlo.append(drill) #shapes=[];s_names=[] ##for o in FreeCAD.ActiveDocument.Objects: ## if 'Shape' in o.Name: ## dlo.append(FreeCAD.ActiveDocument.getObject(o.Name)) ## shapes.append(o.Shape) ## s_names.append(o.Name) shapes=HoleList #stop ##https://forum.freecadweb.org/viewtopic.php?t=18179&start=30 #multifuse shape_base=shapes[0] shapes=shapes[1:] #fc_016=True if len(HoleList)>1: #more than one drill shape = shape_base.fuse(shapes) #shape = Part.makeCompound(shapes) #this could create issues on cutting else: #one drill ONLY shape = shape_base if extrude_holes: shape.Placement.Base.z+=0.05 # fixing coplanar shapes cut shape=shape.extrude(FreeCAD.Base.Vector(0, 0, -totalHeight-0.1)) # fixing coplanar shapes cut if show_shapes: Part.show(shape) Part.show(cut_base) #test_face #stop test_face_gen = False if test_face_gen: Part.show(cut_base) #test_face Part.show(shape) #test_face stop try: cut_base = cut_base.cut(shape) except: say("*** omitting PCBs because there was a not closed loop in your edge lines ***") say('pcb edge not closed') QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, 'Error in creating Board Edge ."+"\r\n"', """pcb edge not closed
review your Board Edges in Kicad!
or try to fix it with Constrainator""") diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #maui #Part.show(cut_base) #stop for s in s_names: #Part.show(s) FreeCAD.ActiveDocument.removeObject(s) #test_face FreeCAD.ActiveDocument.recompute() #stop #say_time() else: #face = cut_base sayw('pcb without holes') # Part.show(cut_base) #test_face # f0 = cut_base.Faces[0] # Part.show(f0) #cut_base = cut_base #else: # #face = cut_base # try: # cut_base = cut_base # except: # cut_base = FreeCAD.ActiveDocument.getObject('PCB_Sketch_draft') #s_PCB_Sketch_draft #if lyr != 'Edge.Cuts': # cut_base.Label=lyr+'-Sketch' #FreeCADGui.SendMsgToActiveView("ViewFit") # ##if len(HoleList)>0: # ## #face = cut_base.cut(Part.makeCompound(HoleList)) # ## cut_base = cut_base.cut(Part.makeCompound(HoleList)) ###VERY fast but failing when overlapping of pads get_time() say('cutting time ' +str(round(running_time-t1,3))) if lyr == 'Edge.Cuts': pcb_name=u'Pcb'+fname_sfx #doc_outline=doc.addObject("Part::Feature","Pcb") doc_outline=doc.addObject("Part::Feature",pcb_name) pcb_name=FreeCAD.ActiveDocument.ActiveObject.Name pcb_board=FreeCAD.ActiveDocument.ActiveObject try: #doc_outline.Shape=cut_base.extrude(Base.Vector(0,0,-totalHeight)) f0 = cut_base.Faces[0] s0 = f0.extrude(Base.Vector(0,0,-totalHeight)) s = s0 for f in cut_base.Faces[1:]: #f0 = f0.union(f) s1 = f.extrude(Base.Vector(0,0,-totalHeight)) s = s.fuse(s1) doc_outline.Shape=s #doc_outline.Shape=f0.extrude(Base.Vector(0,0,-totalHeight)) #doc_outline.Shape=cut_base.Faces[0].extrude(Base.Vector(0,0,-totalHeight)) except: #doc.removeObject("Pcb") doc.removeObject(pcb_name) say("*** omitting PCBs because there was a not closed loop in your edge lines ***") say('pcb edge not closed') QtGui.QApplication.restoreOverrideCursor() diag = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, 'Error in creating Board Edge ."+"\r\n"', """pcb edge not closed
review your Board Edges in Kicad!
or try to fix it with Constrainator""") diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") stop #maui #stop #tobechecked #try: # FreeCAD.activeDocument().removeObject('Shape') #removing base shape #except: # sayw('Shape already removed') #cut_base=cut_base.extrude(Base.Vector(0,0,-pcbThickness)) #Part.show(cut_base) if simplifyComSolid: faces=[] for f in pcb_board.Shape.Faces: faces.append(f) try: _ = Part.Shell(faces) _=Part.Solid(_) FreeCAD.ActiveDocument.removeObject(pcb_name) #doc.addObject('Part::Feature','Pcb').Shape=_ doc.addObject('Part::Feature',pcb_name).Shape=_ pcb_name=FreeCAD.ActiveDocument.ActiveObject.Name pcb_board=FreeCAD.ActiveDocument.ActiveObject except: sayerr('error in simplifying compsolid') # simple_pcb=doc.addObject("Part::Feature","simple_Pcb") # simple_pcb.Shape=pcb_board.Shape # spcb=pcb_board.Shape # Part.show(spcb) #FreeCAD.ActiveDocument.ActiveObject.Label ="Pcb" FreeCADGui.ActiveDocument.ActiveObject.ShapeColor = (colr,colg,colb) #FreeCADGui.ActiveDocument.ActiveObject.Transparency = 20 #if remove_pcbPad==True: # FreeCAD.activeDocument().removeObject(cut_base_name) #FreeCAD.activeDocument().removeObject(Holes_name) boardG_name='Board_Geoms'+fname_sfx board_name='Board'+fname_sfx if use_AppPart and not force_oldGroups and not use_LinkGroups: ## to evaluate to add App::Part hierarchy #sayw("creating hierarchy") doc.Tip = doc.addObject('App::Part',boardG_name) boardG= doc.ActiveObject boardG.Label = boardG_name try: boardG.License = '' boardG.LicenseURL = '' except: pass grp=boardG if rmv_container is None or rmv_container is False: doc.Tip = doc.addObject('App::Part',board_name) board= doc.ActiveObject board.Label = board_name #FreeCAD.ActiveDocument.getObject("Step_Virtual_Models").addObject(impPart) # doc.getObject(board_name).addObject(doc.getObject(boardG_name)) try: #doc.getObject(boardG_name).addObject(LCS) doc.getObject(board_name).addObject(LCS) LCS.MapMode = 'ObjectXY' LCS.MapReversed = False LCS.Support = [(doc.getObject(board_name).Origin.OriginFeatures[0],'')] except: pass doc.getObject(board_name).addObject(doc.getObject(boardG_name)) else: try: #doc.getObject(boardG_name).addObject(LCS) doc.getObject(board_name).addObject(LCS) LCS.MapMode = 'ObjectXY' LCS.MapReversed = False LCS.Support = [(doc.getObject(board_name).Origin.OriginFeatures[0],'')] except: pass doc.getObject(boardG_name).addObject(doc.getObject(pcb_name)) #FreeCADGui.activeView().setActiveObject('Board_Geoms', doc.Board_Geoms) ## end hierarchy elif use_LinkGroups: doc.Tip = doc.addObject('App::LinkGroup',boardG_name) boardG= doc.ActiveObject boardG.Label = boardG_name grp=boardG_name if rmv_container is None or rmv_container is False: doc.Tip = doc.addObject('App::LinkGroup',board_name) board= doc.ActiveObject board.Label = board_name #FreeCAD.ActiveDocument.getObject("Step_Virtual_Models").addObject(impPart) # doc.getObject("Board").addObject(doc.Board_Geoms) #doc.getObject('Board_Geoms').adjustRelativeLinks(doc.getObject('Board')) # doc.getObject(board_name).ViewObject.dropObject(doc.getObject(boardG_name),doc.getObject(boardG_name),'',[]) try: #LCS.adjustRelativeLinks(doc.getObject('Board_Geoms')) #doc.getObject(boardG_name).ViewObject.dropObject(LCS,LCS,'',[]) doc.getObject(board_name).ViewObject.dropObject(LCS,LCS,'',[]) # LinkGroups don't have 'Origin' Feature # LCS.MapMode = 'ObjectXY' # LCS.MapReversed = False # LCS.Support = [(doc.getObject(board_name).Origin.OriginFeatures[0],'')] FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(LCS) FreeCADGui.runCommand('Std_ToggleVisibility',0) #stop except: pass doc.getObject(board_name).ViewObject.dropObject(doc.getObject(boardG_name),doc.getObject(boardG_name),'',[]) else: try: #LCS.adjustRelativeLinks(doc.getObject('Board_Geoms')) #doc.getObject(boardG_name).ViewObject.dropObject(LCS,LCS,'',[]) doc.getObject(board_name).ViewObject.dropObject(LCS,LCS,'',[]) # LinkGroups don't have 'Origin' Feature # LCS.MapMode = 'ObjectXY' # LCS.MapReversed = False # LCS.Support = [(doc.getObject(board_name).Origin.OriginFeatures[0],'')] FreeCADGui.Selection.clearSelection() FreeCADGui.Selection.addSelection(LCS) FreeCADGui.runCommand('Std_ToggleVisibility',0) #stop except: pass FreeCADGui.Selection.clearSelection() #grp.addObject(pcb_board) #doc.getObject('Pcb').adjustRelativeLinks(doc.getObject('Board_Geoms')) #doc.getObject('Board_Geoms').ViewObject.dropObject(doc.getObject('Pcb'),None,'',[]) doc.getObject(boardG_name).ViewObject.dropObject(doc.getObject(pcb_name),doc.getObject(pcb_name),'',[]) FreeCADGui.Selection.clearSelection() #FreeCADGui.activeView().setActiveObject('Board_Geoms', doc.Board_Geoms) ## end hierarchy else: #sayw("creating flat groups") grp=doc.addObject("App::DocumentObjectGroup", boardG_name) grp.addObject(pcb_board) #pcb_sk=FreeCAD.ActiveDocument.PCB_Sketch #grp.addObject(pcb_sk) #grp.addObject(doc_outline) pcb_bbx = doc.getObject(pcb_name).Shape.BoundBox say("pcb dimensions: ("+"{0:.2f}".format(pcb_bbx.XLength)+";"+"{0:.2f}".format(pcb_bbx.YLength)+";"+"{0:.2f}".format(pcb_bbx.ZLength)+")") say_time() if k_index == 1: FreeCAD.ActiveDocument.removeObject(ndsk.Name) #FreeCADGui.activeDocument().activeView().viewAxometric() FreeCADGui.activeDocument().activeView().viewTop() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") #FreeCADGui.SendMsgToActiveView("ViewFit") #pads_found=getPadsList(content) return PCB_Models, k_index ### ### #cmd open option args=sys.argv #say(args) if len(args) >= 3: # #filename="./psu-fc-1.wrl" #path, fname = os.path.split(args[2]) #export_board_2step=True ##sys.argv="" ext = os.path.splitext(os.path.basename(args[2]))[1] fullfname=args[2] fname=os.path.splitext(os.path.basename(args[2]))[0] #say(filePath+' ');say(fname+' ');say(ext); fullFileName=fullfname+".kicad_pcb" fullFileNamefp=fullfname+".kicad_mod" fileName=fname+".kicad_pcb" fileNamefp=fname+".kicad_mod" #filePath = os.path.dirname(os.path.abspath(__file__)) filePath = os.path.dirname(os.path.abspath(fullFileName)) filePathfp = os.path.dirname(os.path.abspath(fullFileName)) #filePath = os.path.split(os.path.realpath(__file__))[0] say ('arg file path '+filePath) filefound=True if filePath == "": filePath = u"." last_pcb_path = filePath print (last_pcb_path) #say(fullFileName) if os.path.exists(fullFileName): #say("opening "+ fullFileName) #cfgParsWrite(configFilePath) #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) original_filename=fullFileName onLoadBoard(fullFileName) else: fullfilePath=filePath+os.sep+fname+".kicad_pcb" #say(fullfilePath) if os.path.exists(fullfilePath): #say("opening "+ fullfilePath) #cfgParsWrite(configFilePath) #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) original_filename=fullfilePath onLoadBoard(fullfilePath) else: filefound=False # sayw("missing "+ fullfilePath) # sayw("missing "+ fullFileName) # #say("error missing "+ fullfilePath) # QtGui.QApplication.restoreOverrideCursor() # reply = QtGui.QMessageBox.information(None,"Error ...","... missing \r\n"+ fullfilePath+"\r\n... missing \r\n"+ fullFileName) if filefound ==False: if os.path.exists(fullFileNamefp): #say("opening "+ fullFileName) #cfgParsWrite(configFilePath) #cfg_update_all() import fps fps.addfootprint(fullFileNamefp) else: fullfilePath=filePath+os.sep+fname+".kicad_mod" #say(fullfilePath) if os.path.exists(fullfilePath): fp_loaded= False doc = FreeCAD.ActiveDocument if doc is not None: for o in doc.Objects: if hasattr(o, 'Label'): if o.Label.endswith('_fp'): fp_loaded= True #say("opening "+ fullfilePath) #cfgParsWrite(configFilePath) #cfg_update_all() #print(fp_loaded) if not (fp_loaded): import fps fps.addfootprint(fullfilePath) # onLoadFootprint(fullfilePath) else: sayw("missing "+ fullfilePath) sayw("missing "+ fullFileName) #say("error missing "+ fullfilePath) if 0: QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Error ...","... missing \r\n"+ fullfilePath+"\r\n... missing \r\n"+ fullFileName) # ### #QtGui.QDesktopServices.openUrl(QtCore.QUrl("t")) # code *********************************************************************************** #form = RotateXYZGuiClass() #def singleInstance(): # app = QtGui.qApp # # for i in app.topLevelWidgets(): # if i.objectName() == "kicadStepUp": # i.deleteLater() # else: # pass # #singleInstance() ############################################################################################################### ### new dock widget ### add def link inside class # def link(self, linkStr): # QtGui.QDesktopServices.openUrl(QtCore.QUrl(linkStr)) class Ui_DockWidget(object): def link(self, linkStr): #QtGui.QDesktopServices.openUrl(QtCore.QUrl(linkStr)) try: QtGui.QDesktopServices.openUrl(QtCore.QUrl(linkStr)) #workaround Qt5 waiting for PySide2 except: #QtGui.QDesktopServices.openUrl(QtCore.QUrl(linkStr.fromLocalFile())) pass #class Ui_DockWidget(object): def setupUi(self, DockWidget): DockWidget.setObjectName("DockWidget") DockWidget.resize(169, 480) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("kicad-StepUp-icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) DockWidget.setWindowIcon(icon) DockWidget.setLayoutDirection(QtCore.Qt.LeftToRight) DockWidget.setFeatures(QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetMovable) DockWidget.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea|QtCore.Qt.RightDockWidgetArea) self.dockWidgetContents = QtGui.QWidget() self.dockWidgetContents.setObjectName("dockWidgetContents") self.horizontalLayoutWidget = QtGui.QWidget(self.dockWidgetContents) self.horizontalLayoutWidget.setGeometry(QtCore.QRect(5, 0, 160, 36)) self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") self.horizontalLayout = QtGui.QHBoxLayout(self.horizontalLayoutWidget) self.horizontalLayout.setSpacing(2) self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setObjectName("horizontalLayout") self.dock_left = QtGui.QPushButton(self.horizontalLayoutWidget) self.dock_left.setMaximumSize(QtCore.QSize(28, 28)) self.dock_left.setText("") icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap("icons-new/dock_left.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.dock_left.setIcon(icon1) self.dock_left.setIconSize(QtCore.QSize(24, 24)) self.dock_left.setObjectName("dock_left") self.horizontalLayout.addWidget(self.dock_left) self.dock_float = QtGui.QPushButton(self.horizontalLayoutWidget) self.dock_float.setMaximumSize(QtCore.QSize(28, 28)) self.dock_float.setText("") icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap("icons-new/un_dock.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.dock_float.setIcon(icon2) self.dock_float.setIconSize(QtCore.QSize(16, 16)) self.dock_float.setObjectName("dock_float") self.horizontalLayout.addWidget(self.dock_float) self.dock_minimize = QtGui.QPushButton(self.horizontalLayoutWidget) self.dock_minimize.setMaximumSize(QtCore.QSize(28, 28)) self.dock_minimize.setText("") icon3 = QtGui.QIcon() icon3.addPixmap(QtGui.QPixmap("icons-new/minimize.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.dock_minimize.setIcon(icon3) self.dock_minimize.setIconSize(QtCore.QSize(24, 24)) self.dock_minimize.setObjectName("dock_minimize") self.horizontalLayout.addWidget(self.dock_minimize) self.dock_right = QtGui.QPushButton(self.horizontalLayoutWidget) self.dock_right.setMaximumSize(QtCore.QSize(28, 28)) self.dock_right.setText("") icon4 = QtGui.QIcon() icon4.addPixmap(QtGui.QPixmap("icons-new/dock_right.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.dock_right.setIcon(icon4) self.dock_right.setIconSize(QtCore.QSize(24, 24)) self.dock_right.setObjectName("dock_right") self.horizontalLayout.addWidget(self.dock_right) self.close = QtGui.QPushButton(self.horizontalLayoutWidget) self.close.setMaximumSize(QtCore.QSize(28, 28)) self.close.setText("") icon5 = QtGui.QIcon() icon5.addPixmap(QtGui.QPixmap("icons-new/closeW.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.close.setIcon(icon5) self.close.setIconSize(QtCore.QSize(24, 24)) self.close.setObjectName("close") self.horizontalLayout.addWidget(self.close) self.gridLayoutWidget_4 = QtGui.QWidget(self.dockWidgetContents) self.gridLayoutWidget_4.setGeometry(QtCore.QRect(4, 155, 161, 121)) self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4") self.gridLayout_6 = QtGui.QGridLayout(self.gridLayoutWidget_4) self.gridLayout_6.setSpacing(2) self.gridLayout_6.setContentsMargins(0, 0, 0, 0) self.gridLayout_6.setObjectName("gridLayout_6") self.textInputX = QtGui.QLineEdit(self.gridLayoutWidget_4) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textInputX.sizePolicy().hasHeightForWidth()) self.textInputX.setSizePolicy(sizePolicy) self.textInputX.setMaximumSize(QtCore.QSize(60, 16777215)) self.textInputX.setObjectName("textInputX") self.gridLayout_6.addWidget(self.textInputX, 0, 0, 1, 1) self.textInputZ = QtGui.QLineEdit(self.gridLayoutWidget_4) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textInputZ.sizePolicy().hasHeightForWidth()) self.textInputZ.setSizePolicy(sizePolicy) self.textInputZ.setMaximumSize(QtCore.QSize(60, 16777215)) self.textInputZ.setObjectName("textInputZ") self.gridLayout_6.addWidget(self.textInputZ, 2, 0, 1, 1) self.textInputY = QtGui.QLineEdit(self.gridLayoutWidget_4) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textInputY.sizePolicy().hasHeightForWidth()) self.textInputY.setSizePolicy(sizePolicy) self.textInputY.setMaximumSize(QtCore.QSize(60, 16777215)) self.textInputY.setObjectName("textInputY") self.gridLayout_6.addWidget(self.textInputY, 1, 0, 1, 1) self.TranslateX = QtGui.QPushButton(self.gridLayoutWidget_4) self.TranslateX.setMinimumSize(QtCore.QSize(32, 32)) self.TranslateX.setMaximumSize(QtCore.QSize(64, 64)) self.TranslateX.setText("") icon6 = QtGui.QIcon() icon6.addPixmap(QtGui.QPixmap("icons-new/shiftX.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.TranslateX.setIcon(icon6) self.TranslateX.setObjectName("TranslateX") self.gridLayout_6.addWidget(self.TranslateX, 0, 1, 1, 1) self.TranslateY = QtGui.QPushButton(self.gridLayoutWidget_4) self.TranslateY.setMinimumSize(QtCore.QSize(32, 32)) self.TranslateY.setMaximumSize(QtCore.QSize(64, 64)) self.TranslateY.setText("") icon7 = QtGui.QIcon() icon7.addPixmap(QtGui.QPixmap("icons-new/shiftY.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.TranslateY.setIcon(icon7) self.TranslateY.setObjectName("TranslateY") self.gridLayout_6.addWidget(self.TranslateY, 1, 1, 1, 1) self.TranslateZ = QtGui.QPushButton(self.gridLayoutWidget_4) self.TranslateZ.setMinimumSize(QtCore.QSize(32, 32)) self.TranslateZ.setMaximumSize(QtCore.QSize(64, 64)) self.TranslateZ.setText("") icon8 = QtGui.QIcon() icon8.addPixmap(QtGui.QPixmap("icons-new/shiftZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.TranslateZ.setIcon(icon8) self.TranslateZ.setObjectName("TranslateZ") self.gridLayout_6.addWidget(self.TranslateZ, 2, 1, 1, 1) self.PutOnX = QtGui.QPushButton(self.gridLayoutWidget_4) self.PutOnX.setMinimumSize(QtCore.QSize(32, 32)) self.PutOnX.setMaximumSize(QtCore.QSize(64, 64)) self.PutOnX.setText("") icon9 = QtGui.QIcon() icon9.addPixmap(QtGui.QPixmap("icons-new/putX.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.PutOnX.setIcon(icon9) self.PutOnX.setObjectName("PutOnX") self.gridLayout_6.addWidget(self.PutOnX, 0, 3, 1, 1) self.PutOnY = QtGui.QPushButton(self.gridLayoutWidget_4) self.PutOnY.setMinimumSize(QtCore.QSize(32, 32)) self.PutOnY.setMaximumSize(QtCore.QSize(64, 64)) self.PutOnY.setText("") icon10 = QtGui.QIcon() icon10.addPixmap(QtGui.QPixmap("icons-new/putY.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.PutOnY.setIcon(icon10) self.PutOnY.setObjectName("PutOnY") self.gridLayout_6.addWidget(self.PutOnY, 1, 3, 1, 1) self.PutOnZ = QtGui.QPushButton(self.gridLayoutWidget_4) self.PutOnZ.setMinimumSize(QtCore.QSize(32, 32)) self.PutOnZ.setMaximumSize(QtCore.QSize(64, 64)) self.PutOnZ.setText("") icon11 = QtGui.QIcon() icon11.addPixmap(QtGui.QPixmap("icons-new/putZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.PutOnZ.setIcon(icon11) self.PutOnZ.setObjectName("PutOnZ") self.gridLayout_6.addWidget(self.PutOnZ, 2, 3, 1, 1) self.gridLayoutWidget_5 = QtGui.QWidget(self.dockWidgetContents) self.gridLayoutWidget_5.setGeometry(QtCore.QRect(5, 35, 160, 121)) self.gridLayoutWidget_5.setObjectName("gridLayoutWidget_5") self.gridLayout_7 = QtGui.QGridLayout(self.gridLayoutWidget_5) self.gridLayout_7.setSpacing(2) self.gridLayout_7.setContentsMargins(0, 0, 0, 0) self.gridLayout_7.setObjectName("gridLayout_7") self.textInputRX = QtGui.QLineEdit(self.gridLayoutWidget_5) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textInputRX.sizePolicy().hasHeightForWidth()) self.textInputRX.setSizePolicy(sizePolicy) self.textInputRX.setMaximumSize(QtCore.QSize(60, 16777215)) self.textInputRX.setObjectName("textInputRX") self.gridLayout_7.addWidget(self.textInputRX, 0, 0, 1, 1) self.textInputRY = QtGui.QLineEdit(self.gridLayoutWidget_5) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textInputRY.sizePolicy().hasHeightForWidth()) self.textInputRY.setSizePolicy(sizePolicy) self.textInputRY.setMaximumSize(QtCore.QSize(60, 16777215)) self.textInputRY.setObjectName("textInputRY") self.gridLayout_7.addWidget(self.textInputRY, 1, 0, 1, 1) self.textInputRZ = QtGui.QLineEdit(self.gridLayoutWidget_5) self.textInputRZ.setMaximumSize(QtCore.QSize(60, 16777215)) self.textInputRZ.setObjectName("textInputRZ") self.gridLayout_7.addWidget(self.textInputRZ, 2, 0, 1, 1) self.RotateX = QtGui.QPushButton(self.gridLayoutWidget_5) self.RotateX.setMinimumSize(QtCore.QSize(32, 32)) self.RotateX.setMaximumSize(QtCore.QSize(64, 64)) self.RotateX.setText("") icon12 = QtGui.QIcon() icon12.addPixmap(QtGui.QPixmap("icons-new/rotateX.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.RotateX.setIcon(icon12) self.RotateX.setObjectName("RotateX") self.gridLayout_7.addWidget(self.RotateX, 0, 1, 1, 1) self.RotateY = QtGui.QPushButton(self.gridLayoutWidget_5) self.RotateY.setMinimumSize(QtCore.QSize(32, 32)) self.RotateY.setMaximumSize(QtCore.QSize(64, 64)) self.RotateY.setText("") icon13 = QtGui.QIcon() icon13.addPixmap(QtGui.QPixmap("icons-new/rotateY.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.RotateY.setIcon(icon13) self.RotateY.setObjectName("RotateY") self.gridLayout_7.addWidget(self.RotateY, 1, 1, 1, 1) self.RotateZ = QtGui.QPushButton(self.gridLayoutWidget_5) self.RotateZ.setMinimumSize(QtCore.QSize(32, 32)) self.RotateZ.setMaximumSize(QtCore.QSize(64, 64)) self.RotateZ.setText("") icon14 = QtGui.QIcon() icon14.addPixmap(QtGui.QPixmap("icons-new/rotateZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.RotateZ.setIcon(icon14) self.RotateZ.setObjectName("RotateZ") self.gridLayout_7.addWidget(self.RotateZ, 2, 1, 1, 1) self.CenterX = QtGui.QPushButton(self.gridLayoutWidget_5) self.CenterX.setMinimumSize(QtCore.QSize(32, 32)) self.CenterX.setMaximumSize(QtCore.QSize(64, 64)) self.CenterX.setText("") icon15 = QtGui.QIcon() icon15.addPixmap(QtGui.QPixmap("icons-new/centerX.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.CenterX.setIcon(icon15) self.CenterX.setObjectName("CenterX") self.gridLayout_7.addWidget(self.CenterX, 0, 2, 1, 1) self.CenterY = QtGui.QPushButton(self.gridLayoutWidget_5) self.CenterY.setMinimumSize(QtCore.QSize(32, 32)) self.CenterY.setMaximumSize(QtCore.QSize(64, 64)) self.CenterY.setText("") icon16 = QtGui.QIcon() icon16.addPixmap(QtGui.QPixmap("icons-new/centerY.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.CenterY.setIcon(icon16) self.CenterY.setObjectName("CenterY") self.gridLayout_7.addWidget(self.CenterY, 1, 2, 1, 1) self.CenterZ = QtGui.QPushButton(self.gridLayoutWidget_5) self.CenterZ.setMinimumSize(QtCore.QSize(32, 32)) self.CenterZ.setMaximumSize(QtCore.QSize(64, 64)) self.CenterZ.setText("") icon17 = QtGui.QIcon() icon17.addPixmap(QtGui.QPixmap("icons-new/centerZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.CenterZ.setIcon(icon17) self.CenterZ.setObjectName("CenterZ") self.gridLayout_7.addWidget(self.CenterZ, 2, 2, 1, 1) self.gridLayoutWidget_6 = QtGui.QWidget(self.dockWidgetContents) self.gridLayoutWidget_6.setGeometry(QtCore.QRect(4, 280, 161, 190)) self.gridLayoutWidget_6.setObjectName("gridLayoutWidget_6") self.gridLayout_8 = QtGui.QGridLayout(self.gridLayoutWidget_6) self.gridLayout_8.setSpacing(2) self.gridLayout_8.setContentsMargins(0, 0, 0, 0) self.gridLayout_8.setObjectName("gridLayout_8") self.HelpPB = QtGui.QPushButton(self.gridLayoutWidget_6) self.HelpPB.setMinimumSize(QtCore.QSize(27, 36)) self.HelpPB.setMaximumSize(QtCore.QSize(64, 64)) self.HelpPB.setText("") icon18 = QtGui.QIcon() icon18.addPixmap(QtGui.QPixmap("icons-new/help.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.HelpPB.setIcon(icon18) self.HelpPB.setObjectName("HelpPB") self.gridLayout_8.addWidget(self.HelpPB, 4, 1, 1, 1) self.ConfigPB = QtGui.QPushButton(self.gridLayoutWidget_6) self.ConfigPB.setMinimumSize(QtCore.QSize(60, 36)) self.ConfigPB.setMaximumSize(QtCore.QSize(64, 64)) self.ConfigPB.setText("") icon19 = QtGui.QIcon() icon19.addPixmap(QtGui.QPixmap("icons-new/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ConfigPB.setIcon(icon19) self.ConfigPB.setObjectName("ConfigPB") self.gridLayout_8.addWidget(self.ConfigPB, 4, 0, 1, 1) self.pushPCB = QtGui.QPushButton(self.gridLayoutWidget_6) self.pushPCB.setMinimumSize(QtCore.QSize(47, 36)) self.pushPCB.setMaximumSize(QtCore.QSize(64, 64)) self.pushPCB.setText("") icon20 = QtGui.QIcon() icon20.addPixmap(QtGui.QPixmap("icons-new/Sketcher_Rectangle.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.pushPCB.setIcon(icon20) self.pushPCB.setObjectName("pushPCB") self.gridLayout_8.addWidget(self.pushPCB, 3, 3, 1, 1) self.makeCompound = QtGui.QPushButton(self.gridLayoutWidget_6) self.makeCompound.setMinimumSize(QtCore.QSize(47, 36)) self.makeCompound.setMaximumSize(QtCore.QSize(64, 64)) self.makeCompound.setText("") icon21 = QtGui.QIcon() icon21.addPixmap(QtGui.QPixmap("icons-new/compound.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.makeCompound.setIcon(icon21) self.makeCompound.setObjectName("makeCompound") self.gridLayout_8.addWidget(self.makeCompound, 1, 3, 1, 1) self.LoadBoard = QtGui.QPushButton(self.gridLayoutWidget_6) self.LoadBoard.setMinimumSize(QtCore.QSize(60, 36)) self.LoadBoard.setMaximumSize(QtCore.QSize(64, 64)) self.LoadBoard.setText("") icon22 = QtGui.QIcon() icon22.addPixmap(QtGui.QPixmap("icons-new/importBoard.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.LoadBoard.setIcon(icon22) self.LoadBoard.setObjectName("LoadBoard") self.gridLayout_8.addWidget(self.LoadBoard, 2, 0, 1, 1) self.ScaleVRML = QtGui.QPushButton(self.gridLayoutWidget_6) self.ScaleVRML.setMinimumSize(QtCore.QSize(60, 36)) self.ScaleVRML.setMaximumSize(QtCore.QSize(64, 64)) self.ScaleVRML.setText("") icon23 = QtGui.QIcon() icon23.addPixmap(QtGui.QPixmap("icons-new/export3DModel.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.ScaleVRML.setIcon(icon23) self.ScaleVRML.setObjectName("ScaleVRML") self.gridLayout_8.addWidget(self.ScaleVRML, 1, 0, 1, 1) self.cb_virtual = QtGui.QCheckBox(self.gridLayoutWidget_6) self.cb_virtual.setMinimumSize(QtCore.QSize(47, 32)) self.cb_virtual.setMaximumSize(QtCore.QSize(64, 128)) self.cb_virtual.setText("") icon24 = QtGui.QIcon() icon24.addPixmap(QtGui.QPixmap("icons-new/virtual.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cb_virtual.setIcon(icon24) self.cb_virtual.setObjectName("cb_virtual") self.gridLayout_8.addWidget(self.cb_virtual, 2, 1, 1, 1) self.cb_materials = QtGui.QCheckBox(self.gridLayoutWidget_6) self.cb_materials.setMinimumSize(QtCore.QSize(47, 32)) self.cb_materials.setMaximumSize(QtCore.QSize(64, 128)) self.cb_materials.setText("") icon25 = QtGui.QIcon() icon25.addPixmap(QtGui.QPixmap("icons-new/materials.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cb_materials.setIcon(icon25) self.cb_materials.setObjectName("cb_materials") self.gridLayout_8.addWidget(self.cb_materials, 1, 1, 1, 1) self.LoadFootprint = QtGui.QPushButton(self.gridLayoutWidget_6) self.LoadFootprint.setMinimumSize(QtCore.QSize(60, 36)) self.LoadFootprint.setMaximumSize(QtCore.QSize(64, 64)) self.LoadFootprint.setText("") icon26 = QtGui.QIcon() icon26.addPixmap(QtGui.QPixmap("icons-new/importFP.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.LoadFootprint.setIcon(icon26) self.LoadFootprint.setObjectName("LoadFootprint") self.gridLayout_8.addWidget(self.LoadFootprint, 0, 0, 1, 1) self.cb_expStep = QtGui.QCheckBox(self.gridLayoutWidget_6) self.cb_expStep.setMinimumSize(QtCore.QSize(47, 20)) self.cb_expStep.setMaximumSize(QtCore.QSize(128, 64)) self.cb_expStep.setText("") icon27 = QtGui.QIcon() icon27.addPixmap(QtGui.QPixmap("icons-new/exportPart.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.cb_expStep.setIcon(icon27) self.cb_expStep.setObjectName("cb_expStep") self.gridLayout_8.addWidget(self.cb_expStep, 2, 3, 1, 1) self.makeUnion = QtGui.QPushButton(self.gridLayoutWidget_6) self.makeUnion.setMinimumSize(QtCore.QSize(27, 36)) self.makeUnion.setMaximumSize(QtCore.QSize(64, 64)) self.makeUnion.setText("") icon28 = QtGui.QIcon() icon28.addPixmap(QtGui.QPixmap("icons-new/fusion.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.makeUnion.setIcon(icon28) self.makeUnion.setObjectName("makeUnion") self.gridLayout_8.addWidget(self.makeUnion, 0, 3, 1, 1) self.import3D = QtGui.QPushButton(self.gridLayoutWidget_6) self.import3D.setMinimumSize(QtCore.QSize(27, 36)) self.import3D.setMaximumSize(QtCore.QSize(64, 64)) self.import3D.setText("") icon29 = QtGui.QIcon() icon29.addPixmap(QtGui.QPixmap("icons-new/add_block.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.import3D.setIcon(icon29) self.import3D.setObjectName("import3D") self.gridLayout_8.addWidget(self.import3D, 0, 1, 1, 1) self.checkCollisions = QtGui.QPushButton(self.gridLayoutWidget_6) self.checkCollisions.setMinimumSize(QtCore.QSize(27, 36)) self.checkCollisions.setMaximumSize(QtCore.QSize(64, 64)) self.checkCollisions.setText("") icon30 = QtGui.QIcon() icon30.addPixmap(QtGui.QPixmap("icons-new/collisions.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.checkCollisions.setIcon(icon30) self.checkCollisions.setObjectName("checkCollisions") self.gridLayout_8.addWidget(self.checkCollisions, 3, 1, 1, 1) self.export3DStep = QtGui.QPushButton(self.gridLayoutWidget_6) self.export3DStep.setMinimumSize(QtCore.QSize(60, 36)) self.export3DStep.setMaximumSize(QtCore.QSize(64, 64)) self.export3DStep.setText("") icon31 = QtGui.QIcon() icon31.addPixmap(QtGui.QPixmap("icons-new/export3DStep.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.export3DStep.setIcon(icon31) self.export3DStep.setObjectName("export3DStep") self.gridLayout_8.addWidget(self.export3DStep, 3, 0, 1, 1) self.CreateAxis = QtGui.QPushButton(self.gridLayoutWidget_6) self.CreateAxis.setMinimumSize(QtCore.QSize(27, 36)) self.CreateAxis.setMaximumSize(QtCore.QSize(64, 64)) self.CreateAxis.setText("") icon32 = QtGui.QIcon() icon32.addPixmap(QtGui.QPixmap("icons-new/axis.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.CreateAxis.setIcon(icon32) self.CreateAxis.setObjectName("CreateAxis") self.gridLayout_8.addWidget(self.CreateAxis, 4, 3, 1, 1) self.config_ini_Lbl = QtGui.QLabel(self.dockWidgetContents) self.config_ini_Lbl.setGeometry(QtCore.QRect(176, 12, 305, 16)) self.config_ini_Lbl.setObjectName("config_ini_Lbl") self.textEdit = QtGui.QTextBrowser(self.dockWidgetContents) self.textEdit.setGeometry(QtCore.QRect(176, 36, 305, 453)) self.textEdit.setOpenExternalLinks(True) self.textEdit.setObjectName("textEdit") DockWidget.setWidget(self.dockWidgetContents) ############################################################################################################### pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(axis_b64)) self.CreateAxis.setIcon(QtGui.QIcon(pm)) self.CreateAxis.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(exportPart_b64)) self.cb_expStep.setIcon(QtGui.QIcon(pm)) self.cb_expStep.setIconSize(QtCore.QSize(chkb_sizeX,chkb_sizeY)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(virtual_b64)) self.cb_virtual.setIcon(QtGui.QIcon(pm)) self.cb_virtual.setIconSize(QtCore.QSize(chkb_sizeX,chkb_sizeY)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(materials_b64)) self.cb_materials.setIcon(QtGui.QIcon(pm)) self.cb_materials.setIconSize(QtCore.QSize(chkb_sizeX,chkb_sizeY)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(collisions_b64)) self.checkCollisions.setIcon(QtGui.QIcon(pm)) self.checkCollisions.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(importBoard_b64)) self.LoadBoard.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.LoadBoard.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(export3DModel_b64)) self.ScaleVRML.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.ScaleVRML.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(importFP_b64)) self.LoadFootprint.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.LoadFootprint.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(compound_b64)) self.makeCompound.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.makeCompound.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(fusion_b64)) self.makeUnion.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.makeUnion.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(rotateX_b64)) self.RotateX.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.RotateX.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(rotateY_b64)) self.RotateY.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.RotateY.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(rotateZ_b64)) self.RotateZ.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.RotateZ.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(shiftX_b64)) self.TranslateX.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.TranslateX.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(shiftY_b64)) self.TranslateY.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.TranslateY.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(shiftZ_b64)) self.TranslateZ.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.TranslateZ.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(centerX_b64)) self.CenterX.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.CenterX.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(centerY_b64)) self.CenterY.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.CenterY.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(centerZ_b64)) self.CenterZ.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.CenterZ.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(putX_b64)) self.PutOnX.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.PutOnX.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(putY_b64)) self.PutOnY.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.PutOnY.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(putZ_b64)) self.PutOnZ.setIconSize(QtCore.QSize(btn_sizeX,btn_sizeY)) self.PutOnZ.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(edit_b64)) self.ConfigPB.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.ConfigPB.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(pcb_edge_b64)) #sketch self.pushPCB.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.pushPCB.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(help_b64)) self.HelpPB.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.HelpPB.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(add_block_b64)) self.import3D.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.import3D.setIcon(QtGui.QIcon(pm)) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(stop_grey_b64)) #self.collisionLbl.setPixmap(pm) ##self.collisionLbl.setPixmapSize(QtCore.QSize(btn_sm_sizeX,btn_sm_sizeY)) ## todo #self.checkCollisions.clicked.connect(changePixmap_stop) #self.checkCollisions.clicked.connect(changePixmap_ok) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(dock_left_b64)) self.dock_left.setIconSize(QtCore.QSize(btn_sm_sizeX,btn_sm_sizeY)) self.dock_left.setIcon(QtGui.QIcon(pm)) self.dock_left.clicked.connect(dock) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(un_dock_b64)) self.dock_float.setIconSize(QtCore.QSize(btn_sm_sizeX,btn_sm_sizeY)) self.dock_float.setIcon(QtGui.QIcon(pm)) self.dock_float.clicked.connect(undock) #self.dock_minimize.clicked.connect(minimz_alt) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(minimize_b64)) self.dock_minimize.setIconSize(QtCore.QSize(btn_sm_sizeX,btn_sm_sizeY)) self.dock_minimize.setIcon(QtGui.QIcon(pm)) self.dock_minimize.clicked.connect(minimz) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(dock_right_b64)) self.dock_right.setIconSize(QtCore.QSize(btn_sm_sizeX,btn_sm_sizeY)) self.dock_right.setIcon(QtGui.QIcon(pm)) self.dock_right.clicked.connect(dock_right) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(closeW_b64)) self.close.setIconSize(QtCore.QSize(btn_sm_sizeX,btn_sm_sizeY)) self.close.setIcon(QtGui.QIcon(pm)) self.close.clicked.connect(close_ksu) pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(export3DStep_b64)) self.export3DStep.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.export3DStep.setIcon(QtGui.QIcon(pm)) self.export3DStep.clicked.connect(self.onExport3DStep) self.RotateX.clicked.connect(self.onRotateX) self.RotateY.clicked.connect(self.onRotateY) self.RotateZ.clicked.connect(self.onRotateZ) self.TranslateX.clicked.connect(self.onTranslateX) self.TranslateY.clicked.connect(self.onTranslateY) self.TranslateZ.clicked.connect(self.onTranslateZ) self.CenterX.clicked.connect(self.onCenterX) self.CenterY.clicked.connect(self.onCenterY) self.CenterZ.clicked.connect(self.onCenterZ) self.PutOnX.clicked.connect(self.onPutOnX) self.PutOnY.clicked.connect(self.onPutOnY) self.PutOnZ.clicked.connect(self.onPutOnZ) self.CreateAxis.clicked.connect(self.onCreateAxis) self.LoadFootprint.clicked.connect(self.onLoadFootprint_click) if enable_materials==0: #export_board_2step=False self.cb_materials.setChecked(False) # Check by default True or False else: self.cb_materials.setChecked(True) # Check by default True or False # if enable_materials: # self.checkBox_4.setChecked(True) # Check by default True or False if export_board_2step==False: #export_board_2step=False self.cb_expStep.setChecked(False) # Check by default True or False else: self.cb_expStep.setChecked(True) # Check by default True or False say("export to STEP "+str(export_board_2step)) if addVirtual==0: #export_board_2step=False self.cb_virtual.setChecked(False) # Check by default True or False else: self.cb_virtual.setChecked(True) # Check by default True or False ##export_board_2step=True self.cb_virtual.clicked.connect(self.on_cb_virtual_clicked) # connect on def "on_checkBox_1_clicked" self.cb_materials.clicked.connect(self.on_cb_materials_clicked) # connect on def "on_cb_materials_clicked" self.ScaleVRML.clicked.connect(self.onScaleVRML) self.checkCollisions.clicked.connect(self.onCollisions) self.import3D.clicked.connect(self.onImport3DModel) self.LoadBoard.clicked.connect(self.onLoadBoard_click) self.cb_expStep.clicked.connect(self.on_cb_expStep_clicked) home = expanduser("~") ini_file_full_path_bold=''+home+os.sep+'ksu-config.ini' ini_file_full_path=home+os.sep+'ksu-config.ini' #self.config_ini_Lbl = QtGui.QLabel(ini_file_full_path, self) #self.config_ini_Lbl.setText(ini_file_full_path_bold) self.ConfigPB.clicked.connect(self.onCfg) self.pushPCB.clicked.connect(self.onPushPCB) self.HelpPB.clicked.connect(self.onHelp) self.makeUnion.clicked.connect(group_part_union) self.makeCompound.clicked.connect(group_part) self.config_ini_Lbl.linkActivated.connect(self.link) local_link=""+ini_file_full_path_bold+"" #self.config_ini_Lbl.setText(local_link) self.config_ini_Lbl.setText('') self.config_ini_Lbl.setToolTip(translate("Ui_DockWidget", "ksu config ini file\nlocation")) self.textInputRX.setAlignment(QtCore.Qt.AlignRight) self.textInputRY.setAlignment(QtCore.Qt.AlignRight) self.textInputRZ.setAlignment(QtCore.Qt.AlignRight) self.textInputX.setAlignment(QtCore.Qt.AlignRight) self.textInputY.setAlignment(QtCore.Qt.AlignRight) self.textInputZ.setAlignment(QtCore.Qt.AlignRight) ############################################################################################################### self.retranslateUi(DockWidget) QtCore.QMetaObject.connectSlotsByName(DockWidget) DockWidget.setTabOrder(self.dock_left, self.dock_float) DockWidget.setTabOrder(self.dock_float, self.dock_minimize) DockWidget.setTabOrder(self.dock_minimize, self.dock_right) DockWidget.setTabOrder(self.dock_right, self.close) DockWidget.setTabOrder(self.close, self.textInputRX) DockWidget.setTabOrder(self.textInputRX, self.RotateX) DockWidget.setTabOrder(self.RotateX, self.CenterX) DockWidget.setTabOrder(self.CenterX, self.textInputRY) DockWidget.setTabOrder(self.textInputRY, self.RotateY) DockWidget.setTabOrder(self.RotateY, self.CenterY) DockWidget.setTabOrder(self.CenterY, self.textInputRZ) DockWidget.setTabOrder(self.textInputRZ, self.RotateZ) DockWidget.setTabOrder(self.RotateZ, self.CenterZ) DockWidget.setTabOrder(self.CenterZ, self.textInputX) DockWidget.setTabOrder(self.textInputX, self.TranslateX) DockWidget.setTabOrder(self.TranslateX, self.PutOnX) DockWidget.setTabOrder(self.PutOnX, self.textInputY) DockWidget.setTabOrder(self.textInputY, self.TranslateY) DockWidget.setTabOrder(self.TranslateY, self.PutOnY) DockWidget.setTabOrder(self.PutOnY, self.textInputZ) DockWidget.setTabOrder(self.textInputZ, self.TranslateZ) DockWidget.setTabOrder(self.TranslateZ, self.PutOnZ) DockWidget.setTabOrder(self.PutOnZ, self.LoadFootprint) DockWidget.setTabOrder(self.LoadFootprint, self.import3D) DockWidget.setTabOrder(self.import3D, self.makeUnion) DockWidget.setTabOrder(self.makeUnion, self.ScaleVRML) DockWidget.setTabOrder(self.ScaleVRML, self.cb_materials) DockWidget.setTabOrder(self.cb_materials, self.makeCompound) DockWidget.setTabOrder(self.makeCompound, self.LoadBoard) DockWidget.setTabOrder(self.LoadBoard, self.cb_virtual) DockWidget.setTabOrder(self.cb_virtual, self.cb_expStep) DockWidget.setTabOrder(self.cb_expStep, self.export3DStep) DockWidget.setTabOrder(self.export3DStep, self.checkCollisions) DockWidget.setTabOrder(self.checkCollisions, self.CreateAxis) DockWidget.setTabOrder(self.CreateAxis, self.ConfigPB) DockWidget.setTabOrder(self.ConfigPB, self.HelpPB) DockWidget.setTabOrder(self.HelpPB, self.textEdit) ## retraslateUi Qt5 compatibility ############################################################################################################# def retranslateUi(self, DockWidget): #DockWidget.setWindowTitle(QtGui.QApplication.translate("DockWidget", "kicad StepUp tools", None, QtGui.QApplication.UnicodeUTF8)) DockWidget.setWindowTitle(translate("Ui_DockWidget", "KiCad StepUp tools")) self.dock_left.setToolTip(translate("Ui_DockWidget", "dock left")) self.dock_float.setToolTip(translate("Ui_DockWidget", "un-dock (floating)")) self.dock_minimize.setToolTip(translate("Ui_DockWidget", "minimize")) self.dock_right.setToolTip(translate("Ui_DockWidget", "dock right")) self.close.setToolTip(translate("Ui_DockWidget", "close")) self.textInputX.setToolTip(translate("Ui_DockWidget", "translate (+/- mm)")) self.textInputX.setText("0.10") self.textInputZ.setToolTip(translate("Ui_DockWidget", "translate (+/- mm)")) self.textInputZ.setText("0.10") self.textInputY.setToolTip(translate("Ui_DockWidget", "translate (+/- mm)")) self.textInputY.setText("0.10") self.TranslateX.setToolTip(translate("Ui_DockWidget", "translate X (+/- mm)")) self.TranslateY.setToolTip(translate("Ui_DockWidget", "translate Y (+/- mm)")) self.TranslateZ.setToolTip(translate("Ui_DockWidget", "translate Z (+/- mm)")) self.PutOnX.setToolTip(translate("Ui_DockWidget", "put on X")) self.PutOnY.setToolTip(translate("Ui_DockWidget", "put on Y")) self.PutOnZ.setToolTip(translate("Ui_DockWidget", "put on Z")) self.textInputRX.setToolTip(translate("Ui_DockWidget", "rotation angle (+/- deg)")) self.textInputRX.setText("90") self.textInputRY.setToolTip(translate("Ui_DockWidget", "rotation angle (+/- deg)")) self.textInputRY.setText("90") self.textInputRZ.setToolTip(translate("Ui_DockWidget", "rotation angle (+/- deg)")) self.textInputRZ.setText("90") self.RotateX.setToolTip(translate("Ui_DockWidget", "rotate X (+/- deg)")) self.RotateY.setToolTip(translate("Ui_DockWidget", "rotate Y (+/- deg)")) self.RotateZ.setToolTip(translate("Ui_DockWidget", "rotate Z (+/- deg)")) self.CenterX.setToolTip(translate("Ui_DockWidget", "center X")) self.CenterY.setToolTip(translate("Ui_DockWidget", "center Y")) self.CenterZ.setToolTip(translate("Ui_DockWidget", "center Z")) self.makeCompound.setToolTip(translate("Ui_DockWidget", "make Compound of Parts")) self.LoadBoard.setToolTip(translate("Ui_DockWidget", "Load KiCad\n" "Board .kicad_pcb")) self.ScaleVRML.setToolTip(translate("Ui_DockWidget", "export to KiCad:\n" "STEP & scaled VRML 1/2.54")) self.cb_virtual.setToolTip(translate("Ui_DockWidget", "enable loading\n" "virtual & mechanical\n" "models")) self.cb_materials.setToolTip(translate("Ui_DockWidget", "use wrl\n" "material")) self.LoadFootprint.setToolTip(translate("Ui_DockWidget", "load KiCad footprint\n" "\'kicad_mod\'")) self.cb_expStep.setToolTip(translate("Ui_DockWidget", "export STEP Board\n" "and Parts after loading")) self.makeUnion.setToolTip(translate("Ui_DockWidget", "make Union of Parts")) self.import3D.setToolTip(translate("Ui_DockWidget", "import STEP\n" "3D model")) self.checkCollisions.setToolTip(translate("Ui_DockWidget", "check Collisions\n" "tolerance 1e-06")) self.export3DStep.setToolTip(translate("Ui_DockWidget", "export selected objects to STEP")) self.CreateAxis.setToolTip(translate("Ui_DockWidget", "create reference Axis")) self.HelpPB.setToolTip(translate("Ui_DockWidget", "Help & starting Guide")) self.ConfigPB.setToolTip(translate("Ui_DockWidget", "view Config File content")) self.pushPCB.setToolTip(translate("Ui_DockWidget", "push PCB Edge to KiCad\n" "from Sketcher to pcbnew")) #self.config_ini_Lbl.setText("TextLabel")) #self.config_ini_Lbl.setText("TextLabel") ## NB!!! comment the line ##self.config_ini_Lbl.setText("TextLabel") ############################################################################################################### def onRotateX(self): FreeCAD.Console.PrintMessage("RotateX!") alpha=self.textInputRX.text() alpha=alpha.replace(',', '.') angle=alpha.split('.') self.textInputRX.setText(angle[0]) routineR_XYZ('x',angle[0]) position=get_position() ## def onRotateY(self): FreeCAD.Console.PrintMessage("RotateY!") alpha=self.textInputRY.text() alpha=alpha.replace(',', '.') angle=alpha.split('.') self.textInputRY.setText(angle[0]) routineR_XYZ('y',angle[0]) position=get_position() ## def onRotateZ(self): FreeCAD.Console.PrintMessage("RotateZ!") alpha=self.textInputRZ.text() alpha=alpha.replace(',', '.') angle=alpha.split('.') self.textInputRZ.setText(angle[0]) routineR_XYZ('z',angle[0]) position=get_position() ## def onTranslateX(self): v=self.textInputX.text() v=v.replace(',', '.') v=v.replace(" ", "") #FreeCAD.Console.PrintMessage(v+"\r\n") routineT_XYZ('x',v) position=get_position() ## def onTranslateY(self): v=self.textInputY.text() v=v.replace(',', '.') v=v.replace(" ", "") #FreeCAD.Console.PrintMessage(v+"\r\n") routineT_XYZ('y',v) position=get_position() ## def onTranslateZ(self): v=self.textInputZ.text() v=v.replace(',', '.') v=v.replace(" ", "") #FreeCAD.Console.PrintMessage(v+"\r\n") routineT_XYZ('z',v) position=get_position() ## def onCenterX(self): FreeCAD.Console.PrintMessage("centering\r\n") routineC_XYZ('x') position=get_position() ## def onCenterY(self): FreeCAD.Console.PrintMessage("centering\r\n") routineC_XYZ('y') position=get_position() ## def onCenterZ(self): FreeCAD.Console.PrintMessage("centering\r\n") routineC_XYZ('z') position=get_position() ## def onPutOnX(self): FreeCAD.Console.PrintMessage("putting on Plane X\r\n") routineP_XYZ('x') position=get_position() ## def onPutOnY(self): FreeCAD.Console.PrintMessage("putting on Plane Y\r\n") routineP_XYZ('y') position=get_position() ## def onPutOnZ(self): FreeCAD.Console.PrintMessage("putting on Plane Z\r\n") routineP_XYZ('z') position=get_position() ## def onCreateAxis(self): FreeCAD.Console.PrintMessage("Create Axis!"+"\r\n") if FreeCAD.ActiveDocument is None: #say("none") FreeCAD.newDocument() if FreeCAD.ActiveDocument.getObject("axis") is None: create_axis() ## def onLoadFootprint_click(self): #self.setGeometry(25, 250, 500, 500) sayw("kicad StepUp version "+str(___ver___)) #say("tolerance on vertex = "+str(edge_tolerance)) say("tolerance on vertex applied") import fps fps.addfootprint() # onLoadFootprint() ## def on_cb_materials_clicked(self): global enable_materials if self.cb_materials.isChecked(): enable_materials=1 ini_vars[13] = u'enablematerials' #cfgParsWrite(configFilePath) #cfg_update_all() prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs.SetBool('vrml_materials',1) else: enable_materials=0 ini_vars[13] = u'nomaterials' #cfgParsWrite(configFilePath) #cfg_update_all() prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs.SetBool('vrml_materials',0) say("materials = "+str(enable_materials)) ## def onScaleVRML(self): global applymaterials, exportS FreeCAD.Console.PrintMessage("ScaleToVRML!"+"\r\n") applymaterials=0 if self.cb_materials.isChecked(): applymaterials=1 #self.setGeometry(25, 250, 500, 500) result=routineScaleVRML() #position=get_position() #try: # dummy=str(position[0]) ##if exportS: ## try: ## say("X:"+str(position[0])) ## say("Y:"+str(position[1])) ## say("Z:"+str(position[2])) #except: # pass if result==-1: msg="************\r\nSelect an object\r\n************" self.labelInfoMsg.setText(msg) ## def changePixmap_stop_disabled(self): #print("Do stuff here") pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(stop_b64)) self.collisionLbl.setPixmap(pm) self.collisionLbl.setEnabled(False) self.collisionLbl.setToolTip(translate("Ui_DockWidget", 'collisions result status')) #self.setPixmap(pm) #self.pixmap = QtGui.QPixmap(pm) #self.repaint() # repaint() will trigger the paintEvent(self, event), this way the new pixmap will be drawn on the label ##self.checkCollisions.clicked.connect(changePixmap_ok) ## def changePixmap_ok(self): #print("Do stuff here") pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(ok_b64)) #self.collisionLbl.setPixmap(pm) #self.collisionLbl.setEnabled(True) self.checkCollisions.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.checkCollisions.setIcon(pm) #self.checkCollisions.setEnabled(True) QtCore.QTimer.singleShot(timer_Collisions, self.changePixmap_button_base) self.checkCollisions.setToolTip(translate("Ui_DockWidget", 'NO collisions found')) #self.setPixmap(pm) ##self.pixmap = QtGui.QPixmap(pm) #self.repaint() # repaint() will trigger the paintEvent(self, event), this way the new pixmap will be drawn on the label ## def changePixmap_button_base(self): #print("Do stuff here") pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(collisions_b64)) #self.collisionLbl.setPixmap(pm) #self.collisionLbl.setEnabled(True) self.checkCollisions.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.checkCollisions.setIcon(pm) #self.checkCollisions.setEnabled(True) #QtCore.QTimer.singleShot(timer_Collisions, self.changePixmap_stop_disabled) self.checkCollisions.setToolTip(translate("Ui_DockWidget", 'check Collisions\ntolerance 1e-06')) #self.setPixmap(pm) ##self.pixmap = QtGui.QPixmap(pm) #self.repaint() # repaint() will trigger the paintEvent(self, event), this way the new pixmap will be drawn on the label ## def changePixmap_collisions(self): #print("Do stuff here") pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(stop_b64)) self.checkCollisions.setIconSize(QtCore.QSize(btn_md_sizeX,btn_md_sizeY)) self.checkCollisions.setIcon(pm) QtCore.QTimer.singleShot(timer_Collisions, self.changePixmap_button_base) self.checkCollisions.setToolTip(translate("Ui_DockWidget", 'collisions FOUND!')) #self.setPixmap(pm) #self.pixmap = QtGui.QPixmap(pm) #self.repaint() # repaint() will trigger the paintEvent(self, event), this way the new pixmap will be drawn on the label ## def changePixmap_stop(self): #print("Do stuff here") pm = QtGui.QPixmap() pm.loadFromData(base64.b64decode(stop_b64)) self.collisionLbl.setPixmap(pm) self.collisionLbl.setEnabled(True) QtCore.QTimer.singleShot(timer_Collisions, self.changePixmap_stop_disabled) self.collisionLbl.setToolTip(translate("Ui_DockWidget", 'collisions FOUND!')) #self.setPixmap(pm) #self.pixmap = QtGui.QPixmap(pm) #self.repaint() # repaint() will trigger the paintEvent(self, event), this way the new pixmap will be drawn on the label ## def onImport3DModel(self): Import3DModelF() ## def onExport3DStep(self): Export3DStepF() ## def onCollisions(self): #self.setGeometry(25, 250, 500, 500) collisions=routineCollisions() if collisions==0: self.changePixmap_ok() elif collisions==1: self.changePixmap_collisions() else: self.changePixmap_button_base() # self.label17.setText("No") # self.label18.setText("collisions") # self.label19.setText("found!") #elif collisions==1: # self.label17.setText("Collisions") # self.label18.setText("detected!") # self.label19.setText("!!!") #else: # self.label17.setText(" ") # self.label18.setText(" ") # self.label19.setText(" ") def onLoadBoard_click(self): #self.setGeometry(25, 250, 500, 500) sayw("kicad StepUp version "+str(___ver___)) #say("tolerance on vertex = "+str(edge_tolerance)) say("tolerance on vertex applied") #ini_content=read_ini_file() ini_content=cfg_read_all() self.textEdit.setText(ini_content) cfg_read_all() #cfgParsRead(configFilePath) onLoadBoard() ## def on_cb_expStep_clicked(self): global export_board_2step if self.cb_expStep.isChecked(): export_board_2step=True ini_vars[12] = u'yes' #cfgParsWrite(configFilePath) #cfg_update_all() prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs.SetBool('exp_step',1) else: export_board_2step=False ini_vars[12] = u'no' #cfgParsWrite(configFilePath) #cfg_update_all() prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs.SetBool('exp_step',0) say("export STEP = "+str(export_board_2step)) ## def on_cb_virtual_clicked(self): global addVirtual if self.cb_virtual.isChecked(): addVirtual=1 ini_vars[7] = u'addVirtual' #cfgParsWrite(configFilePath) #cfg_update_all() prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs.SetBool('mode_virtual',1) else: addVirtual=0 ini_vars[7] = u'noVirtual' #cfgParsWrite(configFilePath) #cfg_update_all() prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") prefs.SetBool('mode_virtual',0) say("virtual = "+str(addVirtual)) ## def onCfg(self): global expanded_view global configFilePath, ini_content #QtGui.QMessageBox.information(None,"info ...","your home path is \r\n"+ home+"\r\n") #sayw(expanded_view) #stop if 1: #if "ksuWB" not in FreeCADGui.activeWorkbench().name(): if 'pref_page' not in globals(): FreeCADGui.activateWorkbench("KiCadStepUpWB") FreeCADGui.runCommand("Std_DlgPreferences") elif expanded_view!=1: temporary_undock() #to do .... #undock() clear_console() ini_content=cfg_read_all() self.textEdit.setText(ini_content) cfg_read_all() #resolution = QtGui.QDesktopWidget().screenGeometry() #xp=(resolution.width() / 2) - sizeXMax/2 # - (KSUWidget.frameSize().width() / 2) #yp=(resolution.height() / 2) - sizeY/2 # - (KSUWidget.frameSize().height() / 2)) #KSUWidget.setGeometry(xp, yp, sizeXMax, sizeY) textEdit_dim = textEdit_dim_base self.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) self.textEdit.setToolTip(translate("Ui_DockWidget", "ksu config ini file\ncontent")) centerOnScreen (KSUWidget) #say("your home path is "+ expanduser("~")) sayw("kicad StepUp version "+str(___ver___)) #ini_content=read_ini_file() expanded_view=1 #QtCore.QTimer.singleShot(10,self.onCfg) #cfgParsRead(configFilePath) else: #sayw(expanded_view) expanded_view=0 clear_console() KSUWidget.setVisibility=False #textEdit_dim = textEdit_dim_hide #self.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) cfg_read_all() if docking_mode == 'float': undock() KSUWidget.setVisibility=True elif docking_mode == 'left': #tabify() dock() KSUWidget.setVisibility=True else: dock_right() KSUWidget.setVisibility=True sayw("kicad StepUp version "+str(___ver___)) # def onHide(self): # global expanded_view # global configFilePath, ini_content # if not expanded_view: # undock() # #resolution = QtGui.QDesktopWidget().screenGeometry() # #xp=(resolution.width() / 2) - sizeXMax/2 # - (KSUWidget.frameSize().width() / 2) # #yp=(resolution.height() / 2) - sizeY/2 # - (KSUWidget.frameSize().height() / 2)) # #KSUWidget.setGeometry(xp, yp, sizeXMax, sizeY) # centerOnScreen (KSUWidget) # #ini_content=read_ini_file() # ini_content=cfg_read_all() # self.textEdit.setText(ini_content) # cfg_read_all() # expanded_view=True # #cfgParsRead(configFilePath) # else: # KSUWidget.setGeometry(250, 250, sizeX, sizeY) # dock_right() # expanded_view=False # #ini_content=read_ini_file() # ini_content=cfg_read_all() # self.textEdit.setText(ini_content) # #configParser.read(configFilePath) # cfg_read_all() # #cfgParsRead(configFilePath) # #global ui # #Dialog = QtGui.QDialog() # #ui = Ui_Dialog() # #ui.setupUi(Dialog) # #ui.comboBox.addItems(material_properties_names) # #reply=Dialog.exec_() # # #bklist = configParser.get('Blacklist', 'bklist') # #say(configFilePath) # #say(bklist) ## def onPushPCB(self): PushPCB() # #def onExport3DStep(self): # global last_3d_path, start_time, load_sketch # #say("export3DSTEP") # if load_sketch==False: # msg="""Edge editing NOT supported on FC0.15!
please upgrade your FC release""" # say_warning(msg) # msg="Edge editing NOT supported on FC0.15!" # sayerr(msg) # #if 0: # #if FreeCAD.ActiveDocument is None: # # FreeCAD.newDocument("PCB_Sketch") # # PCB_Sketch= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PCB_Sketch') # # offset=[0.0,0.0] #offset=[148.5,98.5] # # FreeCAD.activeDocument().PCB_Sketch.Placement = FreeCAD.Placement(FreeCAD.Vector(offset[0],offset[1]),FreeCAD.Rotation(0.000000,0.000000,0.000000,1.000000)) # # FreeCAD.getDocument('PCB_Sketch').recompute() # # FreeCADGui.SendMsgToActiveView("ViewFit") # else: # sel = FreeCADGui.Selection.getSelection() # if len (sel) == 1: # #sayw(doc.Name) # if "Sketch" in sel[0].TypeId: # cfg_read_all() # if last_3d_path is "": # last_3d_path=last_pcb_path # sayw(last_pcb_path) # #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") # testing=False # if not testing: # Filter="" # name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push Sketch PCB Edge to KiCad board ...", # last_3d_path, "*.kicad_pcb") # else: # name='d:/Temp/e2.kicad_pcb' # #say(name) # if name: # if os.path.exists(name): # last_3d_path=os.path.dirname(name) # start_time=current_milli_time() # export_pcb(name) # else: # msg="""Save to an EXISTING KiCad pcb file to update your Edge!""" # say_warning(msg) # msg="Save to an EXISTING KiCad pcb file to update your Edge!" # sayerr(msg) # # else: # msg="""select one Sketch to be pushed to kicad board!""" # sayerr(msg) # say_warning(msg) # # else: # msg="""select one Sketch to be pushed to kicad board!""" # sayerr(msg) # say_warning(msg) # def onHelp(self): # m = Timer (5.0,ZoomFitThread) # m.start() def onHelp(self): global expanded_view global configFilePath, ini_content, pt_osx, pt_lnx #QtGui.QMessageBox.information(None,"info ...","your home path is \r\n"+ home+"\r\n") #say("your home path is "+ expanduser("~")) #pm = QtGui.QPixmap() #pm.loadFromData(base64.b64decode(dock_left_b64)) #sayw(expanded_view) if expanded_view!=2: clear_console() cfg_read_all() temporary_undock() #to do .... #undock() #resolution = QtGui.QDesktopWidget().screenGeometry() #xp=(resolution.width() / 2) - sizeXMax/2 # - (KSUWidget.frameSize().width() / 2) #yp=(resolution.height() / 2) - sizeY/2 # - (KSUWidget.frameSize().height() / 2)) #KSUWidget.setGeometry(xp, yp, sizeXMax, sizeY) textEdit_dim = textEdit_dim_base self.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) self.textEdit.setToolTip(translate("Ui_DockWidget", "Help Start Guide")) centerOnScreen (KSUWidget) #ini_content=read_ini_file() font_color="""""" import FreeCAD, FreeCADGui # paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/MainWindow") # if 'dark' in paramGet.GetString("StyleSheet").lower(): #we are using a StyleSheet font_color="""""" from PySide2 import QtGui font_color="""""" #FreeCADGui.getMainWindow().palette().background().color() sayw("kicad StepUp version "+str(___ver___)) help_txt="""kicad StepUp version """+___ver___+"""
""" help_txt+=font_color help_txt+="""Kicad StepUp is a tool set to easily collaborate between kicad pcb EDA (board and 3D parts) as STEP models and FreeCAD MCAD modeler.
""" help_txt+="""
""" help_txt+=font_color home = expanduser("~") ini_file_full_path=home+os.sep+'ksu-config.ini' ini_fname='ksu-config.ini' #FreeCAD.Console.PrintMessage(FreeCAD.ConfigGet("AppHomePath")+'Mod/') file_path_mod=FreeCAD.ConfigGet("UserAppData")+'Mod' if os.path.exists(file_path_mod): say('Mod folder exists\r\n') #else: # msg="missing Mod folder Module!\r\n\r\n" # reply = QtGui.QMessageBox.information(None,"Info ...",msg) #if not pt_osx and not pt_lnx: # import ksu_locator # ksuWBpath = os.path.dirname(ksu_locator.__file__) # #sys.path.append(ksuWB + '/Gui') # ksuWB_demo_path = os.path.join( ksuWBpath, 'demo') # pdf_file_path=os.path.join(ksuWB_demo_path,'kicadStepUp-starter-Guide.pdf') # #say(pdf_file_path) # pdf_name='kicadStepUp-starter-Guide' # help_txt+="configuration options:
Configuration options are located in the preferences system of FreeCAD, which is located in the Edit menu -> Preferences.
" # help_txt+="starter Guide:
"+pdf_name+"
" #if not pt_osx and not pt_lnx: # pdf_file_path=file_path_mod+os.sep+'ksu-wb'+os.sep+'demo'+os.sep+'kicadStepUp-starter-Guide.pdf' # #say(pdf_file_path) # pdf_name='kicadStepUp-starter-Guide' # help_txt+="configuration options:
Configuration options are located in the preferences system of FreeCAD, which is located in the Edit menu -> Preferences.
" # help_txt+="starter Guide:
"+pdf_name+"
" #else: # #say(pdf_file_path) # pdf_name='kicadStepUp-starter-Guide' # help_txt+="configuration options:
Configuration options are located in the preferences system of FreeCAD, which is located in the Edit menu -> Preferences.
" # help_txt+="starter Guide:
FC-UserAppData/Mod
"+pdf_name+"

" import ksu_locator ksuWBpath = os.path.dirname(ksu_locator.__file__) ksuWB_demo_path = os.path.join( ksuWBpath, 'demo') pdf_file_path=os.path.join(ksuWB_demo_path,'kicadStepUp-starter-Guide.pdf') pdf_name='kicadStepUp-cheat-sheet' help_txt+="configuration options:
Configuration options are located in the preferences system of FreeCAD, which is located in the Edit menu -> Preferences.
" help_txt+="starter Guide:
"+pdf_file_path+"
"+pdf_name+"

" help_txt+="kicadStepUp-cheat-sheet.pdf
" #help_txt+="" help_txt+="StepUp can be used to align 3D model to kicad footprint.
" help_txt+="The artwork can be used for MCAD interchange and collaboration, and for enclosure design.
" help_txt+="The 3D visualization of components on board assemblies in kicad 3dviewer, will be the same in your mechanical software, " help_txt+="because of the STEP interchange format.
" help_txt+="It is also possible to Update a pcb Edge from a FC Sketcher.
" help_txt+="
First of all: configure your path to 3D models in
FreeCAD Preferences Page
" help_txt+="Note: each button has its own Tooltip
" help_txt+="useful buttons:
Load kicad Board directly -> will load kicad board and parts in FreeCAD coming from kicad '.kicad_pcb' file
" help_txt+="Load kicad Footprint module -> will load directly kicad footprint in FreeCAD to easily align the 3D model to footprint
" help_txt+="Export to kicad STEP & scaled VRML -> will convert MCAD model to STEP and VRML to be used by Kicad and kicad StepUp
" help_txt+=" -> VRML can be multipart;
-> STEP must be single part

('Part Boolean Union' or 'Part Makecompound')
" help_txt+="assign material to selected colors and your VRML 3D models will have nice shiny effects
" help_txt+="Push pcb Sketch to kicad_pcb Edge -> will push pcb Sketch to kicad_pcb Edge in your design; it can be done with an empty or with an existing pcb Edge
" help_txt+="
for a more detailed help have a look at
kicadStepUp-starter-Guide.pdf
" help_txt+="or just follow the YouTube video tutorials
kicadStepUp basics
" help_txt+="kicadStepUp STEP alignment to Kicad footprint
" help_txt+="check always the latest release of kicadStepUp

" help_txt+="Designing in kicad native 3d-viewer will produce a fully aligned STEP MCAD version " help_txt+="with the same view of kicad 3d render.
" help_txt+="Moreover, KiCad StepUp tool set will let you to load the kicad footprint inside FreeCAD and align the 3D part with a visual real time feedback " help_txt+="of the 3d model and footprint reciprocal position.
" help_txt+="With this tool is possible to download a part from on-line libraries, align the model to kicad footprint " help_txt+="and export the model to wrl, for immediate 3d-viewer alignment in pcbnew.
" help_txt+="Now the two words are connected for a better collaboration; just design in kicad EDA and transfer " help_txt+="the artwork to MCAD (FreeCAD) smoothly.
" help_txt+="The workflow is very simple and maintains the usual way to work with kicad:
" help_txt+="Add models to your library creating 3D models in FreeCAD, or getting models from online libs " help_txt+="or from the parametric 3D lib expressly done to kicad kicadStepUp 3D STEP models generator
" help_txt+="Once you have your 3D MCAD model, you need to have a copy of that in STEP and VRML format.
" help_txt+="(with the latest kicad release you can only have STEP model, VRML is not needed anymore, but it is possible" help_txt+=" to mix VRML, STEP and IGES format)
" help_txt+="Just exporting the model with FreeCAD and put your model in the same folder in which " help_txt+="normally you are used to put vrml models; the script will assembly the MCAD board and models as in 3d-viewer of kicad." help_txt+="
NB
STEP model has to be fused in single object

(Part Boolean Union of objects)" help_txt+="
or a Compoud (Part Makecompound of objects)
" help_txt+="
enable 'Report view' Panel to see helping messages" help_txt+="
" help_txt+="
" self.textEdit.setText(help_txt) #self.textEdit.setTextColor(QtGui.QColor('black')) #self.textEdit.setStyleSheet("background-color: rgb(255, 255, 255)"); # to reset it to default color ... #txtEdit->setStyleSheet(""); expanded_view=2 #cfgParsRead(configFilePath) else: expanded_view=0 clear_console() KSUWidget.setVisibility=False #textEdit_dim = textEdit_dim_hide #self.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) cfg_read_all() if docking_mode == 'float': undock() KSUWidget.setVisibility=True elif docking_mode == 'left': #tabify() dock() KSUWidget.setVisibility=True else: dock_right() KSUWidget.setVisibility=True sayw("kicad StepUp version "+str(___ver___)) #self.textEdit.setStyleSheet(""); #say('onHelp') #reply = QtGui.QMessageBox.question(None, "", "step file exists, overwrite?",QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) ## ## sketch testing button def Export3DStepF(): global last_3d_path, last_pcb_path, stp_exp_mode, use_AppPart, use_Links, links_imp_mode, use_LinkGroups #say("export3DSTEP") sel = FreeCADGui.Selection.getSelection() if len (sel) > 0: #sayw(doc.Name) if "App::Part" in sel[0].TypeId and not use_AppPart: msg="""App::Part hierarchy cannot be exported ATM
use the buttons to make a Union or Compound before exporting it""" say_warning(msg) else: cfg_read_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") last_3d_path = pg.GetString("last_3d_path") if len(last_3d_path) == 0: last_3d_path=last_pcb_path sayw(last_pcb_path) #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Export 3D STEP ...", make_unicode(last_3d_path), "*.step *.stp") else: name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Export 3D STEP ...", make_unicode(last_3d_path), "*.step *.stp",options=QtWidgets.QFileDialog.DontUseNativeDialog) #say(name) if name: last_3d_path=os.path.dirname(name) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_3d_path",make_string(last_3d_path)) #my_sk=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.PCB_Sketch,False) #my_sk_name=FreeCAD.ActiveDocument.ActiveObject.Name #FreeCAD.ActiveDocument.removeObject(FreeCAD.ActiveDocument.PCB_Sketch.Name) #ImportGui.export(sel,name) ## PCB_Sketch=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.getObject(my_sk_name),False) #cpy_sketch(my_sk_name,"PCB_Sketch") #FreeCAD.ActiveDocument.removeObject(my_sk_name) #FreeCAD.ActiveDocument.getObject("Board_Geoms").addObject(FreeCAD.ActiveDocument.getObject("PCB_Sketch")) sel = FreeCADGui.Selection.getSelection() selN=sel[0].Name doc = FreeCAD.ActiveDocument #sayw(stp_exp_mode) #stop #deselect Sketches if not use_AppPart: for e in sel: if 'Sketch' in e.TypeId: FreeCADGui.Selection.removeSelection(FreeCAD.ActiveDocument.getObject(e.Name)) sel = FreeCADGui.Selection.getSelection() else: #skl=[sk,grp] skl=[] skl=find_skt_in_Doc() #print skl #print sk_name,';',grp_name for sk in skl: say('moving sketch from grp') #print sk FreeCAD.ActiveDocument.getObject(sk[1]).removeObject(FreeCAD.ActiveDocument.getObject(sk[0])) #FreeCAD.ActiveDocument.getObject(selN).removeObject(FreeCAD.ActiveDocument.getObject(sk_name)) #stop fcv = getFCversion() fcb = checkFCbug(fcv) # sayerr('not fcb '+str(not fcb)) # sayw(stp_exp_mode) # say(fcv[0]) if (stp_exp_mode == 'hierarchy' and not fcb) or (fcv[0]==0 and fcv[1]<=16): # FC not bugged or < 0.17 sayw('exporting hierarchy') ImportGui.export(sel,name) elif (stp_exp_mode == 'onelevel') or (stp_exp_mode == 'hierarchy' and fcb): sayw('exporting ONE level hierarchy') try: import kicadStepUpCMD except: sayerr('to export STEP it is necessary to use StepUp Workbench
instead of the single Macro
(because of '+str(fcv)+' FC bug)') msg="""to export STEP it is necessary to use StepUp Workbench
instead of the single Macro
(because of """+str(fcv)+""" FC bug
""" say_warning(msg) for sk in skl: say('including sketch in grp') FreeCAD.ActiveDocument.getObject(sk[1]).addObject(FreeCAD.ActiveDocument.getObject(sk[0])) stop if fcb: cpmode='compound' else: cpmode='part' suffix='_' to_export_name=kicadStepUpCMD.deep_copy(doc,cpmode,suffix) # to_export_name=FreeCAD.ActiveDocument.ActiveObject.Name #sayw(FreeCAD.ActiveDocument.getObject(to_export_name).Label) #say(sel[0]) __objs__=[] __objs__.append(FreeCAD.ActiveDocument.getObject(to_export_name)) #import ImportGui ImportGui.export(__objs__,name) #FreeCAD.ActiveDocument.removeObject(to_export_nam) removesubtree(__objs__) del __objs__ if fcb: # bugged FC version sayerr('exported a simplified STEP hierarchy because of '+str(fcv)+' FC bug') msg="""exported a simplified STEP hierarchy
because of """+str(fcv)+""" FC bug
""" say_warning(msg) #FreeCADGui.Selection.removeSelection(sel[0]) #FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(to_export_name)) #sel1 = FreeCADGui.Selection.getSelection() #say(sel1[0]) #ImportGui.export(sel1[0],name) #stop elif stp_exp_mode == 'flat': # need to deselect all 'Part' containers and select all simple objs #say('flat') if len(sel)==1 and 'App::Part' in sel[0].TypeId: ## flattening a Part hierarchy container sayw('flattening Part container') # FreeCADGui.Selection.removeSelection(sel[0]) __objs__=[] # sayerr(FreeCAD.ActiveDocument.getObject(selN).Label) # sayerr(FreeCAD.ActiveDocument.getObject(selN).OutListRecursive) for o in FreeCAD.ActiveDocument.getObject(selN).OutListRecursive: #sayw( o.TypeId ) #if 'Part::Feature' in o.TypeId: if hasattr(o, 'Shape'): # print o.Label # say ('adding ') # FreeCADGui.Selection.addSelection(o) __objs__.append(o) ImportGui.export(__objs__,name) del __objs__ else: sayw('exporting selection') ImportGui.export(sel,name) #print selN,'-',sk_name #FreeCAD.ActiveDocument.getObject(selN).removeObject(App.ActiveDocument.getObject(sk_name)) if use_AppPart: for sk in skl: say('including sketch in grp') FreeCAD.ActiveDocument.getObject(sk[1]).addObject(FreeCAD.ActiveDocument.getObject(sk[0])) # PCB_Sketch=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.getObject(my_sk_name),False) #try: # FreeCAD.ActiveDocument.getObject(selN).addObject(FreeCAD.ActiveDocument.getObject(sk_name)) #except: # sayw('no PCB_Sketch2') # pass else: msg="""select something to be exported!""" sayerr(msg) say_warning(msg) ## def Import3DModelF(): global last_3d_path, last_pcb_path global zfit say("import3DModel") #sayw(doc.Name) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") last_3d_path = pg.GetString("last_3d_path") cfg_read_all() if len(last_3d_path) == 0: last_3d_path=last_pcb_path sayw(last_pcb_path) Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Import 3D File...", make_unicode(last_3d_path), "*.step *.stp *.stpZ *.iges *.igs *.FCStd") else: name, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Import 3D File...", make_unicode(last_3d_path), "*.step *.stp *.stpZ *.iges *.igs *.FCStd",options=QtWidgets.QFileDialog.DontUseNativeDialog) #say(name) if name: ext = os.path.splitext(os.path.basename(name))[1] #sayw(ext.lower()) if ext.lower() == ".fcstd": FreeCAD.open(name) else: if FreeCAD.ActiveDocument is None: #say("none") doc=FreeCAD.newDocument() else: doc=FreeCAD.ActiveDocument ##ReadShapeCompoundMode paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") ReadShapeCompoundMode_status=paramGetVS.GetBool("ReadShapeCompoundMode") #sayerr("checking ReadShapeCompoundMode") sayw("ReadShapeCompoundMode status "+str(ReadShapeCompoundMode_status)) enable_ReadShapeCompoundMode=False if ReadShapeCompoundMode_status: paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") paramGetVS.SetBool("ReadShapeCompoundMode",False) sayw("disabling ReadShapeCompoundMode") enable_ReadShapeCompoundMode=True FreeCAD.setActiveDocument(doc.Name) FreeCAD.ActiveDocument=FreeCAD.getDocument(doc.Name) FreeCADGui.ActiveDocument=FreeCADGui.getDocument(doc.Name) if name.lower().endswith('stpz'): try: import stepZ stepZ.insert(name,doc.Name) except: sayerr('.stpZ not supported!') else: ImportGui.insert(name, doc.Name) #enable_ReadShapeCompoundMode=False if enable_ReadShapeCompoundMode: paramGetVS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") paramGetVS.SetBool("ReadShapeCompoundMode",True) sayw("enabling ReadShapeCompoundMode") if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") last_3d_path=os.path.dirname(name) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_3d_path",make_string(last_3d_path)) ## #import PySide #from PySide import QtCore, QtGui #, QtWidgets ##from PyQt5 import QtCore, QtGui, QtWidgets QtWidgets = QtGui class Ui_STEP_Preferences(object): def setupUi(self, STEP_Preferences): import os import ksu_locator ksuWBpath = os.path.dirname(ksu_locator.__file__) #sys.path.append(ksuWB + '/Gui') ksuWB_demo_path = os.path.join( ksuWBpath, 'demo') STEP_Preferences.setObjectName("STEP_Preferences") STEP_Preferences.resize(860, 752) STEP_Preferences.setWindowTitle("STEP Suggested Preferences") STEP_Preferences.setToolTip("") self.verticalLayoutWidget = QtWidgets.QWidget(STEP_Preferences) self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 847, 732)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.label = QtWidgets.QLabel(self.verticalLayoutWidget) self.label.setText("Please set your preferences for STEP Import Export to:") self.label.setObjectName("label") self.verticalLayout_2.addWidget(self.label) self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget) self.label_2.setText("") self.label_2.setPixmap(QtGui.QPixmap(os.path.join(ksuWB_demo_path,"Import-Export-settings.png"))) self.label_2.setObjectName("label_2") self.verticalLayout_2.addWidget(self.label_2) self.label_3 = QtWidgets.QLabel(self.verticalLayoutWidget) self.label_3.setText("(you can disable this warning on StepUp preferences)") self.label_3.setAlignment(QtCore.Qt.AlignCenter) self.label_3.setObjectName("label_3") self.verticalLayout_2.addWidget(self.label_3) self.buttonBox = QtWidgets.QDialogButtonBox(self.verticalLayoutWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth()) self.buttonBox.setSizePolicy(sizePolicy) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout_2.addWidget(self.buttonBox) self.verticalLayout.addLayout(self.verticalLayout_2) self.buttonBox.accepted.connect(STEP_Preferences.accept) self.retranslateUi(STEP_Preferences) QtCore.QMetaObject.connectSlotsByName(STEP_Preferences) def retranslateUi(self, STEP_Preferences): pass ## class Ui_LayerSelection(object): def setupUi(self, LayerSelection): LayerSelection.setObjectName("LayerSelection") LayerSelection.resize(341, 232) LayerSelection.setWindowTitle("LayerSelection") LayerSelection.setToolTip("") self.buttonBoxLayer = QtWidgets.QDialogButtonBox(LayerSelection) self.buttonBoxLayer.setGeometry(QtCore.QRect(60, 190, 271, 32)) self.buttonBoxLayer.setOrientation(QtCore.Qt.Horizontal) self.buttonBoxLayer.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBoxLayer.setObjectName("buttonBoxLayer") self.verticalLayoutWidget = QtWidgets.QWidget(LayerSelection) self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 325, 171)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.label = QtWidgets.QLabel(self.verticalLayoutWidget) self.label.setText("Select the layer to push the Sketch\n" "Default \'Edge.Cuts\'") self.label.setObjectName("label") self.verticalLayout_2.addWidget(self.label) self.verticalLayout.addLayout(self.verticalLayout_2) self.comboBoxLayerSel = QtWidgets.QComboBox(self.verticalLayoutWidget) self.comboBoxLayerSel.setObjectName("comboBoxLayerSel") self.verticalLayout.addWidget(self.comboBoxLayerSel) self.radioBtn_newdoc = QtWidgets.QRadioButton(self.verticalLayoutWidget) self.radioBtn_newdoc.setToolTip(translate("Ui_LayerSelection", "open in new FreeCAD document")) self.radioBtn_newdoc.setText(translate("Ui_LayerSelection", "open in new document")) self.radioBtn_newdoc.setChecked(True) self.radioBtn_newdoc.setObjectName("radioBtn_newdoc") self.verticalLayout.addWidget(self.radioBtn_newdoc) self.radioBtn_replace_pcb = QtWidgets.QRadioButton(self.verticalLayoutWidget) self.radioBtn_replace_pcb.setToolTip(translate("Ui_LayerSelection", "

replace PCB in current document

N.B. Sketch constrains will be deleted!

")) self.radioBtn_replace_pcb.setText(translate("Ui_LayerSelection", "replace PCB and Sketch in current document")) self.radioBtn_replace_pcb.setObjectName("radioBtn_replace_pcb") self.verticalLayout.addWidget(self.radioBtn_replace_pcb) self.radioBtn_keep_sketch = QtWidgets.QRadioButton(self.verticalLayoutWidget) self.radioBtn_keep_sketch.setToolTip(translate("Ui_LayerSelection", "

keep Sketch in current document

N.B. this option will keep Sketch & constrains but replace the PCB

This could lead to a unsynced Sketch feature

")) self.radioBtn_keep_sketch.setText(translate("Ui_LayerSelection", "replace PCB and keep Sketch in curr. doc")) self.radioBtn_keep_sketch.setObjectName("radioBtn_keep_sketch") self.verticalLayout.addWidget(self.radioBtn_keep_sketch) self.retranslateUi(LayerSelection) self.buttonBoxLayer.accepted.connect(LayerSelection.accept) self.buttonBoxLayer.rejected.connect(LayerSelection.reject) QtCore.QMetaObject.connectSlotsByName(LayerSelection) #-------#------------------------------------------------------------------------- self.comboBoxLayerSel.currentTextChanged.connect(self.on_combobox_changed) #addition def retranslateUi(self, LayerSelection): pass def on_combobox_changed(self, value): #print('combo change',value) if value != 'Edge.Cuts': #self.radioBtn_newdoc.setChecked(True) self.radioBtn_replace_pcb.setChecked(True) #self.radioBtn_replace_pcb.setEnabled(False) self.radioBtn_keep_sketch.setEnabled(False) self.radioBtn_replace_pcb.setEnabled(True) self.radioBtn_replace_pcb.setText("import Layer in active document") else: self.radioBtn_replace_pcb.setEnabled(True) self.radioBtn_keep_sketch.setEnabled(True) ## class Ui_LayerSelectionOut(object): def setupUi(self, LayerSelectionOut): LayerSelectionOut.setObjectName("LayerSelectionOut") LayerSelectionOut.resize(293, 249) LayerSelectionOut.setWindowTitle("LayerSelection") LayerSelectionOut.setToolTip("") self.buttonBoxLayer = QtWidgets.QDialogButtonBox(LayerSelectionOut) self.buttonBoxLayer.setGeometry(QtCore.QRect(10, 200, 271, 32)) self.buttonBoxLayer.setOrientation(QtCore.Qt.Horizontal) self.buttonBoxLayer.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBoxLayer.setObjectName("buttonBoxLayer") self.verticalLayoutWidget = QtWidgets.QWidget(LayerSelectionOut) self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 271, 80)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.label = QtWidgets.QLabel(self.verticalLayoutWidget) self.label.setText("Select the layer to push the Sketch\nDefault \'Edge.Cuts\'") self.label.setObjectName("label") self.verticalLayout_2.addWidget(self.label) self.verticalLayout.addLayout(self.verticalLayout_2) self.comboBoxLayerSel = QtWidgets.QComboBox(self.verticalLayoutWidget) self.comboBoxLayerSel.setObjectName("comboBoxLayerSel") self.verticalLayout.addWidget(self.comboBoxLayerSel) self.verticalLayoutWidget_2 = QtWidgets.QWidget(LayerSelectionOut) self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(10, 90, 271, 99)) self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2") self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.verticalLayout_3.setObjectName("verticalLayout_3") self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.width_label = QtWidgets.QLabel(self.verticalLayoutWidget_2) self.width_label.setMinimumSize(QtCore.QSize(150, 0)) self.width_label.setToolTip("") self.width_label.setText(translate("Ui_LayerSelectionOut", "Line Width:")) self.width_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.width_label.setObjectName("width_label") self.horizontalLayout.addWidget(self.width_label) self.lineEdit_width = QtWidgets.QLineEdit(self.verticalLayoutWidget_2) self.lineEdit_width.setToolTip(translate("Ui_LayerSelectionOut", "Line width for drawings")) self.lineEdit_width.setText("0.16") self.lineEdit_width.setObjectName("lineEdit_width") self.horizontalLayout.addWidget(self.lineEdit_width) self.verticalLayout_3.addLayout(self.horizontalLayout) self.retranslateUi(LayerSelectionOut) self.buttonBoxLayer.accepted.connect(LayerSelectionOut.accept) self.buttonBoxLayer.rejected.connect(LayerSelectionOut.reject) QtCore.QMetaObject.connectSlotsByName(LayerSelectionOut) #-------#------------------------------------------------------------------------- self.comboBoxLayerSel.currentTextChanged.connect(self.on_combobox_changed) #addition def retranslateUi(self, LayerSelectionOut): pass def on_combobox_changed(self, value): #print('combo change',value) if 'Zone' in value: self.lineEdit_width.setEnabled(False) self.width_label.setEnabled(False) #self.width_label.setText("-----:") else: self.lineEdit_width.setEnabled(True) self.width_label.setEnabled(True) ## def PushPCB(): #def onExport3DStep(self): global last_3d_path, start_time, load_sketch, last_pcb_path, edge_width #say("export3DSTEP") if load_sketch==False: msg = translate("PushPCB", "Edge editing NOT supported on FC0.15!
please upgrade your FC release") say_warning(msg) msg = translate("PushPCB", "Edge editing NOT supported on FC0.15!") sayerr(msg) #if 0: #if FreeCAD.ActiveDocument is None: # FreeCAD.newDocument("PCB_Sketch") # PCB_Sketch= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PCB_Sketch') # offset=[0.0,0.0] #offset=[148.5,98.5] # FreeCAD.activeDocument().PCB_Sketch.Placement = FreeCAD.Placement(FreeCAD.Vector(offset[0],offset[1]),FreeCAD.Rotation(0.000000,0.000000,0.000000,1.000000)) # FreeCAD.getDocument('PCB_Sketch').recompute() # FreeCADGui.SendMsgToActiveView("ViewFit") else: sel = FreeCADGui.Selection.getSelection() if len (sel) == 1: #sayw(doc.Name) if "Sketch" in sel[0].TypeId: if 0: from pivy import coin from math import degrees print(translate("PushPCB", "getting camera view")) pcam = FreeCADGui.ActiveDocument.ActiveView.getCamera() sketch = sel[0] rot = sketch.getGlobalPlacement().Rotation print(rot,degrees(rot.Angle), float(rot.Angle)) cam = FreeCADGui.ActiveDocument.ActiveView.getCameraNode() if rot.Angle < 0.001: rot.Angle = 0 print (translate("PushPCB", "forcing rotAngle to 0")) cam.orientation.setValue(coin.SbVec3f(rot.Axis.x, rot.Axis.y, rot.Axis.z), rot.Angle) #-pi) FreeCADGui.ActiveDocument.ActiveView.fitAll() print(translate("PushPCB", "evaluate to recompute")) FreeCAD.ActiveDocument.recompute() #sel[0].ViewObject.Visibility = False #print(sel[0].Label) #sel[0].recompute(True) cfg_read_all() if len(last_pcb_path) == 0: last_pcb_path = "" # last_3d_path=last_pcb_path # sayw(last_pcb_path) #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") # layer_list = ['Edge.Cuts','Dwgs.User','Cmts.User','Eco1.User','Eco2.User','Margin', 'F.FillZone', 'F.KeepOutZone', 'F.MaskZone','B.FillZone', 'B.KeepOutZone', 'B.MaskZone',] layer_list = ['Edge.Cuts','Dwgs.User','Cmts.User','Eco1.User','Eco2.User','User.1','User.2','User.3','User.4','User.5','User.6','User.7','User.8','User.9','Margin', 'F.FillZone', 'F.KeepOutZone', 'F.MaskZone','B.FillZone', 'B.KeepOutZone', 'B.MaskZone',] LayerSelectionDlg = QtGui.QDialog() ui = Ui_LayerSelectionOut() ui.setupUi(LayerSelectionDlg) ui.comboBoxLayerSel.addItems(layer_list) if 0: ui.comboBoxLayerSel.setEditable(True) reply=LayerSelectionDlg.exec_() if reply==1: # ok SketchLayer=str(ui.comboBoxLayerSel.currentText()) if 1: #'Edge' not in SketchLayer: edge_width=float(ui.lineEdit_width.text().replace(',','.')) print(SketchLayer) skname=sel[0].Name #else: #canel # print('Cancel') # stop # pass testing=False if not testing: Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push Sketch PCB Edge to KiCad board ...", make_unicode(last_pcb_path), "*.kicad_pcb") else: name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push Sketch PCB Edge to KiCad board ...", make_unicode(last_pcb_path), "*.kicad_pcb",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: name='d:/Temp/e2.kicad_pcb' #say(name) if name: if os.path.exists(name): last_pcb_path=os.path.dirname(name) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path",make_string(last_pcb_path)) start_time=current_milli_time() export_pcb(name,SketchLayer,skname) else: if not (name.endswith("kicad_pcb")): name = name + ".kicad_pcb" msg = translate("PushPCB", "Saving to an empty KiCad pcb file")+'\n'+name sayw(msg) last_pcb_path=os.path.dirname(name) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path",make_string(last_pcb_path)) import ksu_locator ksuWBpath = os.path.dirname(ksu_locator.__file__) ksuWB_demo_path = os.path.join( ksuWBpath, 'demo') copyfile(os.path.join(ksuWB_demo_path,'empty-kv5.kicad_pcb'), name) start_time=current_milli_time() export_pcb(name,SketchLayer,skname) # msg="""Save to an EXISTING KiCad pcb file to update your Edge!""" # say_warning(msg) # msg="Save to an EXISTING KiCad pcb file to update your Edge!" # sayerr(msg) else: #cancel print(translate("PushPCB", "Cancel")) pass if 0: print(translate("PushPCB", "Restoring cam view")) FreeCADGui.ActiveDocument.ActiveView.setCamera(pcam) # sel[0].ViewObject.Visibility = True else: msg = translate("PushPCB", "Select one Sketch to be pushed to kicad board!") sayerr(msg) say_warning(msg) else: msg = translate("PushPCB", "Select one Sketch to be pushed to kicad board!") sayerr(msg) say_warning(msg) ## def Sync3DModel(): global last_3d_path, start_time global last_fp_path, test_flag global configParser, configFilePath, last_pcb_path global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global pcb_path, use_AppPart, force_oldGroups, use_Links, use_LinkGroups global original_filename, aux_orig, grid_orig global off_x, off_y, maxRadius, use_pypro import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser #say("export3DSTEP") if load_sketch==False: msg="""Board editing NOT supported on FC0.15!
please upgrade your FC release""" say_warning(msg) msg="Board editing NOT supported on FC0.15!" sayerr(msg) else: sel = FreeCADGui.Selection.getSelection() if len (sel) == 1: if hasattr(sel[0],"Shape") or "Link" in sel[0].TypeId: cfg_read_all() if len(last_pcb_path) == 0: last_pcb_path = "" #sayw(last_pcb_path) #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") testing=False if not testing: Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): fname, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Load KiCad PCB board data...", make_unicode(last_pcb_path), "*.kicad_pcb") else: fname, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Load KiCad PCB board data...", make_unicode(last_pcb_path), "*.kicad_pcb",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: fname='c:/Temp/demo/demo-test-mp.kicad_pcb' if fname is not None: if os.path.exists(fname): last_pcb_path=os.path.dirname(fname) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path",make_string(last_pcb_path)) start_time=current_milli_time() doc=FreeCAD.ActiveDocument #filePath=last_pcb_path #fpath=filePath+os.sep+doc.Label+'.kicad_pcb' #sayerr('to '+fpath) #print fname if fname is None: fpath=original_filename else: fpath=fname sayerr('Loading from '+fpath) #stop if len(fpath) > 0: #new_edge_list=getBoardOutline() #say (new_edge_list) cfg_read_all() path, fname = os.path.split(fpath) name=os.path.splitext(fname)[0] ext=os.path.splitext(fname)[1] fpth = os.path.dirname(os.path.abspath(fpath)) #filePath = os.path.split(os.path.realpath(__file__))[0] say ('file path '+fpth); say('kicad board file: '+fname) # stop if fpth == "": fpth = "." last_pcb_path = fpth last_pcb_path = re.sub("\\\\", "/", last_pcb_path) ini_vars[10] = last_pcb_path #cfg_update_all() #sayerr(name+':'+ext) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) mypcb = KicadPCB.load(fpath) reply=False;ref_found=False;input_ref='' #input_ref = QtGui.QInputDialog.getText(None, 'Sync Ref', 'Reference to be synced',QtGui.QLineEdit.EchoMode.Normal,'REF#',reply) input_ref = QtGui.QInputDialog.getText(None, 'Sync Ref', 'Reference to be synced',QtGui.QLineEdit.EchoMode.Normal,'REF#') #,reply) #print (reply);print('*');print input_ref if len(input_ref) > 1: if input_ref[1]: matching_Reference=input_ref[0] #'LD1' matching_TimeStamp='Null' for m in mypcb.module: try: Ref = m.fp_text[0][1] except: Ref = m.property[0][1] #kv8 fp reference #print(Ref);print(len(Ref)) if Ref.lstrip('"').rstrip('"') == matching_Reference: say ('found Reference: '+Ref) ref_found=True if hasattr(m,'tstamp'): matching_TimeStamp=m.tstamp say ('linked TimeStamp: '+matching_TimeStamp) if sel[0].Label.rfind('_') < sel[0].Label.rfind('['): ts = sel[0].Label[sel[0].Label.rfind('_')+1:sel[0].Label.rfind('[')] nbrModel = sel[0].Label[sel[0].Label.rfind('['):] else: ts = sel[0].Label[sel[0].Label.rfind('_')+1:] nbrModel = '' #ts = sel[0].Label[sel[0].Label.rfind('_')+1:] #mmodel=m.model[0][0] #print (mmodel[mmodel.rfind('/')+1:mmodel.rfind('.')]);stop if len (m.model)>0: if nbrModel == '': mmodel=m.model[0][0] else: nMd=int(nbrModel.replace('[','').replace(']',''))-1 #print (nMd) mmodel=m.model[nMd][0] if mmodel.rfind('/') !=-1: mmodel=mmodel[mmodel.rfind('/')+1:mmodel.rfind('.')] else: mmodel=mmodel[mmodel.rfind('\\')+1:mmodel.rfind('.')] else: mmodel='' if ((len (ts) != 8) and (len (ts) != 12)) or sel[0].Label.rfind('_') == -1: msg="TimeStamp not found!\nAdding & Syncing Ref & TimeStamp" sayw(msg) if len (mmodel)>0: sel[0].Label=Ref+'_'+mmodel.replace('.','')+'_'+matching_TimeStamp+nbrModel else: sel[0].Label=Ref+'_'+sel[0].Label+'_'+matching_TimeStamp+nbrModel else: if len(matching_TimeStamp) > 8: matching_TimeStamp=matching_TimeStamp[-12:] if len (mmodel)>0: sel[0].Label=Ref.lstrip('"').rstrip('"')+'_'+mmodel.replace('.','')+'_'+matching_TimeStamp+nbrModel else: sel[0].Label=Ref.lstrip('"').rstrip('"')+sel[0].Label[sel[0].Label.find('_'):sel[0].Label.rfind('_')+1]+matching_TimeStamp+nbrModel msg="Adding & Syncing Ref & TimeStamp" say(msg) #sel[0].Label=Ref+sel[0].Label[sel[0].Label.index('_'):sel[0].Label.rindex('_')+1]+matching_TimeStamp msg="""3D model Reference & TimeStamp synced
with the Reference """+Ref+""" of the kicad board!


""" msgr="3D model Reference & TimeStamp synced\nwith the Reference "+Ref+" of the kicad board!" say(msgr) say_info(msg) else: sayerr('Reference: '+Ref+' is missing TimeStamp field') msg="""Reference: """+Ref+""" is missing TimeStamp field""" say_warning(msg) if not ref_found: sayerr('Reference: '+matching_Reference+' not found!') msg="""Reference: """+matching_Reference+""" not found!""" say_warning(msg) else: msg="""Operation aborted!""" sayerr(msg) say_info(msg) #model_data=re.findall('\s\(module(\s'+matching_Reference+'\s.+?)\(at',data, re.MULTILINE|re.DOTALL) #model_data=re.findall('\s\(fp_text\s(reference\s'+matching_Reference'+'\s.+?)\(at,data, re.MULTILINE|re.DOTALL) else: msg="""Load an EXISTING KiCad pcb file to sync your 3D model Reference & TimeStamp!""" say_warning(msg) msg="Load an EXISTING KiCad pcb file to sync your 3D model Reference & TimeStamp!" sayerr(msg) else: msg="""select one 3D model to sync its TimeStamp based on its Reference in the kicad board!""" sayerr(msg) say_warning(msg) else: msg="""Operation aborted!""" sayerr(msg) say_info(msg) else: msg="""select one 3D model to sync its TimeStamp based on its Reference in the kicad board!""" sayerr(msg) say_warning(msg) ### def PushMoved(): global last_3d_path, start_time global last_fp_path, test_flag global configParser, configFilePath, last_pcb_path global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global pcb_path, use_AppPart, force_oldGroups, use_Links, use_LinkGroups global original_filename, aux_orig, grid_orig global off_x, off_y, maxRadius, use_pypro import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser ## to export to STEP an object and its links with a different placement and label ## two options must be set: 1) disable 'Reduce number of objects'; 2) disable 'Ignore instance names' ## NB the second one is not good for collaboration with different cads #say("export3DSTEP") if load_sketch==False: msg="""Board editing NOT supported on FC0.15!
please upgrade your FC release""" say_warning(msg) msg="Board editing NOT supported on FC0.15!" sayerr(msg) else: check_ok=False sel = FreeCADGui.Selection.getSelection() if len (sel) >= 1: for s in sel: if s.Label.rfind('_') < s.Label.rfind('['): ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] else: ts = s.Label[s.Label.rfind('_')+1:] if len (ts) == 8 or len (ts) == 12: #print(ts);stop check_ok=True #stop break #else: # msg="""select only 3D model(s) moved to be updated/pushed to kicad board!
a TimeSTamp is required!""" # sayerr(msg) # say_warning(msg) if check_ok: cfg_read_all() #pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") #pg.GetString("last_3d_path") if len(last_pcb_path) == 0: last_pcb_path=u'' #sayw(last_pcb_path) #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") testing=False #True if not testing: Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): fname, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push 3D PCB position(s) to KiCad board ...", make_unicode(last_pcb_path), "*.kicad_pcb") else: fname, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push 3D PCB position(s) to KiCad board ...", make_unicode(last_pcb_path), "*.kicad_pcb",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: fname='c:/Temp/demo/test-rot.kicad_pcb' if fname: if os.path.exists(fname): last_3d_path=os.path.dirname(fname) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_3d_path",make_string(last_3d_path)) start_time=current_milli_time() doc=FreeCAD.ActiveDocument #filePath=last_pcb_path #fpath=filePath+os.sep+doc.Label+'.kicad_pcb' #sayerr('to '+fpath) #print fname if fname is None: fpath=original_filename else: fpath=fname sayerr('saving to '+fpath) #stop if len(fpath) > 0: #new_edge_list=getBoardOutline() #say (new_edge_list) cfg_read_all() path, fname = os.path.split(fpath) name=os.path.splitext(fname)[0] ext=os.path.splitext(fname)[1] fpth = os.path.dirname(os.path.abspath(fpath)) #filePath = os.path.split(os.path.realpath(__file__))[0] say ('my file path '+fpth) # stop if fpth == "": fpth = "." last_pcb_path = fpth last_pcb_path = re.sub("\\\\", "/", last_pcb_path) ini_vars[10] = last_pcb_path #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) #sayerr(name+':'+ext) #mypcb = KicadPCB.load(fpath) with codecs.open(fpath,'r', encoding='utf-8') as txtFile: content = txtFile.readlines() # problems? content.append(u" ") txtFile.close() data=u''.join(content) tml= time.localtime() now=str(tml.tm_year)+'-'+str(tml.tm_mon)+'-'+str(tml.tm_mday)+'-'+str(tml.tm_hour)+'.'+str(tml.tm_min)+'.'+str(tml.tm_sec) #foname=os.path.join(path, name+'-bkp-'+now+ext+'-bak') foname=os.path.join(path, name+u'-bkp-'+make_unicode(now)+ext+u'-bak') oft=None if aux_orig == 1: oft=getAuxOrigin(data) elif grid_orig == 1: oft=getGridOrigin(data) #print oft gof=False origin_warn=False if oft is not None: if oft == [0.0,0.0]: origin_warn=True off_x=oft[0];off_y=-oft[1] offset = oft gof=True pcb_push=True else: pcb_push=False testing=False #True if testing is not True: try: #with codecs.open(foname,'w', encoding='utf-8') as ofile: # ofile.write(data) # ofile.close() copyfile(fpath, foname) say('file copied') except: msg="""problem in writing permissions to kicad board!

""" msg+="file saving aborted to
"+fpath+"


" msgr="problem in writing permissions to kicad board!\n" msgr+="file saving aborted to "+fpath+"\n" pcb_push=False say(msgr) say_info(msg) if pcb_push: mdp = 0 for s in sel: #sayw(doc.Name) if use_pypro: if hasattr(s,"TimeStamp"): ts=s.TimeStamp content = push3D2pcb(s,content,ts) else: msg="""select only 3D model(s) moved to be updated/pushed to kicad board!""" sayerr(msg) say_warning(msg) else: if s.Label.rfind('_') < s.Label.rfind('['): ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] else: ts = s.Label[s.Label.rfind('_')+1:] if len (ts) == 8 or len (ts) == 12: mdp+=1 #print(ts);stop content = push3D2pcb(s,content,ts) #else: # msg="""select only 3D model(s) moved to be updated/pushed to kicad board!
a TimeSTamp is required!""" # sayerr(msg) # say_warning(msg) newcontent=u''.join(content) #pcbTracks=re.findall('\s\(tracks(\s.+?)\)',data, re.MULTILINE|re.DOTALL) found_tracks=True if 0: # forcing found tracks to true 'cause kicad 6 doesn't write it anymore inside the file pcbTracks=re.findall('\s\(tracks(\s.+?)\)',data, re.MULTILINE|re.DOTALL) found_tracks=False if len(pcbTracks)>0: try: if (float(pcbTracks[0])) > 0: found_tracks=True except: found_tracks=True with codecs.open(fpath,'w', encoding='utf-8') as ofile: ofile.write(newcontent) ofile.close() say_time() say('pushed '+str(mdp)+' model(s)') msg="""3D model new position(s) pushed to kicad board!
["""+str(mdp)+""" model(s) updated]

""" if found_tracks: msg+="in case of tracks
you will need to fix your routing!


" msg+="file saved to
"+fpath+"


" msg+="backup file saved to
"+foname+"

" msgr="3D model new position pushed to kicad board!\n" msgr+="file saved to "+fpath+"\n" msgr+="backup file saved to "+foname say(msgr) say_info(msg) if origin_warn: if aux_orig == 1: origin_msg='AuxOrigin' elif grid_orig == 1: origin_msg='GridOrigin' msg = origin_msg +' is set in FC Preferences but not set in KiCAD pcbnew file' sayw(msg) msg=""""""+origin_msg+""" is set in FreeCAD Preferences
but not set in KiCAD pcbnew file
""" msg+="""

Please assign """+origin_msg+""" to your KiCAD pcbnew board file""" msg+="""
for a better Mechanical integration""" say_warning(msg) else: msg="""To update 3D model Position(s) in an EXISTING KiCad pcb file
the KiCAD pcbnew board file must have assigned \'Grid Origin\' or
\'Aux Origin\' (Drill and Place offset)!""" msg+="""
Moreover in FC StepUP preferences you must have
\'PCB Settings\'->\'PCB Placement\'
set to \'Grid Origin\' or \'Aux Origin\'""" say_warning(msg) msg="To update 3D model Position(s) in an EXISTING KiCad pcb file\nthe KiCAD pcbnew board file must have assigned \'Grid Origin\' or \'Aux Origin\' (Drill and Place offset)!" msg+="\nMoreover in FC StepUP preferences you must have\n\'PCB Settings\'->\'PCB Placement\'\nset to \'Grid Origin\' or \'Aux Origin\'" sayerr(msg) else: msg="""Save to an EXISTING KiCad pcb file to update your 3D model position!""" say_warning(msg) msg="Save to an EXISTING KiCad pcb file to update your 3D model position!" sayerr(msg) else: msg="""Operation aborted!""" sayerr(msg) say_info(msg) else: msg="""select only 3D model(s) moved to be updated/pushed to kicad board!
a Time Stamp is required!""" sayerr(msg) say_warning(msg) ### def getModelsData(mypcb): """ mypcb = KicadPCB.load(file_pcb) """ ## NB use always float() to guarantee number not string!!! import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser warn="" PCB_Models = [] Edge_Cuts_lvl=44 Top_lvl=0 conv_offs=25.4 if hasattr(mypcb, 'host'): print(mypcb.host) if hasattr(mypcb, 'version'): version = float(mypcb.version) if version <= 3: QtGui.QApplication.restoreOverrideCursor() reply = QtGui.QMessageBox.information(None,"Error ...","... KICAD pcb version "+ str(version)+" not supported \r\n"+"\r\nplease open and save your board with the latest kicad version") stop if version>=4: Edge_Cuts_lvl=44 Top_lvl=0 conv_offs=1.0 if version >= 20171114: conv_offs=25.4 for lynbr in mypcb.layers: #getting layers name if float(lynbr) == Top_lvl: LvlTopName=(mypcb.layers['{0}'.format(str(lynbr))][0]) if float(lynbr) == Edge_Cuts_lvl: LvlEdgeName=(mypcb.layers['{0}'.format(str(lynbr))][0]) for m in mypcb.module: #parsing modules #check top/bottom for placing 3D models #print(m.tstamp);print(m.fp_text[0][1]) #stop if len(m.at)==2: m_angle=0 else: m_angle=m.at[2] m_at=[m.at[0],-m.at[1]] #y reversed virtual=0 if hasattr(m, 'attr'): if 'virtual' in m.attr: #say('virtual module') virtual=1 else: virtual=0 m_x = float(m.at[0]) m_y = float(m.at[1]) * (-1) m_rot = float(m_angle) #sayw(m.layer);sayerr(LvlTopName) if m.layer == LvlTopName: # top side = "Top" #sayw('top ' + m.layer) else: side = "Bottom" m_rot *= -1 ##bottom 3d model rotation #sayw('bot ' + m.layer) n_md=1 for md in m.model: #say (md[0]) #model name #say(md.at.xyz) #say(md.scale.xyz) #say(md.rotate.xyz) error_scale_module=False #say('scale ');sayw(scale_vrml)#; #error_scale_module=False xsc_vrml_val=md.scale.xyz[0] ysc_vrml_val=md.scale.xyz[1] zsc_vrml_val=md.scale.xyz[2] # if scale_vrml!='1 1 1': if float(xsc_vrml_val)!=1 or float(ysc_vrml_val)!=1 or float(zsc_vrml_val)!=1: if "box_mcad" not in md[0] and "cylV_mcad" not in md[0] and "cylH_mcad" not in md[0]: sayw('wrong scale!!! set scale to (1 1 1)') error_scale_module=True #model_list.append(mdl_name[0]) #model=model_list[j]+'.wrl' #if py2: if sys.version_info[0] == 2: #py2 model=md[0].decode("utf-8") #stop else: #py3 model=md[0] # py3 .decode("utf-8") #print (model, ' MODEL', type(model)) #maui test py3 if (virtual==1 and addVirtual==0): model_name='no3Dmodel' side='noLayer' if model: sayw("virtual model "+model+" skipped") #virtual found warning else: if model: model_name=model #sayw(model_name) warn="" if "box_mcad" not in model_name and "cylV_mcad" not in model_name and "cylH_mcad" not in model_name: if error_scale_module: sayw('wrong scale!!! for '+model_name+' Set scale to (1 1 1)') msg="""Error in '.kicad_pcb' model footprint
""" msg+="
reset values of
"+model_name+"
to:
" msg+="(scale (xyz 1 1 1))
" #warn+=("reset values of scale to (xyz 1 1 1)") warn=("reset values of scale to (xyz 1 1 1)") ##reply = QtGui.QMessageBox.information(None,"info", msg) #stop #model_name=model_name[1:] #say(model_name) #sayw("here") else: model_name='no3Dmodel' #to do how to manage no3Dmodel side='noLayer' sayerr('no3Dmodel') mdl_name=model_name # re.findall(r'(.+?)\.wrl',params) #if virtual == 1: # sayerr("virtual model(s)");sayw(mdl_name) # sayw(mdl_name) # sayerr(params) if len(mdl_name) > 0: # model_name, rot_comb, warn, pos_vrml, rotz_vrml, scale_vrml = get3DParams(mdl_name,params, rot, virtual) #sayerr(md.at.xyz) if conv_offs != 1: #pcb version >= 20171114 (offset wrl in mm) if hasattr(md,'at'): ofs=[md.at.xyz[0]/conv_offs,md.at.xyz[1]/conv_offs,md.at.xyz[2]/conv_offs] if hasattr(md,'offset'): ofs=[md.offset.xyz[0]/conv_offs,md.offset.xyz[1]/conv_offs,md.offset.xyz[2]/conv_offs] else: ofs=md.at.xyz line = [] line.append(model_name) line.append(m_x) line.append(m_y) line.append(m_rot-md.rotate.xyz[2]) line.append(side) line.append(warn) line.append(ofs) #(md.at.xyz) #pos_vrml) line.append(md.rotate.xyz) #rotz_vrml) #sayerr(rotz_vrml) line.append(md.scale.xyz) #scale_vrml) line.append(virtual) if hasattr(m,'tstamp'): line.append(m.tstamp) # fp tstamp else: sayw('missing \'TimeStamp\'') line.append('null') try: line.append(m.fp_text[0][1]) #fp reference except: line.append(m.property[0][1]) #fp reference kv8 line.append(n_md) #number of models in module PCB_Models.append(line) n_md+=1 return PCB_Models ### def PullMoved(): global last_3d_path, start_time global last_fp_path, test_flag global configParser, configFilePath, last_pcb_path global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global pcb_path, use_AppPart, force_oldGroups, use_Links, use_LinkGroups global original_filename, aux_orig, grid_orig global off_x, off_y, maxRadius, use_pypro import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser ## to export to STEP an object and its links with a different placement and label ## two options must be set: 1) disable 'Reduce number of objects'; 2) disable 'Ignore instance names' ## NB the second one is not good for collaboration with different cads #say("export3DSTEP") if load_sketch==False: msg="""Board editing NOT supported on FC0.15!
please upgrade your FC release""" say_warning(msg) msg="Board editing NOT supported on FC0.15!" sayerr(msg) else: check_ok=False sel = FreeCADGui.Selection.getSelection() if len (sel) >= 1: for s in sel: if s.Label.rfind('_') < s.Label.rfind('['): ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] else: ts = s.Label[s.Label.rfind('_')+1:] if len (ts) == 8 or len (ts) == 12: #print(ts);stop check_ok=True #stop break #else: # msg="""select only 3D model(s) moved to be updated/pushed to kicad board!
a TimeSTamp is required!""" # sayerr(msg) # say_warning(msg) if check_ok: cfg_read_all() #pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") #pg.GetString("last_3d_path") if len(last_pcb_path) == 0: last_pcb_path=u'' #sayw(last_pcb_path) #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") testing=False #True if not testing: Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): fname, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Pull 3D model position(s) from pcbnew File...", make_unicode(last_pcb_path), "*.kicad_pcb") else: fname, Filter = PySide.QtGui.QFileDialog.getOpenFileName(None, "Pull 3D model position(s) from pcbnew File...", make_unicode(last_pcb_path), "*.kicad_pcb",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: fname='c:/Temp/demo/test-rot.kicad_pcb' if fname: if os.path.exists(fname): last_3d_path=os.path.dirname(fname) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_3d_path",make_string(last_3d_path)) start_time=current_milli_time() doc=FreeCAD.ActiveDocument #filePath=last_pcb_path #fpath=filePath+os.sep+doc.Label+'.kicad_pcb' #sayerr('to '+fpath) #print fname if fname is None: fpath=original_filename else: fpath=fname sayerr('loading from '+fpath) #stop if len(fpath) > 0: #new_edge_list=getBoardOutline() #say (new_edge_list) cfg_read_all() path, fname = os.path.split(fpath) name=os.path.splitext(fname)[0] ext=os.path.splitext(fname)[1] fpth = os.path.dirname(os.path.abspath(fpath)) #filePath = os.path.split(os.path.realpath(__file__))[0] say ('my file path '+fpth) # stop if fpth == "": fpth = "." last_pcb_path = fpth last_pcb_path = re.sub("\\\\", "/", last_pcb_path) ini_vars[10] = last_pcb_path #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) #sayerr(name+':'+ext) mypcb = KicadPCB.load(fpath) mymodels = getModelsData(mypcb) pcbThickness=float(mypcb.general.thickness) #print(mymodels) #stop #with codecs.open(fpath,'r', encoding='utf-8') as txtFile: # content = txtFile.readlines() # problems? #content.append(u" ") #txtFile.close() #data=u''.join(content) #oft=None #if aux_orig == 1: # oft=getAuxOrigin(data) #if grid_orig == 1: # oft=getGridOrigin(data) #print oft #gof=False #if oft is not None: # off_x=oft[0];off_y=-oft[1] # offset = oft # gof=True # pcb_pull=True #else: # pcb_pull=False oft=None if aux_orig == 1: if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'aux_axis_origin'): oft = mypcb.setup.aux_axis_origin #oft=getAuxOrigin(data) else: oft = [0.0,0.0] elif grid_orig == 1: if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'grid_origin'): oft=mypcb.setup.grid_origin else: oft = [0.0,0.0] else: oft = [0.0,0.0] #oft=getGridOrigin(data) #print ('oft ',oft) gof=False origin_warn=False if oft is not None: if oft == [0.0,0.0]: origin_warn=True off_x=oft[0];off_y=-oft[1] offset = oft gof=True pcb_pull=True else: pcb_pull=False #print ('ofx,ofy reviewed ',off_x,' ',off_y) testing=False #True if pcb_pull: for s in sel: #sayw(doc.Name) if 0: # use_pypro: if hasattr(s,"TimeStamp"): ts=s.TimeStamp content = push3D2pcb(s,content,ts,pcbThickness) else: msg="""select only 3D model(s) to be updated/pulled from kicad board!""" sayerr(msg) say_warning(msg) else: if s.Label.rfind('_') < s.Label.rfind('['): ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] else: ts = s.Label[s.Label.rfind('_')+1:] if len (ts) == 8 or len (ts) == 12: #print (s.Label) #;stop nbrModel = None if s.Label.rfind('_') < s.Label.rfind('['): #ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] nbrModel = s.Label[s.Label.rfind('['):] #print(nbrModel) nMd = int(nbrModel.replace('[','').replace(']',''))-1 else: #ts = s.Label[s.Label.rfind('_')+1:] nMd = 0 #print('nbrModel = 0') #print('timestamp',ts,'numModel',nMd) content = pull3D2dsn(s,mymodels,ts,nMd,gof,pcbThickness) #else: # msg="""select only 3D model(s) to be updated/pulled from kicad board!
a TimeSTamp is required!""" # sayerr(msg) # say_warning(msg) say_time() msg="""3D model new position pulled from kicad board!

""" msg+="file loaded from
"+fpath+"


" msgr="3D model new position pulled from kicad board!\n" say(msgr) say_info(msg) if origin_warn: if aux_orig == 1: origin_msg='AuxOrigin' elif grid_orig == 1: origin_msg='GridOrigin' msg = origin_msg +' is set in FC Preferences but not set in KiCAD pcbnew file' sayw(msg) msg=""""""+origin_msg+""" is set in FreeCAD Preferences
but not set in KiCAD pcbnew file
""" msg+="""

Please assign """+origin_msg+""" to your KiCAD pcbnew board file""" msg+="""
for a better Mechanical integration""" say_warning(msg) else: msg="""To update 3D model Position(s) from an EXISTING KiCad pcb file
the KiCAD pcbnew board file must have assigned \'Grid Origin\' or
\'Aux Origin\' (Drill and Place offset)!""" msg+="""
Moreover in FC StepUP preferences you must have
\'PCB Settings\'->\'PCB Placement\'
set to \'Grid Origin\' or \'Aux Origin\'""" say_warning(msg) msg="To update 3D model Position(s) from an EXISTING KiCad pcb file\nthe KiCAD pcbnew board file must have assigned \'Grid Origin\' or \'Aux Origin\' (Drill and Place offset)!" msg+="\nMoreover in FC StepUP preferences you must have \n\'PCB Settings\'->\'PCB Placement\'\nset to \'Grid Origin\' or \'Aux Origin\'" sayerr(msg) else: msg="""Load from an EXISTING KiCad pcb file to update your 3D model position!""" say_warning(msg) msg="Load from an EXISTING KiCad pcb file to update your 3D model position!" sayerr(msg) else: msg="""Operation aborted!""" sayerr(msg) say_info(msg) else: msg="""select only 3D model(s) to be updated/pulled from kicad board!
a Time Stamp is required!""" sayerr(msg) say_warning(msg) ## def PushFootprint(): #def onExport3DStep(self): global last_3d_path, start_time, load_sketch, last_pcb_path #say("export3DSTEP") if load_sketch==False: msg="""Edge editing NOT supported on FC0.15!
please upgrade your FC release""" say_warning(msg) msg="Edge editing NOT supported on FC0.15!" sayerr(msg) #if 0: #if FreeCAD.ActiveDocument is None: # FreeCAD.newDocument("PCB_Sketch") # PCB_Sketch= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','PCB_Sketch') # offset=[0.0,0.0] #offset=[148.5,98.5] # FreeCAD.activeDocument().PCB_Sketch.Placement = FreeCAD.Placement(FreeCAD.Vector(offset[0],offset[1]),FreeCAD.Rotation(0.000000,0.000000,0.000000,1.000000)) # FreeCAD.getDocument('PCB_Sketch').recompute() # FreeCADGui.SendMsgToActiveView("ViewFit") else: sel = FreeCADGui.Selection.getSelection() if len (sel) >= 1: #sayw(doc.Name) to_discretize=False;sk_to_discr=[];sk_temp=[];sk_to_convert=[];sk_to_reselect=[] fp_label=u'' #annular=0.125 if "Sketch" in sel[0].TypeId or "Group" in sel[0].TypeId: if "Group" in sel[0].TypeId: #print(FreeCAD.ActiveDocument.getObject(sel[0].Name).OutList,'g.OutList') #for o in FreeCAD.ActiveDocument.Objects: #print((sel[0].Name)) fp_label=sel[0].Label.replace(' ','_') for o in FreeCAD.ActiveDocument.getObject(sel[0].Name).OutList: #if sel[0] in o.InList: #print(o.Label) if 'PTH_Drills' in o.Label: centers=[];rads=[] for idx,g in enumerate(o.Geometry): #if 'ArcOfCircle' in str(g) and not isConstruction(g): #o.getConstruction(idx): #g.Construction: if 'Circle' in str(g) and isConstruction(g) == False: if not (g.Center in centers and g.Radius in rads): centers.append(g.Center);rads.append(g.Radius) #print(len(centers), centers) if 'NPTH_Drills' not in o.Label: if '_padNbr=' in o.Label: skLabel = 'Sketch_Pads_TH_SMD'+o.Label[o.Label.index('_padNbr='):]+'_tmp' elif '_padNum=' in o.Label: skLabel = 'Sketch_Pads_TH_SMD'+o.Label[o.Label.index('_padNum='):]+'_tmp' else: skLabel = 'Sketch_Pads_TH_SMD_tmp' else: skLabel = 'Sketch_Pads_NPTH_tmp' FreeCAD.ActiveDocument.addObject('Sketcher::SketchObject', skLabel) skd_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label = skLabel #workaround to keep '=' in Label sk_temp.append(FreeCAD.ActiveDocument.ActiveObject) #FreeCAD.ActiveDocument.getObject(skd_name).Placement = FreeCAD.Placement(FreeCAD.Vector(0.000000, 0.000000, 0.000000), FreeCAD.Rotation(0.000000, 0.000000, 0.000000, 1.000000)) #FreeCAD.ActiveDocument.getObject(skd_name).MapMode = "Deactivated" #print(centers) for i,c in enumerate(centers): FreeCAD.ActiveDocument.getObject(skd_name).addGeometry(Part.Circle(FreeCAD.Vector(c[0], c[1]), FreeCAD.Vector(0, 0, 1), rads[i])) if 'Pads_NPTH' not in FreeCAD.ActiveDocument.getObject(skd_name).Label: FreeCAD.ActiveDocument.getObject(skd_name).addGeometry(Part.Circle(FreeCAD.Vector(c[0], c[1]), FreeCAD.Vector(0, 0, 1), rads[i]*1.4)) # annular = 40% of radius FreeCAD.ActiveDocument.recompute() FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(skd_name)) sk_to_convert.append(FreeCAD.ActiveDocument.getObject(skd_name)) sk_to_reselect.append(o) if 'F_Silks' in o.Label or 'F_Fab' in o.Label or 'F_CrtYd' in o.Label \ or 'Dwg' in o.Label or 'Cmts' in o.Label \ or 'Pads_TH' in o.Label or 'Pads_NPTH' in o.Label or 'Edge_Cuts' in o.Label\ or 'Pads_Round_Rect' in o.Label or 'FZ_' in o.Label\ or 'Pads_Geom' in o.Label: #or 'Pads_Round_Rect' in o.Label or 'Pads_Poly' in o.Label or 'NetTie_Poly' in o.Label or 'FZ_' in o.Label\ #print('adding selection ',o.Label) FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(o.Name)) sk_to_convert.append(o) #print(o.Label,'sk added') if hasattr(o,"LabelText"): sayerr(o.LabelText) if 'Ref' in o.Label or 'Val' in o.Label: FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.getObject(o.Name)) sk_to_convert.append(o) ## checking Pads_Poly for ArcOfCircle to be discretized to_discretize=False if 'NetTie_Poly' in o.Label: if hasattr(o,"Geometry"): for g in o.Geometry: if 'ArcOfCircle' in str(g) and not isConstruction(g): FreeCAD.Console.PrintWarning('need to discretize Arcs\n') to_discretize=True if to_discretize: sk_to_discr.append(o) FreeCADGui.Selection.removeSelection(o) else: #print(o.Label,'sk added') sk_to_convert.append(o) to_discretize=False if 'Pads_Poly' in o.Label: if hasattr(o,"Geometry"): for g in o.Geometry: if 'ArcOfCircle' in str(g) and not isConstruction(g): FreeCAD.Console.PrintWarning('need to discretize Arcs\n') to_discretize=True if to_discretize: sk_to_discr.append(o) FreeCADGui.Selection.removeSelection(o) else: #print(o.Label,'sk added') sk_to_convert.append(o) else: for o in sel: to_discretize=False if 'PTH_Drills' in o.Label: #o= sel[0] centers=[];rads=[] for idx,g in enumerate(o.Geometry): if 'Circle' in str(g) and not isConstruction(g): if not (g.Center in centers and g.Radius in rads): centers.append(g.Center);rads.append(g.Radius) #print(len(centers), centers) if 'NPTH_Drills' not in o.Label: if '_padNbr=' in o.Label: skLabel = 'Sketch_Pads_TH_SMD'+o.Label[o.Label.index('_padNbr='):]+'_tmp' elif '_padNum=' in o.Label: skLabel = 'Sketch_Pads_TH_SMD'+o.Label[o.Label.index('_padNum='):]+'_tmp' else: skLabel = 'Sketch_Pads_TH_SMD_tmp' else: skLabel = 'Sketch_Pads_NPTH_tmp' FreeCAD.ActiveDocument.addObject('Sketcher::SketchObject', skLabel) skd_name=FreeCAD.ActiveDocument.ActiveObject.Name FreeCAD.ActiveDocument.ActiveObject.Label = skLabel #workaround to keep '=' in Label sk_temp.append(FreeCAD.ActiveDocument.ActiveObject) sk_to_convert.append(FreeCAD.ActiveDocument.ActiveObject) FreeCADGui.Selection.removeSelection(o) sk_to_reselect.append(o) #FreeCAD.ActiveDocument.getObject(skd_name).Placement = FreeCAD.Placement(FreeCAD.Vector(0.000000, 0.000000, 0.000000), FreeCAD.Rotation(0.000000, 0.000000, 0.000000, 1.000000)) #FreeCAD.ActiveDocument.getObject(skd_name).MapMode = "Deactivated" for i,c in enumerate(centers): FreeCAD.ActiveDocument.getObject(skd_name).addGeometry(Part.Circle(FreeCAD.Vector(c[0], c[1]), FreeCAD.Vector(0, 0, 1), rads[i])) if 'NPTH_Drills' not in o.Label: FreeCAD.ActiveDocument.getObject(skd_name).addGeometry(Part.Circle(FreeCAD.Vector(c[0], c[1]), FreeCAD.Vector(0, 0, 1), rads[i]*1.4)) # annular = 40% of radius # +annular)) FreeCAD.ActiveDocument.recompute() elif 'NetTie_Poly' in o.Label: for g in sel[0].Geometry: if 'ArcOfCircle' in str(g): FreeCAD.Console.PrintWarning('need to discretize Arcs\n') to_discretize=True if to_discretize: sk_to_discr.append(o) FreeCADGui.Selection.removeSelection(o) else: #print(o.Label,'sk added') sk_to_convert.append(o) elif 'Pads_Poly' in o.Label: to_discretize=False for g in o.Geometry: if 'ArcOfCircle' in str(g): FreeCAD.Console.PrintWarning('need to discretize Arcs\n') to_discretize=True if to_discretize: sk_to_discr.append(o) FreeCADGui.Selection.removeSelection(o) else: print(o.Label,'sk added') sk_to_convert.append(o) for sk in sk_to_discr: ws=sk.Shape.copy() #Part.show(ws) wn=[] q_deflection = 0.005 #0.02 ##0.005 wnc=[] for e in ws.Edges: if hasattr(e.Curve,'Radius'): if not e.Closed: # Arc and not Circle wn.append(Part.makePolygon(e.discretize(QuasiDeflection=q_deflection))) sayw('added discretized arc') else: #wn.append(Part.Wire(e)) wnc.append(Part.Wire(e)) sayw('added circle pad') else: wn.append(Part.Wire(e)) #sk_d=Draft.makeSketch(wn) edgs=[] for s in wn: for e in s.Edges: edgs.append(e) # wns = Part.Wire(Part.__sortEdges__(edgs)) #Part.show(wnc[0]) #print (wns);print(wnc[0]) if len(wnc)>0: wns = Part.Wire(Part.__sortEdges__(edgs)) sk_d=Draft.makeSketch([wns,wnc[0]], autoconstraints=True) else: sk_d=Draft.makeSketch(edgs, autoconstraints=True) Connect = sk_d #stop # for e in ws.Edges: # if hasattr(e.Curve,'Radius'): # if not e.Closed: # Arc and not Circle # #print(e.discretize(QuasiDeflection=q_deflection)) # sh=Part.makePolygon(e.discretize(QuasiDeflection=q_deflection)) # for ed in sh.Edges: # wn.append(ed) # #Part.show(sh) # sayw('added discretized polygon') # else: # #wn.append(Part.Wire(e)) # wnc.append(Part.Wire(e)) # sayw('added circle pad') # else: # wn.append(Part.Wire(e)) # #sk_d=Draft.makeSketch(wn) # edgs=[] # for s in wn: # for e in s.Edges: # edgs.append(e) # #print (e,e.TypeId) # #wns = Part.Wire(Part.__sortEdges__(edgs)) #lines = [] #points = [] #p_coords = [] #for wire in wn: # #points = [] # for vert in wire.Vertexes: # points.append(vert.Point) # p_coords.append((vert.Point.x,vert.Point.y)) #print(points) #print(p_coords) # p=p_coords # import functools # import math # center = functools.reduce(lambda a, b: (a[0] + b[0], a[1] + b[1]), p, (0, 0)) # center = (center[0] / len(p), (center[1] / len(p))) # p.sort(key = lambda a: math.atan2(a[1] - center[1], a[0] - center[0])) # contour=p # #print(p) # #print(sort_to_form_plist(p_coords)) # #stop # #import numpy as np # #vstack_ = np.vstack(p_coords) # ##print(vstack_) # ##contours = np.vstack(p_coords).squeeze() # #contours = np.vstack(points).squeeze() # ##contours= [ np.array(contours) ] # #print(contours) # v_contour=[] # #for p in contours: # # print(Base.Vector(p[0],p[1],p[2])) # # v_contour.append(Base.Vector(p[0],p[1],p[2])) # for p in contour: # print(Base.Vector(p[0],p[1],0.0)) # v_contour.append(Base.Vector(p[0],p[1],0.0)) # sh1 = Part.makePolygon(v_contour) # Part.show(sh1) # stop ## if len (wnc)>0: ## edgs.append(wnc[0].Edges[0]) ## sk_d=Draft.makeSketch(edgs, autoconstraints=True) ## FreeCAD.ActiveDocument.recompute() ### creating an edge ordered sketch del_sk_d = True if 0: try: ### Begin command Part_CompJoinFeatures say('importing BOPTools') import PartGui # from PartGui import BOPTools import BOPTools import BOPTools.JoinFeatures say('trying makeConnect') j = BOPTools.JoinFeatures.makeConnect(name='Connect') j.Objects = [sk_d] j.Proxy.execute(j) j.purgeTouched() for obj in j.ViewObject.Proxy.claimChildren(): obj.ViewObject.hide() ### End command Part_CompJoinFeatures say('makeConnect done') Connect = FreeCAD.ActiveDocument.ActiveObject except: sayw('failed makeConnect') FreeCAD.ActiveDocument.removeObject(FreeCAD.ActiveDocument.ActiveObject.Name) Connect = sk_d del_sk_d = False sv0 = Draft.makeShape2DView(FreeCAD.ActiveDocument.getObject(Connect.Name), FreeCAD.Vector(-0.0, -0.0, 1.0)) FreeCAD.ActiveDocument.recompute() FreeCAD.ActiveDocument.removeObject(Connect.Name) if 0: #del_sk_d: FreeCAD.ActiveDocument.removeObject(sk_d.Name) #FreeCADGui.Selection.clearSelection() #FreeCADGui.Selection.addSelection(FreeCAD.ActiveDocument.Name,sv0.Name) sk_d = Draft.makeSketch(FreeCAD.ActiveDocument.getObject(sv0.Name), autoconstraints=True) FreeCAD.ActiveDocument.removeObject(sv0.Name) FreeCAD.ActiveDocument.recompute() #stop #ws=sk_d.Shape.copy() #sk_do = Draft.makeSketch(ws) #Part.makePolygon(ws.Edges) #FreeCAD.ActiveDocument.removeObject(sk_d.Name) sk_d.Label=sk.Label+'_' FreeCADGui.Selection.addSelection(sk_d) sk_to_convert.append(sk_d) sk_temp.append(sk_d) #stop FreeCAD.ActiveDocument.recompute() #stop #if "Group" in sel[0].TypeId: # for o in FreeCAD.ActiveDocument.Objects: # FreeCADGui.Selection.addSelection(o) cfg_read_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") last_fp_path = pg.GetString("last_fp_path") if len(last_fp_path) == 0: last_fp_path=last_pcb_path #sayw(last_pcb_path) #getSaveFileName(self,"saveFlle","Result.txt",filter ="txt (*.txt *.)") testing=False #True for s in sk_to_convert: FreeCADGui.Selection.addSelection(s) #print(s.Label,'added') for s in sk_to_discr: FreeCADGui.Selection.removeSelection(s) #stop if not testing: Filter="" prefs_ = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUpGui") if not(prefs_.GetBool('not_native_dlg')): name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push Footprint to KiCad module ...", make_unicode(last_fp_path), "*.kicad_mod") else: name, Filter = PySide.QtGui.QFileDialog.getSaveFileName(None, "Push Footprint to KiCad module ...", make_unicode(last_fp_path), "*.kicad_mod",options=QtWidgets.QFileDialog.DontUseNativeDialog) else: if os.path.isdir("d:/Temp/"): name='d:/Temp/ex2.kicad_mod' elif os.path.isdir("c:/Temp/"): name='c:/Temp/ex2.kicad_mod' #say(name) # stop if name: #if os.path.exists(name): last_fp_path=os.path.dirname(name) pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_fp_path", make_string(last_fp_path)) # py3 .decode("utf-8") start_time=current_milli_time() if not name.endswith('.kicad_mod'): name=name+u'.kicad_mod' export_footprint(name,fp_label) for s in sk_to_discr: FreeCADGui.Selection.addSelection(s) for s in sk_to_reselect: FreeCADGui.Selection.addSelection(s) #stop if not testing: for s in sk_temp: FreeCAD.ActiveDocument.removeObject(s.Name) #else: # msg="""Save to an EXISTING KiCad pcb file to update your Edge!""" # say_warning(msg) # msg="Save to an EXISTING KiCad pcb file to update your Edge!" # sayerr(msg) else: msg="""Select Group or Sketch/Text elements to be converted to KiCad Footprint!""" sayerr(msg) say_warning(msg) else: msg="""Select Group or Sketch/Text elements to be converted to KiCad Footprint!""" sayerr(msg) say_warning(msg) ### def simplify_sketch_old(): ''' simplifying & sanitizing sketches ''' global maxRadius, edge_tolerance doc = FreeCAD.ActiveDocument sel = FreeCADGui.Selection.getSelection() if len(sel)==1: if 'Sketcher' in sel[0].TypeId: sanitizeSketch(sel[0].Name) new_edge_list, not_supported, to_discretize, construction_geom = getBoardOutline() ## support for arcs, lines and bsplines in F_Silks sel = FreeCADGui.Selection.getSelection() sk_name=None sk_name=sel[0].Name sk_label=sel[0].Label if len(to_discretize)>0 and sk_name is not None: FreeCADGui.ActiveDocument.getObject(sk_name).Visibility=False # hidden Sketch #sel = FreeCADGui.Selection.getSelection() #for s in sel: # if 'F_Silks' in s.Label: # sk_name=s.Name #if len (sel)==1: #sk_name=sel[0].Name t_name=cpy_sketch(sk_name) ###t_sk=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.getObject(sk_name)) elist, to_dis=check_geom(t_name) #Draft.clone(FreeCAD.ActiveDocument.getObject(sk_name),copy=True) #clone_name=App.ActiveDocument.ActiveObject.Name geoBasic=[] geoBasic=split_basic_geom(t_name, to_dis) #print geoBasic #remove_basic_geom(t_name, to_dis) ## remove_basic_geom(t_name, to_dis) ##remove_basic_geom(t_sk.Name, to_discretize) ##elist, to_dis=check_geom(t_sk.Name) #print elist #stop obj_list_prev=[] for obj in doc.Objects: #print obj.TypeId if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject"): obj_list_prev.append(obj.Name) #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=True) #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=False) b=FreeCAD.ActiveDocument.getObject(t_name) shp1=b.Shape.copy() Part.show(shp1) FreeCAD.ActiveDocument.removeObject(t_name) FreeCAD.ActiveDocument.recompute() #stop obj_list_after=[] for obj in doc.Objects: if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject")\ or (obj.TypeId=="Part::Part2DObjectPython"): if obj.Name not in obj_list_prev: obj_list_after.append(obj.Name) #print obj_list_after #, obj_list_prev sk_to_conv=[] for obj in doc.Objects: if obj.Name in obj_list_after: if (obj.TypeId=="Part::Part2DObjectPython"): FreeCAD.ActiveDocument.removeObject(obj.Name) FreeCAD.ActiveDocument.recompute() else: sk_to_conv.append(obj.Name) keep_sketch_converted=True #False for s in sk_to_conv: #sayerr(s) ## ns=Discretize(s) for g in geoBasic: FreeCAD.ActiveDocument.getObject(ns).addGeometry(g) offset1=[-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[0],-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[1]] elist, to_dis=check_geom(ns,offset1) for e in elist: #print e[(len(e)-1):][0] e[(len(e)-1)]=sk_label #print e[(len(e)-1):][0] #stop new_edge_list=new_edge_list+elist if not keep_sketch_converted: FreeCAD.ActiveDocument.removeObject(ns) else: FreeCAD.ActiveDocument.getObject(ns).Label=sel[0].Label+'_simplified' FreeCAD.ActiveDocument.recompute() ############# end discretizing else: sayw('nothing to simplify') # to do: evaluate sanitize check ####stop ### def simplify_sketch(): ''' simplifying & sanitizing sketches ''' global maxRadius, edge_tolerance, precision doc = FreeCAD.ActiveDocument sel = FreeCADGui.Selection.getSelection() if len(sel)==1: if 'Sketcher' in sel[0].TypeId: sanitizeSketch(sel[0].Name) # new_edge_list, not_supported, to_discretize, construction_geom = getBoardOutline() to_discretize = [] new_edge_list = [] if hasattr(sel[0],'GeometryFacadeList'): Gm = sel[0].GeometryFacadeList for g in Gm: if 'BSplineCurve object' in str(g.Geometry): to_discretize.append(g.Geometry) elif 'Ellipse' in str(g.Geometry) or 'Parabola' in str(g.Geometry) or 'Hyperbola' in str(g.Geometry): to_discretize.append(g.Geometry) else: if not isConstruction(g): #g.Construction: # adding only non construction geo new_edge_list.append(g.Geometry) else: Gm = sel[0].Geometry for g in Gm: if 'BSplineCurve object' in str(g): to_discretize.append(g) elif 'Ellipse' in str(g) or 'Parabola' in str(g) or 'Hyperbola' in str(g): to_discretize.append(g) else: if not isConstruction(g): #g.Construction: # adding only non construction geo new_edge_list.append(g) #for g in sel[0].Geometry: ## support for arcs, lines and bsplines in F_Silks sel = FreeCADGui.Selection.getSelection() sk_name=None sk_name=sel[0].Name sk_label=sel[0].Label if len(to_discretize)>0 and sk_name is not None: FreeCADGui.ActiveDocument.getObject(sk_name).Visibility=False # hidden Sketch #sel = FreeCADGui.Selection.getSelection() #for s in sel: # if 'F_Silks' in s.Label: # sk_name=s.Name #if len (sel)==1: #sk_name=sel[0].Name for g in to_discretize: if 'Ellipse' in str(g) or 'BSpline' in str(g) or 'Hyperbol' in str(g) or 'Parabol' in str(g): bs = g.toBSpline() # (tolerance, maxSegments, maxDegree) gds = bs.toBiArcs(precision) for gd in gds: new_edge_list.append(gd) if len(new_edge_list) > 0: doc.addObject('Sketcher::SketchObject','sSketch') ssk = doc.ActiveObject ssk_name = doc.ActiveObject.Name ssk.Geometry = new_edge_list # for i,g in enumerate (new_edge_list): # if 'BSplineCurve object' in str(g): # ssk.exposeInternalGeometry(i) doc.recompute() ssk.Label = sk_label + u'_simplified' # t_name=cpy_sketch(sk_name) # ###t_sk=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.getObject(sk_name)) # elist, to_dis=check_geom(t_name) # #Draft.clone(FreeCAD.ActiveDocument.getObject(sk_name),copy=True) # #clone_name=App.ActiveDocument.ActiveObject.Name # geoBasic=[] # geoBasic=split_basic_geom(t_name, to_dis) # #remove_basic_geom(t_name, to_dis) # ## remove_basic_geom(t_name, to_dis) # ##remove_basic_geom(t_sk.Name, to_discretize) # ##elist, to_dis=check_geom(t_sk.Name) # #print elist # #stop # obj_list_prev=[] # for obj in doc.Objects: # #print obj.TypeId # if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject"): # obj_list_prev.append(obj.Name) # #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=True) # #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=False) # b=FreeCAD.ActiveDocument.getObject(t_name) # shp1=b.Shape.copy() # Part.show(shp1) # #stop # FreeCAD.ActiveDocument.removeObject(t_name) # FreeCAD.ActiveDocument.recompute() # #stop # obj_list_after=[] # for obj in doc.Objects: # if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject")\ # or (obj.TypeId=="Part::Part2DObjectPython"): # if obj.Name not in obj_list_prev: # obj_list_after.append(obj.Name) # #print obj_list_after #, obj_list_prev # sk_to_conv=[] # for obj in doc.Objects: # if obj.Name in obj_list_after: # if (obj.TypeId=="Part::Part2DObjectPython"): # FreeCAD.ActiveDocument.removeObject(obj.Name) # FreeCAD.ActiveDocument.recompute() # else: # sk_to_conv.append(obj.Name) # keep_sketch_converted=True #False # for s in sk_to_conv: # #sayerr(s) ## # ns=Discretize(s) # for g in geoBasic: # FreeCAD.ActiveDocument.getObject(ns).addGeometry(g) # offset1=[-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[0],-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[1]] # elist, to_dis=check_geom(ns,offset1) # for e in elist: # #print e[(len(e)-1):][0] # e[(len(e)-1)]=sk_label # #print e[(len(e)-1):][0] # #stop # new_edge_list=new_edge_list+elist # if not keep_sketch_converted: # FreeCAD.ActiveDocument.removeObject(ns) # else: # FreeCAD.ActiveDocument.getObject(ns).Label=sel[0].Label+'_simplified' # FreeCAD.ActiveDocument.recompute() ############# end discretizing else: sayw('nothing to simplify') # to do: evaluate sanitize check ####stop ### def normalize_bsplines(): ''' simplifying & sanitizing sketches ''' global edge_tolerance, maxRadius, maxSegments, precision doc = FreeCAD.ActiveDocument docG = FreeCADGui.ActiveDocument sel = FreeCADGui.Selection.getSelection() if len(sel)==1: if 'Sketcher' in sel[0].TypeId: skGeo = sel[0].Geometry found_to_simplify = False kGeo = [] #print(skGeo) maxDegree = 3 #kicad max degree of splines for g in skGeo: #if 'BSplineCurve object' in str(g): # bs = g # # Set degree # #maxDegree = 3 #kicad max degree of splines # if bs.Degree < maxDegree: # bs.increaseDegree(maxDegree) # elif bs.Degree > maxDegree: # # degree too high. We need to approximate the curve # bs = bs.approximateBSpline(edge_tolerance,maxSegment,maxDegree) # (tolerance, maxSegments, maxDegree) # # Generate to a list of bezier curves # import kicadStepUptools; import importlib; importlib.reload(kicadStepUptools) if 'Ellipse' in str(g) or 'Parabola' in str(g) or 'Hyperbola' in str(g): # bs = g.approximateBSpline(edge_tolerance,maxSegments,maxDegree) # (tolerance, maxSegments, maxDegree) bs = g.toBSpline() # (tolerance, maxSegments, maxDegree) bs = bs.toBiArcs(precision) for b in bs: kGeo.append(b) found_to_simplify = True #print(bs) #stop else: #std geo like line, arcs, circles bs = g kGeo.append(bs) if found_to_simplify: doc.addObject('Sketcher::SketchObject','kBspSketch') kbsp = doc.ActiveObject kbsp_name = doc.ActiveObject.Name kbsp.Geometry = kGeo for i,g in enumerate (kGeo): if 'BSplineCurve object' in str(g): kbsp.exposeInternalGeometry(i) doc.recompute() docG.getObject(sel[0].Name).Visibility = False # bezier_list = bs.toBezier() # # Check the result # for bc in bezier_list: # print("%s (degree : %d / nb poles : %d)"%(bc, bc.Degree, bc.NbPoles)) # print(bc, bc.Degree, bc.NbPoles) # Part.show(bc.toShape()) # Draft.makeSketch(FreeCAD.ActiveDocument.ActiveObject,autoconstraints=True) # if not keep_sketch_converted: # FreeCAD.ActiveDocument.removeObject(ns) # else: # FreeCAD.ActiveDocument.getObject(ns).Label=sel[0].Label+'_simplified' # FreeCAD.ActiveDocument.recompute() ############# end discretizing else: sayw('nothing to simplify') else: sayw('nothing to simplify') # to do: evaluate sanitize check ####stop ### ### def export_footprint(fname=None,flabel=None): global last_fp_path, test_flag, start_time global configParser, configFilePath, start_time global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global pcb_path, use_AppPart, force_oldGroups, use_Links, use_LinkGroups global original_filename global off_x, off_y, maxRadius, pad_nbr, dqd sayw('exporting new footprint') doc=FreeCAD.ActiveDocument #print fname if fname is None: sayerr('missing fp file name') stop # fpath=original_filename else: fpath=fname sayerr('saving to '+fpath) #stop if len(fpath) > 0: #new_edge_list=getBoardOutline() #say (new_edge_list) cfg_read_all() path, fname = os.path.split(fpath) name=os.path.splitext(fname)[0] ext=os.path.splitext(fname)[1] fpth = os.path.dirname(os.path.abspath(fpath)) #filePath = os.path.split(os.path.realpath(__file__))[0] #say ('my file path '+fpath) # stop if fpth == "": fpth = "." last_fp_path = fpth last_fp_path = re.sub("\\\\", "/", last_fp_path) ini_vars[11] = last_fp_path #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_fp_path", make_string(last_fp_path)) #sayerr(name+':'+ext) # old_dqd = dqd # dqd=0.001 new_edge_list, not_supported, to_discretize, construction_geom = getBoardOutline() #dqd=old_dqd #print (new_edge_list, to_discretize) #stop ## support for arcs, lines and bsplines in F_Silks sel = FreeCADGui.Selection.getSelection() sk_name=None NetTie_present = False fp_name='fc_footprint' if flabel == '' or flabel is None: fp_name = FreeCAD.ActiveDocument.Name else: fp_name = flabel #print(fp_name, 'fp_name1') for s in sel: if 'F_Silks' in s.Label: sk_name=s.Name sk_label=s.Label if 'NetTie' in s.Label: NetTie_present = True if len(to_discretize)>0 and sk_name is not None: #sel = FreeCADGui.Selection.getSelection() #for s in sel: # if 'F_Silks' in s.Label: # sk_name=s.Name #if len (sel)==1: #sk_name=sel[0].Name t_name=cpy_sketch(sk_name) ###t_sk=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.getObject(sk_name)) elist, to_dis=check_geom(t_name) #Draft.clone(FreeCAD.ActiveDocument.getObject(sk_name),copy=True) #clone_name=App.ActiveDocument.ActiveObject.Name remove_basic_geom(t_name, to_dis) ##remove_basic_geom(t_sk.Name, to_discretize) ##elist, to_dis=check_geom(t_sk.Name) #print elist #stop obj_list_prev=[] for obj in doc.Objects: #print obj.TypeId if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject"): obj_list_prev.append(obj.Name) #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=True) #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=False) b=FreeCAD.ActiveDocument.getObject(t_name) shp1=b.Shape.copy() Part.show(shp1) FreeCAD.ActiveDocument.removeObject(t_name) FreeCAD.ActiveDocument.recompute() #stop obj_list_after=[] for obj in doc.Objects: if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject")\ or (obj.TypeId=="Part::Part2DObjectPython"): if obj.Name not in obj_list_prev: obj_list_after.append(obj.Name) #print obj_list_after #, obj_list_prev sk_to_conv=[] for obj in doc.Objects: if obj.Name in obj_list_after: if (obj.TypeId=="Part::Part2DObjectPython"): FreeCAD.ActiveDocument.removeObject(obj.Name) FreeCAD.ActiveDocument.recompute() elif (obj.TypeId=="Sketcher::SketchObject"): sk_to_conv.append(obj.Name) keep_sketch_converted=False #False for s in sk_to_conv: #sayerr(s) ## ns=Discretize(s) offset1=[-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[0],-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[1]] elist, to_dis=check_geom(ns,offset1) for e in elist: #print e[(len(e)-1):][0] e[(len(e)-1)]=sk_label #print e[(len(e)-1):][0] #stop new_edge_list=new_edge_list+elist if not keep_sketch_converted: FreeCAD.ActiveDocument.removeObject(ns) FreeCAD.ActiveDocument.recompute() ############# end discretizing new_border=u'' #print (new_edge_list) #stop ## maxRadius # 4000 = 4m max length for KiCad #edge_nbr=0 sanitized_edge_list=[] for border in new_edge_list: #print border # [0] if 'arc' in border[0]: #print border[0] if abs(float(border[3])) > maxRadius: #print 'too big radius= ',border[3] #print 'border len= ', len(border) #points=border [10].x p1x = float("{0:.6f}".format(border [10].x));p1y=float("{0:.6f}".format(border [10].y)) #print p1x, ' ',p1y p2x = float("{0:.6f}".format(border [11].x));p2y=float("{0:.6f}".format(border [11].y)) #print '1st point ', border [10],' 2nd point ', border [11] sanitized_edge_list.append(['line',p1x,p1y,p2x,p2y,border[13]]) else: sanitized_edge_list.append(border) else: sanitized_edge_list.append(border) #edge_nbr=edge_nbr+1 # print '------------------' #print (sanitized_edge_list) ####stop #for border in new_edge_list: reference=u"FC_"; value=u"Val_"; xr= 0.0; yr=1.0;xv= 0.0; yv=-1.0 #sel = FreeCADGui.Selection.getSelection() fsize='1.0 1.0'; fthick='0.15' ref_fsize='1.0 1.0'; ref_fthick='0.15' val_fsize='1.0 1.0'; val_fthick='0.15' for o in FreeCAD.ActiveDocument.Objects: #if sel[0].TypeId =='App::DocumentObjectGroup': #if o.TypeId =='App::DocumentObjectGroup': # fp_name=o.Label # print(fp_name, 'fp_name2') #else: # fp_name = FreeCAD.ActiveDocument.Name if o.TypeId =='App::Annotation': if 'Ref' in o.Label: reference=o.LabelText[0] xr=o.Position.x;yr=-o.Position.y fsize_list = o.Label.split('_') l = len (fsize_list) if l > 1: fs = (fsize_list[l-1].rstrip('mm')); ref_fsize=(fs+' '+fs); ref_fthick="{0:.3f}".format((float(fs)*0.15)) else: ref_fsize='1.0 1.0'; ref_fthick='0.15' elif 'Val' in o.Label: value=o.LabelText[0] xv=o.Position.x;yv=-o.Position.y fsize_list = o.Label.split('_') l = len (fsize_list) if l > 1: fs = (fsize_list[l-1].rstrip('mm')); val_fsize=(fs+' '+fs); val_fthick="{0:.3f}".format((float(fs)*0.15)) else: val_fsize='1.0 1.0'; val_fthick='0.15' offset=[0,0] drills=[];psmd=[];pth=[];npth=[] pply=[];ntply=[];prrect=[];pgeom=[];pgeomG=[] pads_TH_SMD=[];pads_NPTH=[] fzply=[];edge_thick=0. #edge_thick=0.15 #; lyr='F.SilkS' # lyr=border[len(border-1):] ## header=u"(module "+fp_name+" (layer F.Cu) (tedit 5A74E519)"+os.linesep #header=header+fp_type #header=header+" (descr \""+fp_name+" StepUp generated footprint\")"+os.linesep # print(fp_name, 'fp_name3') header=" (descr \""+fp_name.replace('-fp','')+" StepUp generated footprint\")"+os.linesep if NetTie_present: header=header+" (tags \"net tie\")"+os.linesep # header=header+" (attr virtual)" header=header+" (fp_text reference \""+reference+u"\" (at "+str(xr)+" "+str(yr)+") (layer F.SilkS)"+os.linesep header=header+" (effects (font (size "+ref_fsize+") (thickness "+ref_fthick+")))"+os.linesep header=header+" )"+os.linesep header=header+" (fp_text value \""+value+u"\" (at "+str(xv)+" "+str(yv)+") (layer F.SilkS)"+os.linesep header=header+" (effects (font (size "+val_fsize+") (thickness "+val_fthick+")))"+os.linesep #header=header+" (effects (font (size 1.0 1.0) (thickness 0.15)))"+os.linesep header=header+" )"+os.linesep header=header+" (fp_text user %R (at "+str(xr)+" "+str(yr)+") (layer F.Fab)"+os.linesep header=header+" (effects (font (size "+ref_fsize+") (thickness "+ref_fthick+")))"+os.linesep #header=header+" (effects (font (size 1 1) (thickness 0.15)))"+os.linesep header=header+" )"+os.linesep header=header+" (fp_text user \"EDIT PAD NUMBERS\" (at "+str(xv)+" "+str(yv-1)+") (layer Cmts.User)"+os.linesep header=header+" (effects (font (size 1 1) (thickness 0.2) italic))"+os.linesep header=header+" )" ## import kicadStepUptools; reload(kicadStepUptools) for border in sanitized_edge_list: #print (border) edge_thick=0.05 lyr=border[(len(border)-1):][0] lyr_splt = lyr.split('_') tk_d=0.1 #print(lyr) if 'CrtYd' in lyr: if len(lyr_splt)>=3: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d lyr=u'F.CrtYd' else: lyr='skip' elif 'Silks' in lyr: if len(lyr_splt)>=3: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d lyr=u'F.SilkS' else: lyr='skip' elif 'Fab' in lyr: if len(lyr_splt)>=3: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d lyr=u'F.Fab' else: lyr='skip' elif 'Dwgs' in lyr: if len(lyr_splt)>=2: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d lyr=u'Dwgs.User' else: lyr='skip' elif 'Cmts' in lyr: if len(lyr_splt)>=2: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d lyr=u'Cmts.User' else: lyr='skip' elif 'Cuts' in lyr: if len(lyr_splt)>=3: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d lyr=u'Edge.Cuts' else: lyr='skip' #elif 'Pads_SMD' in lyr: # edge_thick=0. # lyr=u'Pads_SMD' # psmd.append(border) #elif 'Pads_TH' in lyr and not 'SMD' in lyr: # edge_thick=0. # lyr=u'Pads_TH' # pth.append(border) #elif 'Drills' in lyr: # edge_thick=0. # lyr=u'Drills' # drills.append(border) elif 'NPTH' in lyr: edge_thick=0. lyr=u'NPTH' pads_NPTH.append(border) elif 'Pads_Poly' in lyr: edge_thick=0. if 0: #'B_Cu' in lyr: lyr=u'Pad_Poly_B_Cu' else: lyr=u'Pad_Poly' pply.append(border) elif 'NetTie_Poly' in lyr: edge_thick=0. if 0: #'B_Cu' in lyr: lyr=u'NetTie_Poly_B_Cu' else: lyr=u'NetTie_Poly' ntply.append(border) elif 'FZ_F_Mask' in lyr: edge_thick=0. lyr=u'FZ_Mask_Poly' # fzply.append(border) elif 'Pads_Round_Rect' in lyr: edge_thick=0. if 0: #'B_Cu' in lyr: lyr=u'Pads_Round_Rect_B_Cu' else: lyr=u'Pads_Round_Rect' prrect.append(border) elif 'Pads_TH' in lyr and 'SMD' in lyr: edge_thick=0. if 0: #'B_Cu' in lyr: lyr=u'PadsAll_B_Cu' else: lyr=u'PadsAll' pads_TH_SMD.append(border) elif 'Pads_Geom' in lyr: #edge_thick=float(lyr.split('_')[2]) if len(lyr_splt)>=3: tk=lyr.split('_')[len(lyr_splt)-1] if tk!='': edge_thick=float(tk) else: edge_thick=tk_d #print (lyr) sk = FreeCAD.ActiveDocument.getObjectsByLabel(lyr)[0] if hasattr(sk,'GeometryFacadeList'): Gm = sk.GeometryFacadeList for g in Gm: if isConstruction(g): # g.Construction: if 'Circle' in type(g.Geometry).__name__ and not 'ArcOfCircle' in type(g.Geometry).__name__: sk_ge=g.Geometry.toShape() #needed to fix some issue on sketch geometry building pgeomG.append([ 'circle', sk_ge.Edges[0].Curve.Radius, sk_ge.Edges[0].Curve.Center.x, sk_ge.Edges[0].Curve.Center.y, sk.Label ]) else: Gm = sk.Geometry for g in Gm: if isConstruction(g): #g.Construction: if 'Circle' in type(g).__name__ and not 'ArcOfCircle' in type(g).__name__: sk_ge=g.toShape() #needed to fix some issue on sketch geometry building pgeomG.append([ 'circle', sk_ge.Edges[0].Curve.Radius, sk_ge.Edges[0].Curve.Center.x, sk_ge.Edges[0].Curve.Center.y, sk.Label ]) #lyr=u'Pads_Geom' pgeom.append(border) #print (pgeom);print(pgeomG) #sayw(prrect); sayw(pply) #sayw(pth) #if (lyr != 'Pads_SMD' and lyr != 'Pads_TH' and lyr != 'Drills' and lyr != 'NPTH'\ if ('Pads_SMD' not in lyr and 'Pads_TH' not in lyr and 'Drills' not in lyr and 'NPTH' not in lyr \ and 'Pad_Poly' not in lyr and 'NetTie_Poly' not in lyr and 'Pads_Round_Rect' not in lyr and 'PadsAll' not in lyr)\ and 'Pads_Geom' not in lyr and 'skip' not in lyr and 'FZ_Mask_Poly' not in lyr: #print border, ' BORDER' # #if len (border)>0: new_border=new_border+os.linesep+createFp(border,offset, lyr, edge_thick) #sayw(createEdge(border)) #stop #pth_ordered=collect_pads(psmd) #pads_all) ## pads normalized with sequence of segments ## normalizing TH and SMD pads if len(pads_TH_SMD) >0: pth_ordered=collect_pads(pads_TH_SMD) #pads_all) ## pads normalized with sequence of segments ## search for drills (pads inside pads) drl_found=collect_drl(pth_ordered) #sayerr (pth_ordered) #sayw(drl_found) ## impiling pads pads_TH_SMD=[] for p in pth_ordered: for e in p: pads_TH_SMD.append (e) # print len(pads_TH_SMD) pth=[] pth=pads_TH_SMD ## impiling pads drills=[] for p in drl_found: for e in p: drills.append (e) # print len(drills) #print psmd #stop ## normalizing NPTH pads if len(pads_NPTH) >0: pth_ordered=collect_pads(pads_NPTH) #pads_all) ## pads normalized with sequence of segments ## search for drills (pads inside pads) drl_found=collect_drl(pth_ordered) #sayerr (pth_ordered) #sayw(drl_found) #stop ## impiling pads pads_NPTH=[] for p in pth_ordered: for e in p: pads_NPTH.append (e) # print len(pads_NPTH) npth=[] npth=pads_NPTH ## impiling pads #drills=[] for p in drl_found: for e in p: drills.append (e) # print len(drills) #print psmd #stop ## normalizing Round Rect pads if len(prrect) >0: pth_ordered=collect_pads(prrect) #pads_all) ## pads normalized with sequence of segments ## search for drills (pads inside pads) drl_found=collect_drl(pth_ordered) #sayerr (pth_ordered) #sayw(drl_found) #stop ## impiling pads prrect=[] for p in pth_ordered: for e in p: prrect.append (e) #print len(prrect) if len (prrect)>0: sayw('normalized Round Rect') #print prrect #npth=[] #npth=pads_NPTH ## impiling pads #drills=[] for p in drl_found: for e in p: drills.append (e) #print len(drills) #print psmd #stop ## normalizing Poly pads if len(pply) >0: #print(pply) pth_ordered=collect_pads(pply) #pads_all) ## pads normalized with sequence of segments #sayerr(pth_ordered);sayerr(len(pth_ordered)) ## impiling pads #drl_found=collect_drl(pth_ordered) pply=[] for p in pth_ordered: for e in p: pply.append (e) #print len(prrect) if len (pply)>0: sayw('normalized Poly') # ## impiling pads # for p in drl_found: # for e in p: # drills.append (e) #print prrect #npth=[] #npth=pads_NPTH #print len(drills) #print psmd #stop ## normalizing NetTie Poly if len(ntply) >0: #sayerr(ntply) pth_ordered=collect_pads(ntply) #pads_all) ## pads normalized with sequence of segments #sayerr(pth_ordered) ## impiling pads #drl_found=collect_drl(pth_ordered) ntply=[] for p in pth_ordered: for e in p: ntply.append (e) #print len(prrect) if len (ntply)>0: sayw('normalized NetTie Poly') ## normalizing Geom pads pGm=[] if len(pgeomG) >0: pth_ordered=collect_pads(pgeomG) #pads_all) ## pads normalized with sequence of segments #sayerr(pth_ordered) ## impiling pads drl_found=collect_drl(pth_ordered) print(drl_found) pGm=[] for p in pth_ordered: for e in p: pGm.append (e) #print len(prrect) if len (pGm)>0: sayw('normalized Geom') #print(pGm) # ## impiling pads # for p in drl_found: # for e in p: # drills.append (e) #print prrect #npth=[] #npth=pads_NPTH #print len(drills) #print psmd #stop ## writing content newcontent=u''+header #new_edge=new_border+os.linesep+')'+os.linesep #newcontent=newcontent+new_edge+u' ' if len (new_border)>0: newcontent=newcontent+new_border #+os.linesep #print newcontent #### ----------SMD------------------------------- #npad=u'' #mpad=[] #nline=1 #pad_nbr=1 #found_arc=False #for pad in psmd: # if pad[0]=='circle': # npad=npad+os.linesep+createFpPad(pad,offset,u'SMD') # elif pad[0]=='line' and not found_arc: # mpad.append(pad) # if nline>=4: # npad=npad+os.linesep+createFpPad(mpad,offset,u'SMD') # nline=0 # mpad=[] # nline=nline+1 # elif pad[0]=='arc' or (pad[0]=='line' and found_arc): # found_arc=True # mpad.append(pad) # if nline>=4: # npad=npad+os.linesep+createFpPad(mpad,offset,u'SMD') # nline=0 # mpad=[] # found_arc=False # nline=nline+1 #if len (npad)>0: # newcontent=newcontent+npad+os.linesep # sayw('created SMD pads') ### ----------Drills------------------------------- #print psmd mdrills=[] pad_nbr=1 nline=1 drill_pos=[] found_arc=False #sayerr(drills) for drill in drills: #sayerr(drill) if drill[0]=='circle': #ret=createFpPad(drill,offset,u'Drills') #sayw(ret) drill_pos.append(createFpPad(drill,offset,u'Drills')) # elif drill[0]=='line' and not found_arc: # mdrills.append(drill) # if nline>=4: # #ndrill=ndrill+os.linesep+createFpPad(mdrills,offset,u'Drills') # drill_pos.append(createFpPad(mdrill,offset,u'Drills')) # nline=0 # mdrills=[] # nline=nline+1 elif drill[0]=='arc' or (drill[0]=='line' and found_arc): found_arc=True mdrills.append(drill) #print('arc or line + '+str(nline)) if nline>=4: #ndrill=ndrill+os.linesep+createFpPad(mdrills,offset,u'Drills') drill_pos.append(createFpPad(mdrills,offset,u'Drills')) nline=0 mdrills=[] found_arc=False nline=nline+1 #sayw(drill_pos) ## drill_pos (cntX,cntY,sizeX,sizeY) fp_type=' (attr smd)'+os.linesep if len (drill_pos)>0: #newcontent=newcontent+os.linesep+')'+os.linesep+u' ' sayw ('collected drills centers and positions') fp_type='' #re.sub(r'^[^\n]*\n', '', s) newcontent=u"(module "+fp_name.replace('-fp','')+" (layer F.Cu) (tedit 61218795)"+os.linesep+newcontent else: newcontent=u"(module "+fp_name.replace('-fp','')+" (layer F.Cu) (tedit 61218795)"+os.linesep+fp_type+newcontent #header=header+fp_type ### ----------TH------------------------------- npad=u'' mpad=[] nline=1 pad_nbr=1 found_arc=False for pad in pth: if pad[0]=='circle': npad=npad+os.linesep+createFpPad(pad,offset,u'TH', drill_pos) elif pad[0]=='line' and not found_arc: mpad.append(pad) if nline>=4: npad=npad+os.linesep+createFpPad(mpad,offset,u'TH', drill_pos) nline=0 mpad=[] nline=nline+1 elif pad[0]=='arc' or (pad[0]=='line' and found_arc): found_arc=True mpad.append(pad) if nline>=4: npad=npad+os.linesep+createFpPad(mpad,offset,u'TH', drill_pos) nline=0 mpad=[] found_arc=False nline=nline+1 #print 'len pad '+str(len(npad)) #print newcontent if len (npad)>0: newcontent=newcontent+npad+os.linesep say('created TH pads') ### ----------NPTH------------------------------- npad=u'' mpad=[] nline=1 pad_nbr=1 found_arc=False for pad in npth: if pad[0]=='circle': npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) elif pad[0]=='line' and not found_arc: mpad.append(pad) if nline>=4: npad=npad+os.linesep+createFpPad(mpad,offset,u'NPTH', drill_pos) nline=0 mpad=[] nline=nline+1 elif pad[0]=='arc' or (pad[0]=='line' and found_arc): found_arc=True mpad.append(pad) if nline>=4: npad=npad+os.linesep+createFpPad(mpad,offset,u'NPTH', drill_pos) nline=0 mpad=[] found_arc=False nline=nline+1 #print 'len pad '+str(len(npad)) #print newcontent if len (npad)>0: newcontent=newcontent+npad+os.linesep say('created NPTH pads') ### ----------Round Rect------------------------------- npad=u'' mpad=[] nline=1 pad_nbr=1 found_arc=False #found_line=False for pad in prrect: #sayw('RRect type '+pad[0]) #sayerr(pad) #if pad[0]=='circle': # npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) #if pad[0]=='line' and not found_arc: if pad[0]=='arc' and not found_arc: mpad.append(pad) found_arc=True nline=nline+1 #elif pad[0]=='arc' or (pad[0]=='line' and found_arc): elif (nline<=8 and found_arc): mpad.append(pad) #print mpad #print nline if nline>=8: #print npad #print 'd pos ',drill_pos #print 'mpad';print mpad #print 'offset ',offset #stop npad=npad+os.linesep+createFpPad(mpad,offset,u'RoundRect', drill_pos) nline=0 mpad=[] found_arc=False nline=nline+1 #print npad # ### ----------Round Rect------------------------------- # npad=u'' # mpad=[] # nline=1 # pad_nbr=1 # found_arc=False # for pad in prrect: # #sayerr(pad) # #if pad[0]=='circle': # # npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) # if pad[0]=='line' and not found_arc: # mpad.append(pad) # nline=nline+1 # elif pad[0]=='arc' or (pad[0]=='line' and found_arc): # found_arc=True # mpad.append(pad) # if nline>=8: # #print npad # #print 'd pos ',drill_pos # #print 'mpad';print mpad # #print 'offset ',offset # #stop # npad=npad+os.linesep+createFpPad(mpad,offset,u'RoundRect', drill_pos) # nline=0 # mpad=[] # found_arc=False # nline=nline+1 # #print npad #print 'len pad '+str(len(npad)) #print newcontent if len (npad)>0: newcontent=newcontent+npad+os.linesep say('created Round Rect pads') ### ----------Poly reference Pad------------------------------- #print psmd polypad=[] polypad=pply pad_nbr=1 nline=1 polypad_pos=[] found_arc=False #sayerr(polypad) for circ_pad in polypad: #sayerr(drill) if circ_pad[0]=='circle': #ret=createFpPad(drill,offset,u'Drills') #sayw(circ_pad) polypad_pos.append(createFpPad(circ_pad,offset,u'Drills')) # elif drill[0]=='line' and not found_arc: # mdrills.append(drill) # if nline>=4: # #ndrill=ndrill+os.linesep+createFpPad(mdrills,offset,u'Drills') # drill_pos.append(createFpPad(mdrill,offset,u'Drills')) # nline=0 # mdrills=[] # nline=nline+1 #sayw(drill_pos) ## drill_pos (cntX,cntY,sizeX,sizeY) if len (polypad_pos)>0: #newcontent=newcontent+os.linesep+')'+os.linesep+u' ' sayw ('collected poly pads centers and positions') #print(polypad_pos, 'poly pad pos') #print(pply,'pl geo') ### ----------Poly------------------------------- #polypad_pos=[] ### TBC polypad inside poly sketch #sayerr(pply) #sayerr(polypad_pos) npad=u'' mpad=[] nline=1 pad_nbr=1 poly_closed=False for pad in pply: #sayerr(pad) #print('pad',pad) #if pad[0]=='circle': # npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) if pad[0]=='line': mpad.append(pad) if len(mpad)>1: if abs(mpad[0][1]-pad[3])0: newcontent=newcontent+npad+os.linesep say('created Poly pads') ### ----------NetTie Poly reference pad ------------------------------- #print psmd polypad=[] polypad=ntply pad_nbr=1 nline=1 polypad_pos=[] found_arc=False #sayerr(polypad) for circ_pad in polypad: #sayerr(drill) if circ_pad[0]=='circle': #ret=createFpPad(drill,offset,u'Drills') #sayw(circ_pad) polypad_pos.append(createFpPad(circ_pad,offset,u'Drills')) # elif drill[0]=='line' and not found_arc: # mdrills.append(drill) # if nline>=4: # #ndrill=ndrill+os.linesep+createFpPad(mdrills,offset,u'Drills') # drill_pos.append(createFpPad(mdrill,offset,u'Drills')) # nline=0 # mdrills=[] # nline=nline+1 #sayw(drill_pos) ## drill_pos (cntX,cntY,sizeX,sizeY) if len (polypad_pos)>0: #newcontent=newcontent+os.linesep+')'+os.linesep+u' ' sayw ('collected net tie poly pads centers and positions') #print(polypad_pos, 'poly pad pos') #print(ntply,'nt geo') ### ----------Poly------------------------------- #polypad_pos=[] ### TBC polypad inside poly sketch #sayerr(pply) #sayerr(polypad_pos) npad=u'' mpad=[] nline=1 pad_nbr=1 poly_closed=False for pad in ntply: #sayerr(pad) #if pad[0]=='circle': # npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) if pad[0]=='line': mpad.append(pad) if len(mpad)>1: if abs(mpad[0][1]-pad[3])0: newcontent=newcontent+npad+os.linesep say('created NetTie Poly pads') ### ----------FZ Poly------------------------------- #polypad_pos=[] ### TBC polypad inside poly sketch #sayerr(pply) #sayerr(polypad_pos) #sel = FreeCADGui.Selection.getSelection() npad=u'' for s in sel: poly_closed=False polypad_pos=None print(o.Label,'parsing sketch') pts="" if 'FZ_F_Mask' in s.Label: #print('s.Label',s.Label) ns=Discretize(s.Name,'dont_delete',0.001) #NB use don't delete and pass discretization value face = OSCD2Dg_edgestofaces(FreeCAD.ActiveDocument.getObject(ns).Shape.Edges,3 , edge_tolerance) FreeCAD.ActiveDocument.removeObject(ns) #FreeCAD.ActiveDocument.recompute() face.check() # reports errors face.fix(0,0,0) ws=face.Wires #print(face.Wires,len(face.Wires),'face') if 0: Part.show(face) f=FreeCAD.ActiveDocument.ActiveObject ws=f.Shape.Wires print(ws,len(ws),'face2') padLayer="F.Mask" j=0 for w in ws: j=j+1 # cls=Part.getSortedClusters(w.Edges) # print(cls) clusters = Part.sortEdges(w.Edges) #[0] #print(len(clusters)) for cluster in clusters: # cluster=clusters[0] # if 1: #print(len(cluster)) pts+=" (fp_poly"+os.linesep+" (pts"+os.linesep for i,e in enumerate(cluster): #w.Edges): #print('i',i) if i < len(cluster)-1: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+") (xy "+"{0:.3f}".format(e.Vertexes[1].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[1].Y))+")"+os.linesep else: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+")) (layer \""+padLayer+"\") (width 0))"+os.linesep # print(pts,'\npts',j) #print('j',j) if len (pts)>0: newcontent=newcontent+os.linesep+pts+os.linesep say('created FZ Poly pads') # npad=npad+os.linesep+pts # stop #stop if 0: # len(fzply)>0: #print(fzply,len(fzply)) #stop npad=npad+os.linesep+createFpPad(fzply,offset,u'FZ_Mask_Poly', polypad_pos) if 0: for pad in fzply: # print(fzply) #stop #sayerr(pad) #if pad[0]=='circle': # npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) ## wr.append(Part.makeLine((pad[1], pad[2],0.0),(pad[3], pad[4],0.0))) ## for w in wr: ## sortedEdges = Part.__sortEdges__(w.Edges) ## wire = Part.Wire(sortedEdges) ## if pad[0]=='line': mpad.append(pad) #print(pad) if len(mpad)>2: #print(mpad) if abs(mpad[0][1]-pad[3])0: newcontent=newcontent+npad+os.linesep say('created FZ Poly pads') ### ----------Geom reference Pad------------------------------- #print psmd pgeompad=[] pgeompad=pGm pad_nbr=1 nline=1 pgeompad_pos=[] found_arc=False #sayerr(polypad) for circ_pad in pgeompad: #sayerr(drill) if circ_pad[0]=='circle': #ret=createFpPad(drill,offset,u'Drills') #sayw(circ_pad) pgeompad_pos.append(createFpPad(circ_pad,offset,u'Drills')) # elif drill[0]=='line' and not found_arc: # mdrills.append(drill) # if nline>=4: # #ndrill=ndrill+os.linesep+createFpPad(mdrills,offset,u'Drills') # drill_pos.append(createFpPad(mdrill,offset,u'Drills')) # nline=0 # mdrills=[] # nline=nline+1 #sayw(drill_pos) ## drill_pos (cntX,cntY,sizeX,sizeY) if len (pgeompad_pos)>0: #newcontent=newcontent+os.linesep+')'+os.linesep+u' ' sayw ('collected geometry pads centers and positions') sayw(pgeompad_pos) ### ----------Primitive Geometry------------------------------- ## only Circle supported ATM #polypad_pos=[] ### TBC polypad inside poly sketch #sayerr(pply) #sayerr(polypad_pos) npad=u'' mpad=[] nline=1 pad_nbr=1 #say (pgeom) #stop i=0 for pad in pgeom: #sayerr(pad) #if pad[0]=='circle': # npad=npad+os.linesep+createFpPad(pad,offset,u'NPTH', drill_pos) if pad[0]=='line': sayw('line not suported') if pad[0]=='circle': #print npad #print 'mpad';print mpad mpad.append(pad) npad=npad+os.linesep+createFpPad(mpad,offset,u'Pads_Geom', [pgeompad_pos[i]]) nline=1 mpad=[] i+=1 #nline=nline+1 #print npad #print 'len pad '+str(len(npad)) #print newcontent if len (npad)>0: newcontent=newcontent+npad+os.linesep say('created Geom pads') ## adding 3D model preset newcontent+=os.linesep+u" (model \""+str(fpth)+os.sep+fp_name.rstrip('-fp')+u'.wrl\"'+os.linesep newcontent+=u" (at (xyz 0 0 0))"+os.linesep newcontent+=u" (scale (xyz 1 1 1))"+os.linesep newcontent+=u" (rotate (xyz 0 0 0))"+os.linesep newcontent+=u" )"+os.linesep ### ---------- wrtiting file -------------------- newcontent=newcontent+')'+os.linesep+u' ' #dqd=old_dqd with codecs.open(fpath,'w', encoding='utf-8') as ofile: ofile.write(newcontent) ofile.close() say_time() msg="""new Footprint pushed to kicad footprint!

""" msg+="file saved to
"+fpath+"


" msgr="new Footprint pushed to kicad footprint!\n" msgr+="file saved to "+fpath+"\n" lns=len (not_supported) #print lns if lns > 2: if lns < 103: # writing only some geometry not supported msg+="
found downgraded Geometry:
"+not_supported[:-2]+"!
" msgr+="\nfound downgraded Geometry: "+not_supported[:-2]+"!" else: nss=not_supported[:-2] nss=nss[:101]+'...
...' msg+="
found downgraded Geometry:
"+nss+"
" msgr+="\nfound downgraded Geometry: "+not_supported[:-2]+"!" say(msgr) say_info(msg) #if not edge_pcb_exists: # msg="close your FC Sketch
and reload the kicad_pcb file
" # say_warning(msg) ### def collect_drl(pads): pad_shps=[] #pad,center,shape if pads is not None: for p in pads: #sayw(p) #print p[0][0] ;print p[0][2] if p[0][0]=='circle': p_center=(p[0][2],p[0][3],0) p_radius=p[0][1] #print p_center wr=Part.Wire(Part.makeCircle(p_radius, Base.Vector(p_center))) ps=Part.Wire(wr) face = Part.Face(ps) Part.show(face) shpName=FreeCAD.ActiveDocument.ActiveObject.Name #say( FreeCAD.ActiveDocument.ActiveObject.Label) shape= FreeCAD.ActiveDocument.ActiveObject.Shape pad_shps.append([p,p_center,shpName]) elif p[0][0] =='arc' and 'Pads_Round_Rect' in p[0][len(p[0])-1]: sayw('round rect') #print p r1=p[0][1]; cx1=p[0][2]; cy1=p[0][3] #print 'r1 ', r1, ' c1 ', cx1,',',cy1 r2=p[2][1]; cx2=p[2][2]; cy2=p[2][3] #print 'r2 ', r2, ' c2 ', cx2,',',cy2 #stop if abs(cx1-cx2)>edge_tolerance: # horizontal sayerr('horizontal') #print p px=(p[0][10].x+p[2][10].x)/2; py=(p[0][10].y+p[2][10].y)/2; sx=2*r1+abs(p[1][1]-p[3][1]); sy=2*r1+abs(p[1][2]-p[1][4]) #print r1,' ',sx-2*r1 else: #vertical sayerr('vertical') #print p[0][10].x #print p[2][10].x px=(p[0][10].x+p[2][10].x)/2; py=(p[0][10].y+p[2][10].y)/2; sx=2*r1+abs(p[3][1]-p[3][3]); sy=2*r1+abs(p[1][2]-p[1][4]) p_center=(px,py,0) #Draft.makePoint(px,py, 0) wr=[] #print p #arc -> approximate shape with rectangle wr.append(Part.makeLine((px-sx/2, py-sy/2,0.0),(px-sx/2, py+sy/2,0.0))) wr.append(Part.makeLine((px-sx/2, py+sy/2,0.0),(px+sx/2, py+sy/2,0.0))) wr.append(Part.makeLine((px+sx/2, py+sy/2,0.0),(px+sx/2, py-sy/2,0.0))) wr.append(Part.makeLine((px+sx/2, py-sy/2,0.0),(px-sx/2, py-sy/2,0.0))) ps=Part.Wire(wr) face = Part.Face(ps) Part.show(face) #stop shpName=FreeCAD.ActiveDocument.ActiveObject.Name #say( FreeCAD.ActiveDocument.ActiveObject.Label) shape= FreeCAD.ActiveDocument.ActiveObject.Shape pad_shps.append([p,p_center,shpName]) elif p[0][0]=='line': #print p px=(p[0][1]+p[0][3])/2;py=(p[1][2]+p[1][4])/2; if abs(p[0][1]-p[0][3]) >0: sx=abs(p[0][1]-p[0][3]) px=(p[0][1]+p[0][3])/2 else: sx=abs(p[1][1]-p[1][3]) px=(p[1][1]+p[1][3])/2 if abs(p[1][2]-p[1][4]) >0: sy=abs(p[1][2]-p[1][4]) py=(p[1][2]+p[1][4])/2; else: sy=abs(p[0][2]-p[0][4]) py=(p[0][2]+p[0][4])/2; # Draft.makePoint(px,py, 0) #print p[0];print p[1] #stop p_center=(px,py,0) wr=[] for lines in p: wr.append(Part.makeLine((lines[1], lines[2],0.0),(lines[3], lines[4],0.0))) ps=Part.Wire(wr) face = Part.Face(ps) Part.show(face) #stop shpName=FreeCAD.ActiveDocument.ActiveObject.Name #say( FreeCAD.ActiveDocument.ActiveObject.Label) shape= FreeCAD.ActiveDocument.ActiveObject.Shape pad_shps.append([p,p_center,shpName]) elif p[0][0]=='arc': #print pad r1=p[0][1]; cx1=p[0][2]; cy1=p[0][3] #print 'r1 ', r1, ' c1 ', cx1,',',cy1 r2=p[2][1]; cx2=p[2][2]; cy2=p[2][3] #print 'r2 ', r2, ' c2 ', cx2,',',cy2 #stop if abs(cx1-cx2)>edge_tolerance: # horizontal sayerr('horizontal') px=(p[0][10].x+p[2][11].x)/2;py=(p[0][11].y+p[2][11].y)/2; sx=2*r1+abs((p[0][10].x-p[2][11].x)); sy=2*r1 else: #vertical sayerr('vertical') px=(p[0][10].x+p[0][11].x)/2;py=(p[0][10].y+p[2][10].y)/2; sx=2*r1; sy=2*r1+abs((p[0][10].y-p[2][10].y)) p_center=(px,py,0) # Draft.makePoint(px,py, 0) wr=[] #print p #arc -> approximate shape with rectangle wr.append(Part.makeLine((px-sx/2, py-sy/2,0.0),(px-sx/2, py+sy/2,0.0))) wr.append(Part.makeLine((px-sx/2, py+sy/2,0.0),(px+sx/2, py+sy/2,0.0))) wr.append(Part.makeLine((px+sx/2, py+sy/2,0.0),(px+sx/2, py-sy/2,0.0))) wr.append(Part.makeLine((px+sx/2, py-sy/2,0.0),(px-sx/2, py-sy/2,0.0))) ps=Part.Wire(wr) face = Part.Face(ps) Part.show(face) #stop shpName=FreeCAD.ActiveDocument.ActiveObject.Name #say( FreeCAD.ActiveDocument.ActiveObject.Label) shape= FreeCAD.ActiveDocument.ActiveObject.Shape pad_shps.append([p,p_center,shpName]) i=1 drl=[] for p in pad_shps: #print d point1=FreeCAD.Vector(p[1]) #Draft.makePoint(p[1][0],p[1][1],p[1][2]) shp=FreeCAD.ActiveDocument.getObject(p[2]).Shape for p2 in pad_shps[i:]: shp2=FreeCAD.ActiveDocument.getObject(p2[2]).Shape #sayw(point);say(shp2.BoundBox.YLength);sayw(shp.BoundBox.YLength) #print shp2.isInside(point,0,True) point2=FreeCAD.Vector(p2[1]) if shp2.isInside(point1,0,True): sayerr('pad in pad found! ')#+str(p[0])) #checking who is inside if shp2.BoundBox.YLength > shp.BoundBox.YLength: drl.append(p[0]) else: drl.append(p2[0]) elif shp.isInside(point2,0,True): sayerr('pad in pad found! ')#+str(p2[0])) #checking who is inside if shp2.BoundBox.YLength > shp.BoundBox.YLength: drl.append(p[0]) else: drl.append(p2[0]) i=i+1 #print pad_shps #sayw(drl) testing=False #True if not testing: for p in pad_shps: FreeCAD.ActiveDocument.removeObject(p[2]) for d in drl: pads.remove(d) ## remove drls from pads #say(pads) return drl else: drl=[] return drl ## return drls and pads without drls ## def collect_pads(pad_list): #print pad_list #sort edges to form a single closed 2D shape loopcounter = 0 normalized_pads = [] #sayw((edges)) #stop if (len(pad_list)==0): sayw("no Pads found") else: newPads = []; #sayerr(pad_list) for pad in (pad_list): #print pad if pad[0]=='circle': normalized_pads.append([pad]) #pad_list.pop(i) npd=[] for pad in (pad_list): if pad[0]!='circle': npd.append(pad) pad_list=[] pad_list=npd #sayw(pad_list) #sayerr (len(pad_list)) #stop if (len(pad_list)>0): #print pad_list #newPads.append(pad_list.pop(0)) #print 'HERE' #print pad_list[0] ## print pad_list[1][9].Radius, ' ',pad_list[1][9].Center.x,' ',pad_list[1][9].Center.y ## print pad_list[1][10].x, ' ',pad_list[1][10].y # p1.x,p1.y ## print pad_list[1][11].x, ' ',pad_list[1][11].y # p2.x,p2.y newPads.append(pad_list[0]) pad_list.pop(0) #print pad_list if newPads[0][0]=='line': #print newPads,' line'#;stop nextCoordinate = (newPads[0][3],newPads[0][4]) firstCoordinate = (newPads[0][1],newPads[0][2]) elif newPads[0][0]=='arc': nextCoordinate = (newPads[0][10].x,newPads[0][10].y) firstCoordinate = (newPads[0][11].x,newPads[0][11].y) #elif newPads[0][0]=='circle': # normalized_pads.append(newPads) # ## TDB!!! #print 'nextCoordinate1 ',nextCoordinate #print pad_list;stop while(len(pad_list)>0 and loopcounter < 2): loopcounter = loopcounter + 1 #print "nextCoordinate: ", nextCoordinate #if len(newEdges[0].Vertexes) > 1: # not circle for j, pad in enumerate(pad_list): #print j #sayerr(pad_list[j][0]) #say(pad) #print pad_list[j],' line1'#;stop if pad_list[j][0]=='line': #print pad_list[j],' line2'#;stop #print 'nextCoordinate ',nextCoordinate if distance((pad_list[j][3],pad_list[j][4]), nextCoordinate)<=edge_tolerance: #if edges[j].Vertexes[-1].Point != nextCoordinate: ## if distance((pad_list[j][3],pad_list[j][4]), nextCoordinate)>edge_tolerance_warning: ## sayerr('non coincident edges:\n'+str(nextCoordinate)+';'+str((pad_list[j][1],pad_list[j][2]))) nextCoordinate = (pad_list[j][1],pad_list[j][2]) newPads.append(pad_list.pop(j)) loopcounter = 0 break elif distance((pad_list[j][1],pad_list[j][2]), nextCoordinate)<=edge_tolerance: #if edges[j].Vertexes[0].Point != nextCoordinate: ## if distance((pad_list[j][1],pad_list[j][2]), nextCoordinate)>edge_tolerance_warning: ## sayerr('non coincident edges:\n'+str(nextCoordinate)+';'+str((pad_list[j][3],pad_list[j][4]))) nextCoordinate = (pad_list[j][3],pad_list[j][4]) newPads.append(pad_list.pop(j)) loopcounter = 0 break elif pad_list[j][0]=='arc': #print pad_list[j],' line2'#;stop #print 'nextCoordinate ',nextCoordinate if distance((pad_list[j][11].x,pad_list[j][11].y), nextCoordinate)<=edge_tolerance: #if edges[j].Vertexes[-1].Point != nextCoordinate: ## if distance((pad_list[j][3],pad_list[j][4]), nextCoordinate)>edge_tolerance_warning: ## sayerr('non coincident edges:\n'+str(nextCoordinate)+';'+str((pad_list[j][1],pad_list[j][2]))) nextCoordinate = (pad_list[j][10].x,pad_list[j][10].y) newPads.append(pad_list.pop(j)) loopcounter = 0 break elif distance((pad_list[j][10].x,pad_list[j][10].y), nextCoordinate)<=edge_tolerance: #if edges[j].Vertexes[0].Point != nextCoordinate: ## if distance((pad_list[j][1],pad_list[j][2]), nextCoordinate)>edge_tolerance_warning: ## sayerr('non coincident edges:\n'+str(nextCoordinate)+';'+str((pad_list[j][3],pad_list[j][4]))) nextCoordinate = (pad_list[j][11].x,pad_list[j][11].y) newPads.append(pad_list.pop(j)) loopcounter = 0 break #elif pad_list[j][0]=='circle': # nextCoordinate=firstCoordinate # normalized_pads.append(newPads) # loopcounter = 0 # break # #print pad_list[j] # #stop #sayw(len(pad_list)) #sayw(pad_list) if distance(firstCoordinate, nextCoordinate)<=edge_tolerance:# or pad_list[j-1][0]=='circle': say('2d closed path') normalized_pads.append(newPads) if (len(pad_list)>0): newPads = []; newPads.append(pad_list.pop(0)) if newPads[0][0]=='line': nextCoordinate = (newPads[0][3],newPads[0][4]) firstCoordinate = (newPads[0][1],newPads[0][2]) elif newPads[0][0]=='arc': nextCoordinate = (newPads[0][11].x,newPads[0][11].y) firstCoordinate = (newPads[0][10].x,newPads[0][10].y) #stop #elif newPads[0][0]=='circle': #else: # say("error in creating Pads") # stop #print normalized_pads, 'pads NBR ', len(normalized_pads), 'loopcounter ', loopcounter #sayw('pads NBR '+str(len(normalized_pads))) #sayw('normalized_pads '+str((normalized_pads))) norm_pads=[];n_pads=[] #print normalized_pads #stop for pads in normalized_pads: #sayw (pads) if pads[0][0]=='line': #stop first_elm = pads[0] n_pads=[];i=1 for elm in pads: if i>1: n_pads.append(elm) i=i+1 n_pads.append(first_elm) #stop norm_pads.append(n_pads) else: norm_pads.append(pads) #sayerr(norm_pads);stop if len (norm_pads)>0: #sayw(norm_pads) return norm_pads else: return normalized_pads ## def createFpPad(pad,offset,tp, _drills=None): global pad_nbr, edge_tolerance import Draft #if tp=='SMD': # if pad[0]=='circle': # sayerr('circle pad nbr.'+str(pad_nbr)) # px=pad[2];py=pad[3]*-1;sx=2*pad[1];sy=2*pad[1] # pdl =" (pad "+str(pad_nbr)+" smd circle (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" # pad_nbr=pad_nbr+1 # #say(pad) # return pdl # elif pad[0][0]=='line': # sayerr('rect pad nbr.'+str(pad_nbr)) # px=(pad[0][1]+pad[0][3])/2;py=(pad[1][2]+pad[1][4])/-2; # if abs(pad[0][1]-pad[0][3]) >0: # sx=abs(pad[0][1]-pad[0][3]) # px=(pad[0][1]+pad[0][3])/2 # else: # sx=abs(pad[1][1]-pad[1][3]) # px=(pad[1][1]+pad[1][3])/2 # if abs(pad[1][2]-pad[1][4]) >0: # sy=abs(pad[1][2]-pad[1][4]) # py=(pad[1][2]+pad[1][4])/-2; # else: # sy=abs(pad[0][2]-pad[0][4]) # py=(pad[0][2]+pad[0][4])/-2; # #print pad[0];print pad[1] # #stop # pdl =" (pad "+str(pad_nbr)+" smd rect (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" # pad_nbr=pad_nbr+1 # #say(pad);sayw(pdl) # return pdl # elif pad[0][0]=='arc': # sayerr('arc pad nbr.'+str(pad_nbr)) # #print pad[0] # r1=pad[0][1]; cx1=pad[0][2]; cy1=pad[0][3] # #print 'r1 ', r1, ' c1 ', cx1,',',cy1 # r2=pad[2][1]; cx2=pad[2][2]; cy2=pad[2][3] # #print 'r2 ', r2, ' c2 ', cx2,',',cy2 # #stop # ## px=((cx1-r1)+(cx2+r2))/2;py=((cy1-r1)+(cy2+r2))/-2;sx=abs((cx1-r1)-(cx2+r2));sy=abs((cy1-r1)-(cy2+r2)) # #print pad[0] # #print pad[1] # if abs(cx1-cx2)>edge_tolerance: # horizontal # sayerr('horizontal') # px=(pad[0][10].x+pad[2][11].x)/2;py=(pad[0][11].y+pad[2][11].y)/-2; # sx=2*r1+abs((pad[0][10].x-pad[2][11].x)); sy=2*r1 # else: #vertical # sayerr('vertical') # px=(pad[0][10].x+pad[0][11].x)/2;py=(pad[0][10].y+pad[2][10].y)/-2; # sx=2*r1; sy=2*r1+abs((pad[0][10].y-pad[2][10].y)) # pdl =" (pad "+str(pad_nbr)+" smd oval (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" # pad_nbr=pad_nbr+1 # #say(pad);sayw(pdl) # return pdl # else: # return u'' if tp=='Drills': #getting center and size if pad[0]=='circle': sayerr('circle drill') px=pad[2];py=pad[3]*-1;sx=2*pad[1];sy=2*pad[1] drl =[px,py,sx,sy] pad_nbr=pad_nbr+1 #say(drl) return drl elif pad[0][0]=='arc': sayerr('oval drill') #print pad r1=pad[0][1]; cx1=pad[0][2]; cy1=pad[0][3] #print 'r1 ', r1, ' c1 ', cx1,',',cy1 r2=pad[2][1]; cx2=pad[2][2]; cy2=pad[2][3] # print 'r2 ', r2, ' c2 ', cx2,',',cy2 #stop ## different method for horizontal and vertical if abs(cx1-cx2)>edge_tolerance: # horizontal px=((cx1-r1)+(cx2+r2))/2;py=((cy1-r1)+(cy2+r2))/-2;sx=abs(cx1-cx2)+2*r2;sy=2*r2 else: #vertical px=((cx1-r1)+(cx2+r2))/2;py=((cy1-r1)+(cy2+r2))/-2;sx=2*r2;sy=abs(cy1-cy2)+2*r2 drl =[px,py,sx,sy] pad_nbr=pad_nbr+1 #say(pad);sayw(drl) return drl else: return u'' #elif tp == 'PadsAll' or tp=='TH' or tp=='NPTH': #getting center and size elif 'PadsAll' in tp or tp=='TH' or tp=='NPTH': #getting center and size pad_layers=" (layers *.Cu *.Mask))" if tp=='PadsAll': tp='TH';ptp='thru_hole' if tp=='TH': ptp='thru_hole' else: ptp='np_thru_hole' #sayw (pad) found_drill=False if pad[0]=='circle': if '_padNbr=' in pad[-1]: padNbr='"'+pad[-1][pad[-1].index('_padNbr='):].replace('_padNbr=','').replace('_tmp','').replace('_','')+'"' elif '_padNum=' in pad[-1]: padNbr='"'+pad[-1][pad[-1].index('_padNum='):].replace('_padNum=','').replace('_tmp','').replace('_','')+'"' else: padNbr='"#"' sayerr('circle pad nbr.'+str(pad_nbr)) cx=pad[2];cy=pad[3]*-1;sx=2*pad[1];sy=2*pad[1] if len(_drills)>0: #print _drills for d in _drills: # print d if d[0] > cx-sx/2 and d[0] < cx+sx/2 and d[1] > cy-sy/2 and d[1] < cy+sy/2: sayw('drill in pad found!') found_drill=True break #drl_size=[d[2],d[3]] ### OFFSET if found_drill: if d[2]!=d[3]: drill_str="(drill oval "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[3]) #+")" else: drill_str="(drill "+"{0:.3f}".format(d[2]) #+")" if abs(d[0]-cx)>edge_tolerance or abs(d[1]-cy)>edge_tolerance: #if d[0] != cx or d[1] != cy: drill_str=drill_str+" (offset "+"{0:.3f}".format(cx-d[0])+" "+"{0:.3f}".format(cy-d[1])+"))" #+")" cx=d[0];cy=d[1] else: drill_str=drill_str+")" else: drill_str="" #"(drill 0)" if tp=='NPTH': ptp="np_thru_hole"; pad_layers=" (layers *.Cu *.Mask))" drill_str="(drill "+"{0:.3f}".format(sx) +")" #drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #"(drill 0)" else: #print('pad[-1]',pad[-1]) pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[-1]) if 'B_Cu' in pad[-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask))" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+"))" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" drill_str="" #"(drill 0)" else: if tp=='NPTH': ptp="np_thru_hole"; pad_layers=" (layers *.Cu *.Mask))" drill_str="(drill "+"{0:.3f}".format(sx) +")" #drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #"(drill 0)" else: if 0: #'B_Cu' in tp: ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask))" else: ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" drill_str="" #"(drill 0)" if sx==sy: pshp='circle' else: pshp='oval' #pdl =" (pad "+str(pad_nbr)+" "+ptp+" "+pshp+" (at "+str(cx)+" "+str(cy)+") (size "+str(sx)+" "+str(sy)+") "+drill_str+pad_layers pdl =" (pad "+padNbr+" "+ptp+" "+pshp+" (at "+"{0:.3f}".format(cx)+" "+"{0:.3f}".format(cy)+") (size "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+") "+drill_str+pad_layers pad_nbr=pad_nbr+1 #say(pad) return pdl elif pad[0][0]=='line': if '_padNbr=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNbr='):].replace('_padNbr=','').replace('_tmp','').replace('_','')+'"' elif '_padNum=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNum='):].replace('_padNum=','').replace('_tmp','').replace('_','')+'"' else: padNbr='"#"' #say(_drills) #sayerr('rect pad nbr.'+str(pad_nbr)) #sayw(str(pad)) #cx=(pad[0][1]+pad[0][3])/2;cy=(pad[1][2]+pad[1][4])/-2;sx=abs(pad[0][1]-pad[0][3]);sy=abs(pad[1][2]-pad[1][4]) ptype="rect" sayerr('rect pad nbr.'+str(pad_nbr)) px=(pad[0][1]+pad[0][3])/2;py=(pad[1][2]+pad[1][4])/-2; if abs(pad[0][1]-pad[0][3]) > edge_tolerance: sx=abs(pad[0][1]-pad[0][3]) px=(pad[0][1]+pad[0][3])/2 else: sx=abs(pad[1][1]-pad[1][3]) px=(pad[1][1]+pad[1][3])/2 if abs(pad[1][2]-pad[1][4]) > edge_tolerance: sy=abs(pad[1][2]-pad[1][4]) py=(pad[1][2]+pad[1][4])/-2; else: sy=abs(pad[0][2]-pad[0][4]) py=(pad[0][2]+pad[0][4])/-2; #print pad[0];print pad[1] #stop found_drill=False if len(_drills)>0: for d in _drills: if d[0] > px-sx/2 and d[0] < px+sx/2 and d[1] > py-sy/2 and d[1] < py+sy/2: sayw('drill in pad found! '+str(d[0])+','+str(d[1])+'/'+str(px)+','+str(py)+':'+str(sx)+','+str(sy)) found_drill=True break #drl_size=[d[2],d[3]] ### OFFSET if found_drill: if d[2]!=d[3]: drill_str="(drill oval "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[3]) #+")" else: drill_str="(drill "+"{0:.3f}".format(d[2]) #+")" if abs(d[0]-px)>edge_tolerance or abs(d[1]-py)>edge_tolerance: drill_str=drill_str+" (offset "+"{0:.3f}".format(px-d[0])+" "+"{0:.3f}".format(py-d[1])+"))" #+")" px=d[0];py=d[1] else: drill_str=drill_str+")" else: drill_str="" #"(drill 0)" if tp=='NPTH': sayerr('Error: NPTH rectangular pad WITHOUT drill -> correcting to oval/circle pad') ptp="np_thru_hole"; pad_layers=" (layers *.Cu *.Mask))" if sx==sy: drill_str="(drill "+"{0:.3f}".format(sx) +")" ptype="circle" else: drill_str="(drill oval "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+")" #"(drill 0)" ptype="oval" else: #print('pad[-1] Rect',pad[-1]) pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[0][-1]) if 'B_Cu' in pad[0][-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask))" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+"))" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" #ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" #drill_str="" #"(drill 0)" else: if tp=='NPTH': ptp="np_thru_hole"; pad_layers=" (layers *.Cu *.Mask))" if sx==sy: drill_str="(drill "+"{0:.3f}".format(sx) +")" ptype="circle" else: drill_str="(drill oval "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+")" #"(drill 0)" ptype="oval" else: #print('pad[-1] Rect 2',pad[-1]) pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[0][-1]) if 'B_Cu' in pad[0][-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask))" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+"))" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" #ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" drill_str="" #"(drill 0)" #pdl =" (pad "+str(pad_nbr)+" "+ptp+" rect (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") "+drill_str+pad_layers pdl =" (pad "+padNbr+" "+ptp+" "+ptype+" (at "+"{0:.3f}".format(px)+" "+"{0:.3f}".format(py)+") (size "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+") "+drill_str+pad_layers #pdl =" (pad "+str(pad_nbr)+" thru_hole rect (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" pad_nbr=pad_nbr+1 #say(pad);sayw(pdl) return pdl elif pad[0][0]=='arc': if '_padNbr=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNbr='):].replace('_padNbr=','').replace('_tmp','').replace('_','')+'"' elif '_padNum=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNum='):].replace('_padNum=','').replace('_tmp','').replace('_','')+'"' else: padNbr='"#"' sayerr('arc pad nbr.'+str(pad_nbr)) #print pad r1=pad[0][1]; cx1=pad[0][2]; cy1=pad[0][3] #print 'r1 ', r1, ' c1 ', cx1,',',cy1 r2=pad[2][1]; cx2=pad[2][2]; cy2=pad[2][3] #print 'r2 ', r2, ' c2 ', cx2,',',cy2 #stop if abs(cx1-cx2)>edge_tolerance: # horizontal sayerr('horizontal') px=(pad[0][10].x+pad[2][11].x)/2;py=(pad[0][11].y+pad[2][11].y)/-2; sx=2*r1+abs((pad[0][10].x-pad[2][11].x)); sy=2*r1 else: #vertical sayerr('vertical') px=(pad[0][10].x+pad[0][11].x)/2;py=(pad[0][10].y+pad[2][10].y)/-2; sx=2*r1; sy=2*r1+abs((pad[0][10].y-pad[2][10].y)) found_drill=False if len(_drills)>0: for d in _drills: if d[0] > px-sx/2 and d[0] < px+sx/2 and d[1] > py-sy/2 and d[1] < py+sy/2: sayw('drill in pad found! '+str(d[0])+','+str(d[1])+'/'+str(px)+','+str(py)+':'+str(sx)+','+str(sy)) #if d[0] > cx-sx/2 and d[0] < cx+sx/2 and d[1] > cy-sy/2 and d[1] < cy+sy/2: # sayw('drill in pad found!') found_drill=True break #drl_size=[d[2],d[3]] ### OFFSET if found_drill: if d[2]!=d[3]: drill_str="(drill oval "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[3]) #+")" else: drill_str="(drill "+"{0:.3f}".format(d[2]) #+")" if abs(d[0]-px)>edge_tolerance or abs(d[1]-py)>edge_tolerance: drill_str=drill_str+" (offset "+"{0:.3f}".format(px-d[0])+" "+"{0:.3f}".format(py-d[1])+"))" #+")" px=d[0];py=d[1] else: drill_str=drill_str+")" else: if tp=='NPTH': ptp="np_thru_hole"; pad_layers=" (layers *.Cu *.Mask))" if sx==sy: drill_str="(drill "+"{0:.3f}".format(sx) +")" else: drill_str="(drill oval "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+")" #"(drill 0)" #drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #"(drill 0)" else: pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[0][-1]) if 'B_Cu' in pad[0][-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask))" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+"))" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" if '_padNbr=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNbr='):].replace('_padNbr=','')+'"' elif '_padNum=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNum='):].replace('_padNum=','')+'"' else: padNbr='"#"' #ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" drill_str="" #"(drill 0)" else: if tp=='NPTH': ptp="np_thru_hole"; pad_layers=" (layers *.Cu *.Mask))" if sx==sy: drill_str="(drill "+"{0:.3f}".format(sx) +")" else: drill_str="(drill oval "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+")" #"(drill 0)" #drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #"(drill 0)" else: pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[0][-1]) if 'B_Cu' in pad[0][-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask))" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+"))" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" #ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask))" drill_str="" #"(drill 0)" #pdl =" (pad "+str(pad_nbr)+" "+ptp+" oval (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") "+drill_str+pad_layers pdl =" (pad "+padNbr+" "+ptp+" oval (at "+"{0:.3f}".format(px)+" "+"{0:.3f}".format(py)+") (size "+"{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+") "+drill_str+pad_layers #pdl =" (pad "+str(pad_nbr)+" "+ptp+" oval (at "+str(cx)+" "+str(cy)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" pad_nbr=pad_nbr+1 #say(pad);sayw(pdl) return pdl else: return u'' ##--------------------------------------------## #elif tp=='RoundRect': elif 'RoundRect' in tp: if '_padNbr=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNbr='):].replace('_padNbr=','').replace('_tmp','').replace('_','')+'"' elif '_padNum=' in pad[0][-1]: padNbr='"'+pad[0][-1][pad[0][-1].index('_padNum='):].replace('_padNum=','').replace('_tmp','').replace('_','')+'"' else: padNbr='"#"' found_drill=False #sayw(pad) #stop ptp='thru_hole' pad_layers=" (layers *.Cu *.Mask)" if pad[0][0]=='arc': #say(_drills) sayerr('round rect pad nbr.'+str(pad_nbr)) #print p r1=pad[0][1]; cx1=pad[0][2]; cy1=pad[0][3] #print 'r1 ', r1, ' c1 ', cx1,',',cy1 r2=pad[4][1]; cx2=pad[4][2]; cy2=pad[2][3] #print 'r2 ', r2, ' c2 ', cx2,',',cy2 #stop if abs(cx1-cx2)>edge_tolerance: # horizontal sayerr('horizontal') #print pad px=(pad[0][10].x+pad[4][10].x)/2; py=(pad[0][10].y+pad[4][10].y)/2; sx=abs(pad[5][1]-pad[1][1]) sy=abs(pad[7][2]-pad[3][2]) #print r1,' ',sx-2*r1 else: #vertical stop # sayerr('vertical') # #print pad[0][10].x # #print pad[2][10].x # px=(pad[0][10].x+pad[4][10].x)/2; # py=(pad[0][10].y+pad[4][10].y)/2; # sx=2*r1+abs(pad[3][1]-pad[3][3]); # sy=2*r1+abs(pad[1][2]-pad[1][4]) p_center=(px,py,0) #print px,' ',py #print sx,' ',sy rratio=r1/min(sx,sy) #Draft.makePoint(px,py, 0) # cx=(pad[0][1]+pad[0][3])/2;cy=(pad[1][2]+pad[1][4])/-2;sx=abs(pad[0][1]-pad[0][3])+2*pad[4][1];sy=abs(pad[1][2]-pad[1][4])+2*pad[4][1] # r=pad[4][1] # rratio=r/min(sx,sy) # #sayerr('center='+str(cx)+','+str(cy)+' size='+str(sx)+','+str(sy)) # found_drill=False # r1=pad[0][1]; cx1=pad[0][2]; cy1=pad[0][3] # #print 'r1 ', r1, ' c1 ', cx1,',',cy1 # r2=pad[2][1]; cx2=pad[2][2]; cy2=pad[2][3] # #print 'r2 ', r2, ' c2 ', cx2,',',cy2 # #stop # if abs(cx1-cx2)>edge_tolerance: # horizontal # sayerr('horizontal') # px=(pad[0][10].x+pad[2][11].x)/2;py=(pad[0][11].y+pad[2][11].y)/-2; # sx=2*r1+abs((pad[0][10].x-pad[2][11].x)); sy=2*r1 # else: #vertical # sayerr('vertical') # px=(pad[0][10].x+pad[0][11].x)/2;py=(pad[0][10].y+pad[2][10].y)/-2; # sx=2*r1; sy=2*r1+abs((pad[0][10].y-pad[2][10].y)) found_drill=False if len(_drills)>0: for d in _drills: #sayw(d) if d[0] > px-sx/2 and d[0] < px+sx/2 and -d[1] > py-sy/2 and -d[1] < py+sy/2: sayw('drill in pad found! '+str(d[0])+','+str(-d[1])+'/'+str(px)+','+str(py)+':'+str(sx)+','+str(sy)) #if d[0] > cx-sx/2 and d[0] < cx+sx/2 and d[1] > cy-sy/2 and d[1] < cy+sy/2: # sayw('drill in pad found!') found_drill=True break #drl_size=[d[2],d[3]] ### OFFSET if found_drill: if d[2]!=d[3]: drill_str="(drill oval "+"{0:.3f}".format(abs(d[2]))+" "+"{0:.3f}".format(abs(d[3])) #+")" else: drill_str="(drill "+"{0:.3f}".format(abs(d[2])) #+")" if abs(d[0]-px)>edge_tolerance or abs(-d[1]-py)>edge_tolerance: drill_str=drill_str+" (offset "+"{0:.3f}".format(px-d[0])+" "+"{0:.3f}".format(-py-d[1])+"))" #+")" px=d[0];py=d[1] else: drill_str=drill_str+")" else: pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[0][-1]) if 'B_Cu' in pad[0][-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask)" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+")" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask)" #ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask)" py=-py drill_str="" #"(drill 0)" else: pattern = '_In+([0-9]*?).Cu' result = re.search(pattern, pad[0][-1]) if 'B_Cu' in pad[0][-1]: pdLr='B.Cu' ptp="smd"; pad_layers=" (layers B.Cu B.Paste B.Mask)" elif result is not None: pdLr=result.group().replace('_','.')[1:] ptp="smd"; pad_layers=" (layers "+pdLr+")" else: pdLr='F.Cu' ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask)" #ptp="smd"; pad_layers=" (layers F.Cu F.Paste F.Mask)" py=-py drill_str="" #"(drill 0)" # if len(_drills)>0: # for d in _drills: # sayw(d) # if d[0] > cx-sx/2 and d[0] < cx+sx/2 and d[1] > cy-sy/2 and d[1] < cy+sy/2: # sayw('drill in pad found! '+str(d[0])+','+str(d[1])+'/'+str(cx)+','+str(cy)+':'+str(sx)+','+str(sy)) # found_drill=True # ptp='thru_hole' # p_layers='(layers *.Cu *.Mask)' # break # #drl_size=[d[2],d[3]] # ### OFFSET # if found_drill: # if d[2]!=d[3]: # drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #+")" # else: # drill_str="(drill "+str(d[2]) #+")" # if abs(d[0]-cx)>edge_tolerance or abs(d[1]-cy)>edge_tolerance: # drill_str=drill_str+"(offset "+str(cx-d[0])+" "+str(cy-d[1])+"))" #+")" # cx=d[0];cy=d[1] # else: # drill_str=drill_str+")" # else: # #drill_str="(drill 0)" # ptp='smd' # drill_str="" # p_layers='(layers F.Cu F.Paste F.Mask)' # else: # drill_str="" # ptp='smd' # p_layers='(layers F.Cu F.Paste F.Mask)' #rratio=0.25 ### TBD #pdl =" (pad "+str(pad_nbr)+" "+ptp+" roundrect (at "+str(cx)+" "+str(cy)+") (size "+\ pdl =" (pad "+padNbr+" "+ptp+" roundrect (at "+"{0:.3f}".format(px)+" "+"{0:.3f}".format(py)+") (size "+\ "{0:.3f}".format(sx)+" "+"{0:.3f}".format(sy)+") "+drill_str+" "+pad_layers+" (roundrect_rratio "+"{0:.3f}".format(rratio)+"))" #+str(rratio)+"))" #pdl =" (pad "+str(pad_nbr)+" thru_hole rect (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" pad_nbr=pad_nbr+1 #say(pad);sayw(pdl) return pdl ##--------------------------------------------## #elif tp=='RoundRect': # found_drill=False # if pad[0][0]=='line': # #say(_drills) # sayerr('round rect pad nbr.'+str(pad_nbr)) # cx=(pad[0][1]+pad[0][3])/2;cy=(pad[1][2]+pad[1][4])/-2;sx=abs(pad[0][1]-pad[0][3])+2*pad[4][1];sy=abs(pad[1][2]-pad[1][4])+2*pad[4][1] # r=pad[4][1] # rratio=r/min(sx,sy) # #sayerr('center='+str(cx)+','+str(cy)+' size='+str(sx)+','+str(sy)) # found_drill=False # if len(_drills)>0: # for d in _drills: # if d[0] > cx-sx/2 and d[0] < cx+sx/2 and d[1] > cy-sy/2 and d[1] < cy+sy/2: # sayw('drill in pad found! '+str(d[0])+','+str(d[1])+'/'+str(cx)+','+str(cy)+':'+str(sx)+','+str(sy)) # found_drill=True # ptp='thru_hole' # p_layers='(layers *.Cu *.Mask)' # break # #drl_size=[d[2],d[3]] # ### OFFSET # if found_drill: # if d[2]!=d[3]: # drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #+")" # else: # drill_str="(drill "+str(d[2]) #+")" # if abs(d[0]-cx)>edge_tolerance or abs(d[1]-cy)>edge_tolerance: # drill_str=drill_str+" (offset "+str(cx-d[0])+" "+str(cy-d[1])+"))" #+")" # cx=d[0];cy=d[1] # else: # drill_str=drill_str+")" # else: # #drill_str="(drill 0)" # ptp='smd' # drill_str="" # p_layers='(layers F.Cu F.Paste F.Mask)' # else: # drill_str="" # ptp='smd' # p_layers='(layers F.Cu F.Paste F.Mask)' # #rratio=0.25 ### TBD # #pdl =" (pad "+str(pad_nbr)+" "+ptp+" roundrect (at "+str(cx)+" "+str(cy)+") (size "+\ # pdl =" (pad # "+ptp+" roundrect (at "+str(cx)+" "+str(cy)+") (size "+\ # str(sx)+" "+str(sy)+") "+drill_str+" "+p_layers+"(roundrect_rratio "+str(rratio)+"))" # #pdl =" (pad "+str(pad_nbr)+" thru_hole rect (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" # pad_nbr=pad_nbr+1 # #say(pad);sayw(pdl) # return pdl ##--------------------------------------------## elif tp=='FZ_Mask_Poly': # print('use FZ fp_poly') found_drill=False padLayer="F.Mask" pts="" edgs=[] for lines in pad: edgs.append(Part.makeLine((lines[1],lines[2],0),(lines[3],lines[4],0))) #wd_=float(line[0][4].split('_')[2]) # Part.show(Part.makeLine((lines[1],lines[2],0),(lines[3],lines[4],0))) face = OSCD2Dg_edgestofaces(edgs,3 , edge_tolerance) if 0: face.check() # reports errors face.fix(0,0,0) ws=face.Wires # print('face ws') print(face.Wires,len(face.Wires)) if 1: Part.show(face) f=FreeCAD.ActiveDocument.ActiveObject FreeCAD.ActiveDocument.recompute() ws=f.Shape.Wires print(ws,len(ws)) stop j=0 for w in ws: j=j+1 # cls=Part.getSortedClusters(w.Edges) # print(cls) clusters = Part.sortEdges(w.Edges) #[0] print(len(clusters)) for cluster in clusters: #print(len(cluster)) pts+=" (fp_poly"+os.linesep+" (pts"+os.linesep for i,e in enumerate(cluster): #w.Edges): if i < len(cluster)-1: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+") (xy "+"{0:.3f}".format(e.Vertexes[1].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[1].Y))+")"+os.linesep else: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+")) (layer "+padLayer+") (width 0))"+os.linesep print(pts,j) return pts if 0: clusters=Part.getSortedClusters(w.Edges) for cluster in clusters: # print(cluster) # if len(cluster)>1: # print(cluster) # for c in cluster: # #Part.show(Part.makePolygon(cluster)) # Part.show(c) for i,e in enumerate(cluster): #w.Edges): if i < len(cluster)-1: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+") (xy "+"{0:.3f}".format(e.Vertexes[1].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[1].Y))+")"+os.linesep else: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+")) (layer "+padLayer+") (width 0))"+os.linesep #print(pts,j) stop c = Part.makeCompound(edgs) s=Part.show(c) FreeCAD.ActiveDocument.recompute() #f = Part.makeFace(s.Shape,'Part::FaceMakerSimple') #Part.getSortedClusters(s.Edges) ws=s.Shape.Wires print(ws) print(s.Shape.Edges) cls=Part.getSortedClusters(s.Shape.Edges) print('clsa') print(cls[0]) sc=s.Shape.copy() print(sc.Edges) cls=Part.getSortedClusters(sc.Edges) print('cls') print(cls[0]) stop Part.show(Part.makePolygon(cls[0])) stop for e in edgs: Part.show(e) stop #f= # f= Part.makeFace(sps,'Part::FaceMakerSimple') # Part.show(f) stop wr=[] if 0: pad_ref=" (pad # smd custom (at 0 0 ) (size 0.1 0.1) (layers F.Mask)"+os.linesep pad_ref=pad_ref+" (zone_connect 0)"+os.linesep pad_ref=pad_ref+" (options (clearance outline) (anchor circle))"+os.linesep pad_ref=pad_ref+" (primitives"+os.linesep #pad_ref=pad_ref+" (gr_poly" if 0: #pad[0][0]=='line': #sayw(pad) if 0: pts=" (gr_poly (pts"+os.linesep else: pad_ref=" (fp_poly"+os.linesep+" (pts"+os.linesep pts="" segments_nbr=len(pad) i=1 for lines in pad: if i0: for d in _drills: #print d point=FreeCAD.Vector(d[0],-1*d[1],0) if shape.isInside(point,0,True): sayw('pad in poly found! '+str(d[0])+','+str(-1*d[1])) found_drill=True break #stop #if 1: FreeCAD.ActiveDocument.removeObject(shpName) if 1: #if found_drill: #print(len(ant.Wires)) i=1 for w in ant.Wires: pattern = '_In+([0-9]*?)_Cu' result = re.search(pattern, layers[i]) add_maskLayer = True if 'B_Cu' in layers[i]: padLayer = 'B.Cu' elif result is not None: padLayer = result.group().replace('_Cu','.Cu')[1:] # print(padLayer) add_maskLayer = False else: padLayer = 'F.Cu' i=i+1 clusters = Part.sortEdges(w.Edges) #[0] #print(len(clusters)) for cluster in clusters: #print(len(cluster)) for i,e in enumerate(cluster): #w.Edges): if i < len(cluster)-1: #if (e.Vertexes[0].X == clusters[i-1].Vertexes[0].X) and (e.Vertexes[0].Y == clusters[i-1].Vertexes[0].Y): #pts=pts+" (xy "+str(e.Vertexes[0].X)+" "+str(-1*(e.Vertexes[0].Y))+") (xy "+str(e.Vertexes[1].X)+" "+str(-1*(e.Vertexes[1].Y))+")"+os.linesep pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+") (xy "+"{0:.3f}".format(e.Vertexes[1].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[1].Y))+")"+os.linesep #float("{0:.3f}".format(value)) #else: # pts=pts+" (xy "+str(e.Vertexes[1].X)+" "+str(-1*(e.Vertexes[1].Y))+") (xy "+str(e.Vertexes[0].X)+" "+str(-1*(e.Vertexes[0].Y))+")"+os.linesep else: #"{0:.3f}".format( pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X)+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y))+")) (layer "+padLayer+") (width 0))"+os.linesep #pts=pts+" (xy "+str(e.Vertexes[0].X)+" "+str(-1*(e.Vertexes[0].Y))+")) (layer "+padLayer+") (width 0))"+os.linesep #i=1 #for lines in pad: # if i1: ## if (lines[1] == pad[i-1][1]) and (lines[2] == pad[i-1][2]): ## pts=pts+" (xy "+str(lines[1])+" "+str(-1*lines[2])+") (xy "+str(lines[3])+" "+str(-1*lines[4])+")"+os.linesep ## else: ## pts=pts+" (xy "+str(lines[3])+" "+str(-1*lines[4])+") (xy "+str(lines[1])+" "+str(-1*lines[2])+")"+os.linesep ## else: ## pts=pts+" (xy "+str(lines[1])+" "+str(-1*lines[2])+") (xy "+str(lines[3])+" "+str(-1*lines[4])+")"+os.linesep ## else: ## #pts=pts+" (xy "+str(lines[1]-d[0])+" "+str(-1*lines[2]-d[1])+")) (width 0))"+os.linesep ## pts=pts+" (xy "+str(lines[1])+" "+str(-1*lines[2])+")) (layer F.Cu) (width 0))"+os.linesep #i=i+1 #for i,lines in enumerate(pad): # if i1: # if (lines[1] == pad[i-1][1]) and (lines[2] == pad[i-1][2]): # pts=pts+" (xy "+str(lines[1])+" "+str(-1*lines[2])+") (xy "+str(lines[3])+" "+str(-1*lines[4])+")"+os.linesep # else: # pts=pts+" (xy "+str(lines[3])+" "+str(-1*lines[4])+") (xy "+str(lines[1])+" "+str(-1*lines[2])+")"+os.linesep # else: # pts=pts+" (xy "+str(lines[1])+" "+str(-1*lines[2])+") (xy "+str(lines[3])+" "+str(-1*lines[4])+")"+os.linesep # else: # #pts=pts+" (xy "+str(lines[1]-d[0])+" "+str(-1*lines[2]-d[1])+")) (width 0))"+os.linesep # pts=pts+" (xy "+str(lines[1])+" "+str(-1*lines[2])+")) (layer F.Cu) (width 0))"+os.linesep # #i=i+1 if add_maskLayer: pts = pts + pts.replace('.Cu','.Mask') #pad_ref=" (pad "+str(pad_nbr)+" smd custom (at "+str(d[0])+" "+str(d[1])+" ) (size "+str(d[2])+" "+str(d[2])+") (layers F.Cu F.Paste F.Mask)"+os.linesep if found_drill: if add_maskLayer: pad_ref=" (pad "+padNbr+" smd circle (at "+"{0:.3f}".format(d[0])+" "+"{0:.3f}".format(d[1])+" ) (size "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[2])+") (layers "+padLayer+" "+padLayer.rstrip('Cu')+"Mask))"+os.linesep else: pad_ref=" (pad "+padNbr+" smd circle (at "+"{0:.3f}".format(d[0])+" "+"{0:.3f}".format(d[1])+" ) (size "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[2])+") (layers "+padLayer+"))"+os.linesep #if 0: #'B_Cu' in tp: # #pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers B.Cu B.Paste B.Mask)"+os.linesep # pad_ref=" (pad # smd circle (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers B.Cu B.Mask))"+os.linesep #else: # #pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers F.Cu F.Paste F.Mask)"+os.linesep # pad_ref=" (pad # smd circle (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers F.Cu F.Mask))"+os.linesep else: pad_ref="" # #pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers F.Cu F.Paste F.Mask)"+os.linesep # pad_ref=pad_ref+" (zone_connect 0)"+os.linesep # pad_ref=pad_ref+" (options (clearance outline) (anchor circle))"+os.linesep # pad_ref=pad_ref+" (primitives"+os.linesep # #pad_ref=pad_ref+" (gr_poly (pts"+os.linesep #if pad_ref!="": # pad_ref=pad_ref+pts # pad_ref=pad_ref+" ))"+os.linesep #else: # pad_ref=pad_ref+pts+os.linesep pad_ref=pad_ref+pts+os.linesep #sayerr(pad_ref) pad_nbr=pad_nbr+1 found_drill=False else: sayw("missing reference pad for polyline pad") #stop return pad_ref ### #elif tp=='Poly': elif 'Poly' in tp: #NB after NetTie_Poly found_drill=False wr=[] if pad[0][0]=='line': #sayw(pad) pts=" (gr_poly (pts"+os.linesep segments_nbr=len(pad) i=1 layers = [] pnts=[] for lines in pad: #if i0: for d in _drills: #print (d) point=FreeCAD.Vector(d[0],-1*d[1],0) #print(point) if shape.isInside(point,0,True): sayw('pad in poly found! '+str(d[0])+','+str(-1*d[1])) found_drill=True break # FreeCAD.ActiveDocument.removeObject(shpName) #stop #if 1: if found_drill: i=1 for w in ant.Shape.Wires: pattern = '_In+([0-9]*?)_Cu' result = re.search(pattern, layers[i]) clusters = Part.sortEdges(w.Edges) #[0] internalLayer = False print(layers[i]) if 'B_Cu' in layers[i]: padLayer = 'B.Cu' elif result is not None: padLayer = result.group().replace('_Cu','.Cu')[1:] internalLayer = True else: padLayer = 'F.Cu' i=i+1 #print(len(clusters)) for cluster in clusters: #print(len(cluster)) for i,e in enumerate(cluster): #w.Edges): if i < len(cluster)-1: #if (e.Vertexes[0].X == clusters[i-1].Vertexes[0].X) and (e.Vertexes[0].Y == clusters[i-1].Vertexes[0].Y): pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X-d[0])+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y)-d[1])+") (xy "+"{0:.3f}".format(e.Vertexes[1].X-d[0])+" "+"{0:.3f}".format(-1*(e.Vertexes[1].Y)-d[1])+")"+os.linesep #else: # pts=pts+" (xy "+str(e.Vertexes[1].X)+" "+str(-1*(e.Vertexes[1].Y))+") (xy "+str(e.Vertexes[0].X)+" "+str(-1*(e.Vertexes[0].Y))+")"+os.linesep else: pts=pts+" (xy "+"{0:.3f}".format(e.Vertexes[0].X-d[0])+" "+"{0:.3f}".format(-1*(e.Vertexes[0].Y)-d[1])+")) (layer "+padLayer+") (width 0))"+os.linesep #pts = pts + pts.replace('F.Cu','F.Mask') #pad_ref=" (pad "+str(pad_nbr)+" smd custom (at "+str(d[0])+" "+str(d[1])+" ) (size "+str(d[2])+" "+str(d[2])+") (layers F.Cu F.Paste F.Mask)"+os.linesep if found_drill: #ref pad found if internalLayer: pad_ref=" (pad "+padNbr+" smd custom (at "+"{0:.3f}".format(d[0])+" "+"{0:.3f}".format(d[1])+" ) (size "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[2])+") (layers "+padLayer+")"+os.linesep else: pad_ref=" (pad "+padNbr+" smd custom (at "+"{0:.3f}".format(d[0])+" "+"{0:.3f}".format(d[1])+" ) (size "+"{0:.3f}".format(d[2])+" "+"{0:.3f}".format(d[2])+") (layers "+padLayer+" "+padLayer.rstrip('Cu')+"Mask)"+os.linesep #if 'B_Cu' in lyr: # pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers B.Cu B.Paste B.Mask)"+os.linesep # #pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers B.Cu B.Mask)"+os.linesep #else: # #pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers F.Cu F.Paste F.Mask)"+os.linesep # pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers F.Cu F.Mask)"+os.linesep #else: # pad_ref="" # pad_ref=pad_ref+pts+os.linesep # #pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str("{0:.3f}".format(d[2]))+" "+str("{0:.3f}".format(d[2]))+") (layers F.Cu F.Paste F.Mask)"+os.linesep pad_ref=pad_ref+" (zone_connect 0)"+os.linesep pad_ref=pad_ref+" (options (clearance outline) (anchor circle))"+os.linesep pad_ref=pad_ref+" (primitives"+os.linesep # #pad_ref=pad_ref+" (gr_poly (pts"+os.linesep #if pad_ref!="": pad_ref=pad_ref+pts pad_ref=pad_ref+" ))"+os.linesep #pad_ref=pad_ref+pts+" ))"+os.linesep #sayerr(pad_ref) pad_nbr=pad_nbr+1 found_drill=False else: sayw("missing reference pad for polyline pad") stop FreeCAD.ActiveDocument.removeObject(shpName) return pad_ref # if found_drill: # i=1 # for lines in pad: # if i0: # for d in _drills: # if d[0] > cx-sx/2 and d[0] < cx+sx/2 and d[1] > cy-sy/2 and d[1] < cy+sy/2: # sayw('drill in pad found! '+str(d[0])+','+str(d[1])+'/'+str(cx)+','+str(cy)+':'+str(sx)+','+str(sy)) # found_drill=True # ptp='thru_hole' # p_layers='(layers *.Cu *.Mask)' # break # #drl_size=[d[2],d[3]] # ### OFFSET # if found_drill: # if d[2]!=d[3]: # drill_str="(drill oval "+str(d[2])+" "+str(d[3]) #+")" # else: # drill_str="(drill "+str(d[2]) #+")" # if abs(d[0]-cx)>edge_tolerance or abs(d[1]-cy)>edge_tolerance: # drill_str=drill_str+" (offset "+str(cx-d[0])+" "+str(cy-d[1])+"))" #+")" # cx=d[0];cy=d[1] # else: # drill_str=drill_str+")" # else: # #drill_str="(drill 0)" # ptp='smd' # drill_str="" # p_layers='(layers F.Cu F.Paste F.Mask)' # else: # drill_str="" # ptp='smd' # p_layers='(layers F.Cu F.Paste F.Mask)' # #rratio=0.25 ### TBD # pdl =" (pad "+str(pad_nbr)+" "+ptp+" roundrect (at "+str(cx)+" "+str(cy)+") (size "+\ # str(sx)+" "+str(sy)+") "+drill_str+" "+p_layers+"(roundrect_rratio "+str(rratio)+"))" # #pdl =" (pad "+str(pad_nbr)+" thru_hole rect (at "+str(px)+" "+str(py)+") (size "+str(sx)+" "+str(sy)+") (layers F.Cu F.Paste F.Mask))" # pad_nbr=pad_nbr+1 # say(pad);sayw(pdl) # return pdl #return pad_ref ## elif tp=='Pads_Geom': found_drill=False wr=[] if len(_drills)>0: for d in _drills: #print d sayw('drill found! '+str(d[0])+','+str(-1*d[1])) found_drill=True break if pad[0][0]=='line': sayw('line is not supported') elif pad[0][0]=='circle': print (pad) wd_=float(pad[0][4].split('_')[2]) pts=" (gr_circle (center "+"{0:.3f}".format(pad[0][2]-d[0])+" "+"{0:.3f}".format(-1*pad[0][3]-d[1])+") (end "+"{0:.3f}".format(pad[0][2]-d[0]+pad[0][1])+" "+"{0:.3f}".format(-1*pad[0][3]-d[1])+") (width "+"{0:.3f}".format(wd_)+"))"+os.linesep say(pts) if found_drill: #pad_ref=" (pad "+str(pad_nbr)+" smd custom (at "+str(d[0])+" "+str(d[1])+" ) (size "+str(d[2])+" "+str(d[2])+") (layers F.Cu F.Paste F.Mask)"+os.linesep if 0: #'B_Cu' in tp: pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str(d[2])+" "+str(d[2])+") (layers B.Cu B.Paste B.Mask)"+os.linesep else: pad_ref=" (pad # smd custom (at "+str("{0:.3f}".format(d[0]))+" "+str("{0:.3f}".format(d[1]))+" ) (size "+str(d[2])+" "+str(d[2])+") (layers F.Cu F.Paste F.Mask)"+os.linesep pad_ref=pad_ref+" (zone_connect 0)"+os.linesep pad_ref=pad_ref+" (options (clearance outline) (anchor circle))"+os.linesep pad_ref=pad_ref+" (primitives"+os.linesep #pad_ref=pad_ref+" (gr_poly (pts"+os.linesep pad_ref=pad_ref+pts pad_ref=pad_ref+" ))"+os.linesep #sayerr(pad_ref) pad_nbr=pad_nbr+1 found_drill=False else: sayerr("missing reference pad for polyline pad") stop return pad_ref else: return u'' pass ### def getBoardOutline(): global edge_tolerance, maxRadius, maxSegments global dvm, dqd, precision use_discretize = False # discretize or toBiArc method for Ellipses and parabola and Hyperbola if not FreeCAD.activeDocument(): return False # doc = FreeCAD.activeDocument() outline = [] #to_discretize=[[]] * 2 #[[][]] to_discretize=[] construction_geom=[] not_supported = '' sel = FreeCADGui.Selection.getSelection() if len (sel) >0: #sayw(doc.Name) for j in sel: #for j in doc.Objects: sayerr(j.Name) #sayw(j.Geometry) #if hasattr(j, "Proxy") and hasattr(j.Proxy, "Type") and j.Proxy.Type == "PCBboard": ##if hasattr(j, "Geometry"): if "Sketch" in j.TypeId: try: #print j.Geometry for k in range(len(j.Geometry)): #print(type(j.Geometry[k]).__name__) bezier_list = [] bezier_list_tmp = [] if 0: #hasattr(j,'GeometryFacadeList'): Gm = j.GeometryFacadeList else: Gm = j.Geometry #if hasattr(Gm[k],'Construction'): if isConstruction(Gm[k]): #if Gm[k].Construction: construction_geom.append(k) sayw('construction skipped') continue if 'Point' in type(Gm[k]).__name__: #skipping points sayw('point skipped') #sayerr('point') continue #if type(j.Geometry[k]).__name__ == 'LineSegment': if 'LineSegment' in type(Gm[k]).__name__: #if 'Line' in type(j.Geometry[k]).__name__: sk_ge=Gm[k].toShape() #needed to fix some issue on sketch geometry building outline.append([ 'line', sk_ge.Edges[0].Vertexes[0].Point.x, sk_ge.Edges[0].Vertexes[0].Point.y, sk_ge.Edges[0].Vertexes[1].Point.x, sk_ge.Edges[0].Vertexes[1].Point.y, j.Label ]) # outline.append([ # 'line', # j.Geometry[k].StartPoint.x, # j.Geometry[k].StartPoint.y, # j.Geometry[k].EndPoint.x, # j.Geometry[k].EndPoint.y # ]) #elif type(j.Geometry[k]).__name__ == 'Circle': elif 'Circle' in type(Gm[k]).__name__ and not 'ArcOfCircle' in type(Gm[k]).__name__: sk_ge=Gm[k].toShape() #needed to fix some issue on sketch geometry building outline.append([ 'circle', sk_ge.Edges[0].Curve.Radius, sk_ge.Edges[0].Curve.Center.x, sk_ge.Edges[0].Curve.Center.y, j.Label ]) # outline.append([ # 'circle', # j.Geometry[k].Radius, # j.Geometry[k].Center.x, # j.Geometry[k].Center.y # ]) #elif type(j.Geometry[k]).__name__ == 'ArcOfCircle': #elif isinstance(j.Geometry[k].Curve,Part.Circle) elif 'ArcOfCircle' in type(Gm[k]).__name__: #if isinstance(j.Shape.Edges[k].Curve,Part.Circle): #sayw(type(j.Geometry[k])) #sayerr(j.Shape.Edges[k].Curve) #print('arc') sk_ge=Gm[k].toShape() #needed to fix some issue on sketch geometry building outline.append([ 'arc', Gm[k].Radius, sk_ge.Edges[0].Curve.Center.x, sk_ge.Edges[0].Curve.Center.y, Gm[k].FirstParameter, Gm[k].LastParameter, Gm[k].Axis[0], Gm[k].Axis[1], Gm[k].Axis[2], Gm[k], sk_ge.Edges[0].Vertexes[0].Point, sk_ge.Edges[0].Vertexes[1].Point, sk_ge.Edges[0].Orientation, j.Label ]) ##j.Geometry[k].Center.x, ##j.Geometry[k].Center.y, # i=j.Geometry[k] # sayerr('Xaxis1a') # if 0: #i.XAxis.x < 0: ## da cambiare this is not available on FC0.16 # sayerr('Xaxis1b') # outline.append([ # 'arc', # i.Radius, # i.Center.x, # i.Center.y, # i.LastParameter+pi, # i.FirstParameter+pi, # -i.Axis[0], # i.Axis[1], # i.Axis[2], # i # ]) # else: # outline.append([ # 'arc', # i.Radius, # i.Center.x, # i.Center.y, # i.LastParameter, # i.FirstParameter+pi, # i.Axis[0], # i.Axis[1], # i.Axis[2], # i # ]) elif (accept_spline) and ('BSplineCurve' in type(Gm[k]).__name__): bs = Gm[k] # Set degree #maxDegree = 3 #kicad max degree of splines if bs.Degree < maxDegree: bs.increaseDegree(maxDegree) elif bs.Degree > maxDegree: # degree too high. We need to approximate the curve bs = bs.approximateBSpline(edge_tolerance,maxSegments,maxDegree) # (tolerance, maxSegments, maxDegree) # Generate to a list of bezier curves bezier_list.extend(bs.toBezier()) # Generate to a list of bezier curves, these are of 4 poles for bc in bezier_list: print("%s (degree : %d / nb poles : %d)"%(bc, bc.Degree, bc.NbPoles)) # poles = bc.getPoles() # spline=Part.BSplineCurve() # spline.buildFromPoles(poles, False, 3) #kicadGeo.append(spline) #print(poles) outline.append([ 'spline', bc.getPole(1).x, bc.getPole(1).y, bc.getPole(2).x, bc.getPole(2).y, bc.getPole(3).x, bc.getPole(3).y, bc.getPole(4).x, bc.getPole(4).y, j.Label ]) #print(outline) # elif (use_discretize) and (accept_spline) and ('Parabola' in type(j.Geometry[k]).__name__ or 'Hyperbola' in type(j.Geometry[k]).__name__\ # or 'Ellipse' in type(j.Geometry[k]).__name__): elif (accept_spline) and ('Parabol' in type(Gm[k]).__name__ or 'Hyperbol' in type(Gm[k]).__name__): ## discretizing # or 'Ellipse' in type(j.Geometry[k]).__name__ ## Ellipses are not well approximated by Splines gd = Gm[k] gds = gd.toShape() pl = gds.discretize(QuasiDeflection=dqd) #pl = gds.discretize(QuasiDeflection=1.0) w = Part.makePolygon(pl) wv = w.Vertexes for i in range(0,len(wv), 1): #for v in wv: if i < len(wv)-1: v1x = wv[i].Point.x v1y = wv[i].Point.y v2x = wv[i+1].Point.x v2y = wv[i+1].Point.y elif 'Arc' not in str(gd): v1x = wv[i].Point.x v1y = wv[i].Point.y v2x = wv[0].Point.x v2y = wv[0].Point.y #print('last point',v1x,v1y,v2x,v2y) #i+=2 outline.append([ 'line', v1x, v1y, v2x, v2y, j.Label, ]) #print(v1x,v1y,v2x,v2y,i) # elif (not use_discretize) and (accept_spline) and ('Parabola' in type(j.Geometry[k]).__name__ or 'Hyperbola' in type(j.Gm[k]).__name__\ # or 'Ellipse' in type(j.Geometry[k]).__name__): elif (not use_discretize) and (accept_spline) and ('Ellipse' in type(Gm[k]).__name__): ## toBiArcs # or 'Ellipse' in type(j.Geometry[k]).__name__ ## Ellipses are not well approximated by Splines gk = Gm[k] bs = gk.toBSpline() # (tolerance, maxSegments, maxDegree) gds = bs.toBiArcs(precision) # import kicadStepUptools; import importlib; importlib.reload(kicadStepUptools) #;kicadStepUptools.open(u"C:/Temp/bspline.kicad_pcb") #for g in gds: # print(str(g)) #stop for g in gds: # s = g.toShape() #needed to fix some issue on sketch geometry building # outline.append([ # 'arc', # g.Radius, # s.Edges[0].Curve.Center.x, # s.Edges[0].Curve.Center.y, # g.FirstParameter, # g.LastParameter, # g.Axis[0], # g.Axis[1], # g.Axis[2], # g, # s.Edges[0].Vertexes[0].Point, # s.Edges[0].Vertexes[1].Point, # s.Edges[0].Orientation, # j.Label # ]) # #Part.show(s) outline.append([ 'arc', g.Radius, g.Center.x, g.Center.y, g.FirstParameter, g.LastParameter, g.Axis[0], g.Axis[1], g.Axis[2], g, g.StartPoint, g.EndPoint, 'Forward', j.Label ]) #elif (accept_spline) and ('Parabola' in type(j.Geometry[k]).__name__ or 'Hyperbola' in type(j.Geometry[k]).__name__): # # or 'Ellipse' in type(j.Geometry[k]).__name__ ## Ellipses are not well approximated by Splines # bs = j.Geometry[k] # bs = bs.approximateBSpline(edge_tolerance,maxSegment,maxDegree) # (tolerance, maxSegments, maxDegree) # bezier_list_tmp.extend(bs.toBezier()) # tmpGeo = [] # for bc in bezier_list_tmp: # print("%s (degree : %d / nb poles : %d)"%(bc, bc.Degree, bc.NbPoles)) # poles = bc.getPoles() # spline=Part.BSplineCurve() # spline.buildFromPoles(poles, False, 3) # tmpGeo.append(spline) # print('double rework for Ellipses and Parabola and Hyperbola') # bezier_list_tmp = [] # for g in tmpGeo: # if 'BSplineCurve object' in str(g): # bs = g # if bs.Degree < maxDegree: # bs.increaseDegree(maxDegree) # elif bs.Degree > maxDegree: # # degree too high. We need to approximate the curve # bs = bs.approximateBSpline(edge_tolerance,maxSegments,maxDegree) # (tolerance, maxSegments, maxDegree) # bezier_list_tmp.extend(bs.toBezier()) # Generate to a list of bezier curves, these are of 4 poles # for bc in bezier_list_tmp: # print("%s (degree : %d / nb poles : %d)"%(bc, bc.Degree, bc.NbPoles)) # poles = bc.getPoles() # #spline=Part.BSplineCurve() # #spline.buildFromPoles(poles, False, 3) # outline.append([ # 'spline', # bc.getPole(1).x, # bc.getPole(1).y, # bc.getPole(2).x, # bc.getPole(2).y, # bc.getPole(3).x, # bc.getPole(3).y, # bc.getPole(4).x, # bc.getPole(4).y, # j.Label # ]) else: ## dropped ... it shouldn't arrive here #print j.Geometry[k],'; not supported' to_discretize.append(k) #to_discretize.append(k, j.Label) str_geom=str(Gm[k]) if 'ArcOfEllipse' in str_geom: str_geom='ArcOfEllipse' elif 'ArcOfParabola' in str_geom: str_geom='ArcOfParabola' elif 'ArcOfHyperbola' in str_geom: str_geom='ArcOfHyperbola' if str_geom not in not_supported: if 'Vector' not in str_geom: not_supported=not_supported + str_geom.strip('<').strip('>').strip(' object')+'; ' #continue ##break except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] FreeCAD.Console.PrintWarning('1. ' + str(e) + "\n") FreeCAD.Console.PrintWarning('error class: '+str(exc_type)+'\nfile name: '+str(fname)+'\nerror @line: '+str(exc_tb.tb_lineno)+'\nerror value: '+str(e.args[0])+'\n') # print (to_discretize) # stop return outline, not_supported, to_discretize, construction_geom ## def createEdge(edg,ofs,sklayer=None,pcb_ver=None): global edge_width, maxRadius #print edg k_edg='' if edge_width is None: edge_width=0.16 #def getMinX(self, x): # if x < self.minX: # self.minX = x # #def getMinY(self, y): # if y < self.minY: # self.minY = y if sklayer is None: layer='Edge.Cuts' else: layer = sklayer if edg[0] == 'line': if pcb_ver is None or pcb_ver < 20211014: k_edg = " (gr_line (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (angle 90) (layer {5}) (width {4}))"\ .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edge_width, layer) #k_edg +=os.linesep #.format('{0:.10f}').format(edg[1] + abs(0), '{0:.10f}').format(edg[2] + abs(0), '{0:.10f}').format(edg[3] + abs(0), '{0:.10f}').format(edg[4] + abs(0), 'Edge.Cuts', edge_width) else: k_edg = " (gr_line (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (layer {5}) (width {4}))"\ .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edge_width, layer) elif edg[0] == 'circle': k_edg = " (gr_circle (center {0:.6f} {1:.6f}) (end {2:.6f} {1:.6f}) (layer {4}) (width {3}))".format(edg[2]+ofs[0], -edg[3]+ofs[1], edg[2]+ofs[0]-edg[1], edge_width, layer) #k_edg +=os.linesep #.format( #'{0:.10f}'.format(i[1] + abs(self.minX)), '{0:.10f}'.format(i[2] + abs(self.minY)), '{0:.10f}'.format( # self.addCircle(edg[1:], 'Edge.Cuts', 0.01) elif edg[0] == 'arc': #print edg #print 2*pi #use_rotation=False #if abs(abs(edg[5])-2*pi) <= edge_tolerance: # print '2PI' # use_rotation=True radius = edg[1] #radius xs = edg[2] #center x ys = (edg[3]) * (-1) #center y #mp = DraftGeomUtils.findMidpoint(self.circle.Edges[0]) #if 0: #use_rotation: # sayerr('2PI') # sayerr('check edge orientation!!!') # #eA = edg[4]+pi # #sA = edg[5]+pi # eA = edg[4]-pi/2 # sA = edg[5]-pi/2 #else: sA = edg[4] eA = edg[5] axisX = edg[6] axisY = edg[7] axisZ = edg[8] angle = degrees(sA - eA) # * (-1) # sayerr(angle) ##x1 = radius * cos(sA) + xs ##y1 = (radius * sin(sA)) * (-1) + ys #xs = edg[11][0] #ys = (edg[11][1]) * (-1) ## sA = atan2(edg[10][1]-edg[3], edg[10][0]-edg[2]) ## eA = atan2(edg[11][1]-edg[3], edg[11][0]-edg[2]) # sayerr(edg[12]) x1 = edg[10][0] y1 = (edg[10][1]) * (-1) x2 = edg[11][0] y2 = (edg[11][1]) * (-1) #y1 = (radius * sin(sA)) * + ys #print axisX, axisY,axisZ #print ('coord xs, ys, x1, y1 ', xs,';', ys,';', x1,';', y1,';',angle) #for i,e in enumerate(edg): # print ('param edg['+str(i)+']='+str(edg[i])) #if angle > 180: # angle = 360 - angle #self.getMinX(xs) #self.getMinY(ys) #self.getMinX(x1) #self.getMinY(y1) # Draft.makePoint(xs, -ys, 0) # Draft.makePoint(x1, -y1, 0) # #Draft.makePoint(mp[0],mp[1],mp[2]) # Draft.makePoint(x2, -y2, 0) if abs(xs) > maxRadius or abs(ys) > maxRadius: if pcb_ver is None or pcb_ver < 20211014: k_edg = " (gr_line (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (angle 0) (layer {5}) (width {4}))"\ .format(x1+ofs[0], y1+ofs[1], x2+ofs[0], y2+ofs[1], edge_width, layer) #k_edg = " (gr_line (start {0} {1}) (end {2} {3}) (angle 90) (layer {5}) (width {4}))"\ # .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edge_width, 'Edge.Cuts') #print xs + ofs[0] #stop else: k_edg = " (gr_line (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (layer {5}) (width {4}))"\ .format(x1+ofs[0], y1+ofs[1], x2+ofs[0], y2+ofs[1], edge_width, layer) else: if pcb_ver is None or pcb_ver < 20211014: #self.pcbElem.append(['gr_arc', xs, ys, x1, y1, curve, width, layer]) k_edg = " (gr_arc (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (angle {4:.6f}) (layer {6}) (width {5}))"\ .format(xs+ofs[0], ys+ofs[1], x1+ofs[0], y1+ofs[1], angle, edge_width, layer) else: #stop # print(angle) # ep = rotatePoint(radius,sA,angle,[x1,y1]) # print(ep[0],ep[1]) mp = mid_point(Base.Vector(x1,y1,0),Base.Vector(x2,y2,0),angle) # Draft.makePoint(x1,y1,0) # FreeCAD.ActiveDocument.ActiveObject.ViewObject.PointColor=(1.0,0.0,0.0,0.0) # Draft.makePoint(x2,y2,0) # FreeCAD.ActiveDocument.ActiveObject.ViewObject.PointColor=(0.0,1.0,0.0,0.0) # Draft.makePoint(mp[0],mp[1],0) # FreeCAD.ActiveDocument.ActiveObject.ViewObject.PointColor=(0.0,0.0,1.0,0.0) # Part.show(Part.Edge(Part.Arc(FreeCAD.Base.Vector(x1, y1, 0), FreeCAD.Base.Vector(mp[0],mp[1], 0), FreeCAD.Base.Vector(x2, y2, 0)))) # print(mp[0],mp[1]) k_edg = " (gr_arc (start {0:.6f} {1:.6f}) (mid {2:.6f} {3:.6f}) (end {4:.6f} {5:.6f}) (layer {7}) (width {6}))"\ .format(x2+ofs[0], y2+ofs[1], mp[0]+ofs[0], mp[1]+ofs[1], x1+ofs[0], y1+ofs[1], edge_width, layer) #.format(xs+ofs[0], ys+ofs[1], mp[0]+ofs[0], mp[1]+ofs[1], x1+ofs[0], y1+ofs[1], edge_width, layer) #print(k_edg) #stop # self.addArc(edg[1:], 'Edge.Cuts', 0.01) elif edg[0] == 'spline': k_edg = " (gr_curve (pts (xy {0:.6f} {1:.6f}) (xy {2:.6f} {3:.6f}) (xy {4:.6f} {5:.6f}) (xy {6:.6f} {7:.6f})) (layer {9}) (width {8}))"\ .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edg[5]+ofs[0], -edg[6]+ofs[1], edg[7]+ofs[0], -edg[8]+ofs[1], edge_width, layer) # (pts (xy 151.983691 88.782809) (xy 152.805595 84.674685) (xy 148.40623 80.726614) (xy 144.40069 81.955321)) (layer Edge.Cuts) (width 0.2)) return k_edg ## def createFp(edg,ofs,layer, edge_thick): global edge_width, maxRadius #print edg k_edg='' #if edge_width is None: # edge_width=0.16 #def getMinX(self, x): # if x < self.minX: # self.minX = x # #def getMinY(self, y): # if y < self.minY: # self.minY = y ## writing fp ln='fp_line' ac='fp_arc' cr='fp_circle' if edg[0] == 'line': if 0: #abs(edg[1]+ofs[0])>500 or abs(edg[2]+ofs[1])>500: #print edg stop k_edg = " ("+ln+" (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (angle 90) (layer {5}) (width {4}))"\ .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edge_thick, layer) else: k_edg = " ("+ln+" (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (layer {5}) (width {4}))"\ .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edge_thick, layer) #k_edg +=os.linesep #.format('{0:.10f}').format(edg[1] + abs(0), '{0:.10f}').format(edg[2] + abs(0), '{0:.10f}').format(edg[3] + abs(0), '{0:.10f}').format(edg[4] + abs(0), 'Edge.Cuts', edge_width) elif edg[0] == 'circle': k_edg = " ("+cr+" (center {0:.6f} {1:.6f}) (end {2:.6f} {1:.6f}) (layer {4}) (width {3}))".format(edg[2]+ofs[0], -edg[3]+ofs[1], edg[2]+ofs[0]-edg[1], edge_thick, layer) #k_edg +=os.linesep #.format( #'{0:.10f}'.format(i[1] + abs(self.minX)), '{0:.10f}'.format(i[2] + abs(self.minY)), '{0:.10f}'.format( # self.addCircle(edg[1:], 'Edge.Cuts', 0.01) elif edg[0] == 'arc': #print edg #print 2*pi #use_rotation=False #if abs(abs(edg[5])-2*pi) <= edge_tolerance: # print '2PI' # use_rotation=True radius = edg[1] xs = edg[2] ys = (edg[3]) * (-1) #if 0: #use_rotation: # sayerr('2PI') # sayerr('check edge orientation!!!') # #eA = edg[4]+pi # #sA = edg[5]+pi # eA = edg[4]-pi/2 # sA = edg[5]-pi/2 #else: sA = edg[4] eA = edg[5] axisX = edg[6] axisY = edg[7] axisZ = edg[8] angle = degrees(sA - eA) # * (-1) # sayerr(angle) ##x1 = radius * cos(sA) + xs ##y1 = (radius * sin(sA)) * (-1) + ys #xs = edg[11][0] #ys = (edg[11][1]) * (-1) ## sA = atan2(edg[10][1]-edg[3], edg[10][0]-edg[2]) ## eA = atan2(edg[11][1]-edg[3], edg[11][0]-edg[2]) # sayerr(edg[12]) if 1: #angle ==< 0: x1 = edg[10][0] y1 = (edg[10][1]) * (-1) x2 = edg[11][0] y2 = (edg[11][1]) * (-1) else: x1 = edg[11][0] y1 = (edg[11][1]) * (-1) #y1 = (radius * sin(sA)) * + ys #print axisX, axisY,axisZ #print 'coord xs, ys, x1, y1 ', xs,';', ys,';', x1,';', y1,';',angle #if angle > 180: # angle = 360 - angle #self.getMinX(xs) #self.getMinY(ys) #self.getMinX(x1) #self.getMinY(y1) # Draft.makePoint(xs, -ys, 0) # Draft.makePoint(x1, -y1, 0) # #Draft.makePoint(mp[0],mp[1],mp[2]) # Draft.makePoint(x2, -y2, 0) if abs(xs) > maxRadius or abs(ys) > maxRadius: k_edg = " ("+ln+" (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (layer {5}) (width {4}))"\ .format(x1+ofs[0], y1+ofs[1], x2+ofs[0], y2+ofs[1], edge_thick, layer) #k_edg = " (gr_line (start {0} {1}) (end {2} {3}) (angle 90) (layer {5}) (width {4}))"\ # .format(edg[1]+ofs[0], -edg[2]+ofs[1], edg[3]+ofs[0], -edg[4]+ofs[1], edge_width, 'Edge.Cuts') #print xs + ofs[0] #stop else: #self.pcbElem.append(['gr_arc', xs, ys, x1, y1, curve, width, layer]) k_edg = " ("+ac+" (start {0:.6f} {1:.6f}) (end {2:.6f} {3:.6f}) (angle {4:.6f}) (layer {6}) (width {5}))"\ .format(xs+ofs[0], ys+ofs[1], x1+ofs[0], y1+ofs[1], angle, edge_thick, layer) #.format( # '{0:.10f}'.format(i[1] + abs(self.minX)), '{0:.10f}'.format(i[2] + abs(self.minY)), '{0:.10f}'.format(i[3] + abs(self.minX)), '{0:.10f}'.format(i[4] + abs(self.minY)), i[5], i[6], i[7])) # self.addArc(edg[1:], 'Edge.Cuts', 0.01) return k_edg ## def Discretize(skt_name,delete=None,dqt=None): ##http://forum.freecadweb.org/viewtopic.php?f=12&t=16336#p129468 ##Discretizes the edge and returns a list of points. ##The function accepts keywords as argument: ##discretize(Number=n) => gives a list of 'n' equidistant points ##discretize(QuasiNumber=n) => gives a list of 'n' quasi equidistant points (is faster than the method above) ##discretize(Distance=d) => gives a list of equidistant points with distance 'd' ##discretize(Deflection=d) => gives a list of points with a maximum deflection 'd' to the edge ##discretize(QuasiDeflection=d) => gives a list of points with a maximum deflection 'd' to the edge (faster) ##discretize(Angular=a,Curvature=c,[Minimum=m]) => gives a list of points with an angular deflection of 'a' ##and a curvature deflection of 'c'. Optionally a minimum number of points ##can be set which by default is set to 2. global dvm, dqd, precision # lng=(abs(FreeCAD.ActiveDocument.getObject(skt_name).Shape.BoundBox.XLength)+abs(FreeCAD.ActiveDocument.getObject(skt_name).Shape.BoundBox.YLength)) # dv=int(dvm*lng) #discretize auto setting # #dv=int(0.5*lng) #discretize auto setting COARSE for testing # #print dv # if dv < 20: # dv=20 b=FreeCAD.ActiveDocument.getObject(skt_name) shp1=b.Shape.copy() #e = shp1.Edges[0].Curve #skList=[] newShapeList = [] newShapes = [] for e in shp1.Edges: ##e = shp1.Edges[0] #.Curve #sayerr(e.Curve) #print DraftGeomUtils.geomType(e) #if isinstance(e.Curve,Part.BSplineCurve): # sayerr('geomType BSP') #Part.show(shp1) if isinstance(e.Curve,Part.BSplineCurve): say('found BSpline') edges = [] arcs = e.Curve.toBiArcs(precision) #print arcs for i in arcs: edges.append(Part.Edge(i)) w = Part.Wire([Part.Edge(i) for i in edges]) Part.show(w) w_name=FreeCAD.ActiveDocument.ActiveObject.Name newShapeList.append(w_name) wn=FreeCAD.ActiveDocument.getObject(w_name) newShapes.append(wn) # elif isinstance(e.Curve,Part.Ellipse): # say('found Ellipse') else: #ellipses or other curves say('found Ellipse') #l=b.Shape.copy().discretize(dv) #l=b.Shape.copy().discretize(QuasiDeflection=0.02) w = Part.Wire(e) Part.show(w) w_name=FreeCAD.ActiveDocument.ActiveObject.Name #newShapeList.append(w_name) wn=FreeCAD.ActiveDocument.getObject(w_name) #newShapes.append(wn) if dqt is None: l=wn.Shape.copy().discretize(QuasiDeflection=dqd) else: l=wn.Shape.copy().discretize(QuasiDeflection=dqt) #l=b.Shape.copy().discretize(QuasiDeflection=dqd) f=Part.makePolygon(l) Part.show(f) sh_name=FreeCAD.ActiveDocument.ActiveObject.Name newShapeList.append(sh_name) newShapes.append(f) FreeCAD.ActiveDocument.removeObject(w_name) FreeCAD.ActiveDocument.recompute() ## sketch = Draft.makeSketch(newShapes[0],autoconstraints=True) sketch = Draft.makeSketch(FreeCAD.ActiveDocument.getObject(newShapeList[0]),autoconstraints=True) #FreeCAD.ActiveDocument.ActiveObject.Label="Sketch_dxf" sname=FreeCAD.ActiveDocument.ActiveObject.Name for w in newShapes[1:]: Draft.makeSketch([w],addTo=sketch) geom=[] ## recreating a correct geometry for i in sketch.Geometry: if isinstance(i,Part.ArcOfCircle) and i.XAxis.x < 0: arc=Part.ArcOfCircle(i.Circle,i.FirstParameter+pi,i.LastParameter+pi) arc.XAxis.x = -arc.XAxis.x geom.append(arc) else: geom.append(i) tsk= FreeCAD.activeDocument().addObject('Sketcher::SketchObject','Sketch_result') tsk.addGeometry(geom) tsk.Placement=FreeCAD.ActiveDocument.getObject(sname).Placement FreeCAD.ActiveDocument.removeObject(sname) #print tsk.Geometry ##for w in newShapes[1:]: ## Draft.makeSketch([w],addTo=sketch) #stop #for wire in wires: # FreeCAD.ActiveDocument.removeObject(wire.Name) for wnm in newShapeList: FreeCAD.ActiveDocument.removeObject(wnm) if delete is None: FreeCAD.ActiveDocument.removeObject(skt_name) FreeCAD.ActiveDocument.recompute() s_name=tsk.Name #else: #ellipses # #l=b.Shape.copy().discretize(dv) # #l=b.Shape.copy().discretize(QuasiDeflection=0.02) # l=b.Shape.copy().discretize(QuasiDeflection=dqd) # f=Part.makePolygon(l) # Part.show(f) # sh_name=FreeCAD.ActiveDocument.ActiveObject.Name # FreeCAD.ActiveDocument.recompute() # Draft.makeSketch(FreeCAD.ActiveDocument.getObject(sh_name),autoconstraints=True) # s_name=FreeCAD.ActiveDocument.ActiveObject.Name # FreeCAD.ActiveDocument.removeObject(sh_name) # FreeCAD.ActiveDocument.removeObject(skt_name) # FreeCAD.ActiveDocument.recompute() return s_name #stop ## def remove_basic_geom(c_name, to_disc): s=FreeCAD.ActiveDocument.getObject(c_name) geoL=len(FreeCAD.ActiveDocument.getObject(c_name).Geometry) #print 'to discretize' #print to_disc to_disc_str=[] for j in range (len(to_disc)): to_disc_str.append(str(to_disc[j])) #print to_disc_str #print 'geo',' ', App.ActiveDocument.getObject(c_name).Geometry #print 'oldgeo',' ', App.ActiveDocument.getObject("PCB_Sketch").Geometry #stop #print 'removing' #for i in range (geoL-1,0,-1): for i in range (geoL-1,-1,-1): #print 's.geom' #if str(s.Geometry[i-1]) in to_disc_str: # print 'found geo to disc' #else: #print str(s.Geometry[i]), ';;' if str(s.Geometry[i]) not in to_disc_str: if hasattr(s,'GeometryFacadeList'): Gm = s.GeometryFacadeList else: Gm = s.Geometry #if hasattr(Gm[i],'Construction'): # if not Gm[i].Construction: if isConstruction(Gm[i]): #print FreeCAD.ActiveDocument.getObject(c_name).Geometry[i] FreeCAD.ActiveDocument.getObject(c_name).delGeometry(i) FreeCAD.ActiveDocument.recompute() #stop #if i not in #App.ActiveDocument.getObject("PCB_Sketch").delGeometry(0, 3, 4, 10, 11, 12) ## def split_basic_geom(c_name, to_disc): s=FreeCAD.ActiveDocument.getObject(c_name) geoL=len(FreeCAD.ActiveDocument.getObject(c_name).Geometry) geoB = [] #print 'to discretize' #print to_disc to_disc_str=[] for j in range (len(to_disc)): to_disc_str.append(str(to_disc[j])) #print to_disc_str #print 'geo',' ', App.ActiveDocument.getObject(c_name).Geometry #print 'oldgeo',' ', App.ActiveDocument.getObject("PCB_Sketch").Geometry #stop #print 'removing' #for i in range (geoL-1,0,-1): for i in range (geoL-1,-1,-1): #print 's.geom' #if str(s.Geometry[i-1]) in to_disc_str: # print 'found geo to disc' #else: #print str(s.Geometry[i]), ';;' if str(s.Geometry[i]) not in to_disc_str: if hasattr(s,'GeometryFacadeList'): Gm = s.GeometryFacadeList else: Gm = s.Geometry #if hasattr(Gm[i],'Construction'): # if not Gm[i].Construction: if isConstruction(Gm[i]): #print FreeCAD.ActiveDocument.getObject(c_name).Geometry[i] geoB.append(FreeCAD.ActiveDocument.getObject(c_name).Geometry[i]) FreeCAD.ActiveDocument.getObject(c_name).delGeometry(i) FreeCAD.ActiveDocument.recompute() #stop #if i not in #App.ActiveDocument.getObject("PCB_Sketch").delGeometry(0, 3, 4, 10, 11, 12) return geoB ## def check_geom(sk_name, ofs=None): if ofs is None: ofs=[0,0] j=FreeCAD.ActiveDocument.getObject(sk_name) to_discretize=[] outline=[] foundBSP=False foundElly=False geo_all='' for k in range(len(j.Geometry)): foundGeo=False #print(type(j.Geometry[k]).__name__) if hasattr(j,'GeometryFacadeList'): Gm = j.GeometryFacadeList else: Gm = j.Geometry #if hasattr(Gm[k],'Construction'): if isConstruction(Gm[k]): sayw('construnction skipped') #if Gm[k].Construction: #sayerr('construction skipped') continue if 'Point' in type(j.Geometry[k]).__name__: #skipping points sayw('point skipped') continue if 'LineSegment' in type(j.Geometry[k]).__name__: #if 'Line' in type(j.Geometry[k]).__name__: sk_ge=j.Geometry[k].toShape() #needed to fix some issue on sketch geometry building outline.append([ 'line', sk_ge.Edges[0].Vertexes[0].Point.x+ofs[0], sk_ge.Edges[0].Vertexes[0].Point.y+ofs[1], sk_ge.Edges[0].Vertexes[1].Point.x+ofs[0], sk_ge.Edges[0].Vertexes[1].Point.y+ofs[1], j.Label ]) # outline.append([ # 'line', # j.Geometry[k].StartPoint.x+ofs[0], # j.Geometry[k].StartPoint.y+ofs[1], # j.Geometry[k].EndPoint.x+ofs[0], # j.Geometry[k].EndPoint.y+ofs[1] # ]) #elif type(j.Geometry[k]).__name__ == 'Circle': elif 'Circle' in type(j.Geometry[k]).__name__ and not 'ArcOfCircle' in type(j.Geometry[k]).__name__: sk_ge=j.Geometry[k].toShape() #needed to fix some issue on sketch geometry building outline.append([ 'circle', sk_ge.Edges[0].Curve.Radius, sk_ge.Edges[0].Curve.Center.x+ofs[0], sk_ge.Edges[0].Curve.Center.y+ofs[1], j.Label ]) #outline.append([ # 'circle', # j.Geometry[k].Radius, # j.Geometry[k].Center.x+ofs[0], # j.Geometry[k].Center.y+ofs[1] #]) #elif type(j.Geometry[k]).__name__ == 'ArcOfCircle': elif 'ArcOfCircle' in type(j.Geometry[k]).__name__: #outline.append([ # 'arc', # j.Geometry[k].Radius, # j.Geometry[k].Center.x+ofs[0], # j.Geometry[k].Center.y+ofs[1], # j.Geometry[k].FirstParameter+ofs[0], # j.Geometry[k].LastParameter+ofs[1], # j.Geometry[k].Axis[0], # j.Geometry[k].Axis[1], # j.Geometry[k].Axis[2], # j.Geometry[k], # j.Shape.Edges[k].Vertexes[0].Point, # j.Shape.Edges[k].Vertexes[1].Point, # j.Shape.Edges[k].Orientation #]) sk_ge=j.Geometry[k].toShape() #needed to fix some issue on sketch geometry building outline.append([ 'arc', j.Geometry[k].Radius, sk_ge.Edges[0].Curve.Center.x+ofs[0], sk_ge.Edges[0].Curve.Center.y+ofs[1], j.Geometry[k].FirstParameter+ofs[0], j.Geometry[k].LastParameter+ofs[1], j.Geometry[k].Axis[0], j.Geometry[k].Axis[1], j.Geometry[k].Axis[2], j.Geometry[k], sk_ge.Edges[0].Vertexes[0].Point, sk_ge.Edges[0].Vertexes[1].Point, sk_ge.Edges[0].Orientation, j.Label ]) ## maxRadius=3500 ## sayerr(j.Geometry[k].Radius) ## stop ##if j.Geometry[k].Radius > maxRadius: ## sayerr(j.Geometry[k].Radius) # i=j.Geometry[k] # sayerr('Xaxis2a') # if 0: #i.XAxis.x < 0: #da cambiare this is not available on FC0.16 # sayerr('Xaxis2b') # outline.append([ # 'arc', # i.Radius, # i.Center.x, # i.Center.y, # i.LastParameter+pi, # i.FirstParameter+pi, # -i.Axis[0], # i.Axis[1], # i.Axis[2], # i # ]) # else: # outline.append([ # 'arc', # i.Radius, # i.Center.x, # i.Center.y, # i.LastParameter, # i.FirstParameter+pi, # i.Axis[0], # i.Axis[1], # i.Axis[2], # i # ]) else: #print j.Geometry[k],'; not supported' to_discretize.append(j.Geometry[k]) #to_discretize.append(k) str_geom=str(j.Geometry[k]) if 'ArcOfEllipse' in str_geom: str_geom='ArcOfEllipse' elif 'ArcOfParabola' in str_geom: str_geom='ArcOfParabola' elif 'ArcOfHyperbola' in str_geom: str_geom='ArcOfHyperbola' #continue ##break #print j.Geometry[k],'; not supported' #else: # str_geom=str(j.Geometry[k]) # if 'ArcOfEllipse' in str_geom: # str_geom='ArcOfEllipse' # foundGeom=True;foundBSP==False;foundElly==False # elif 'ArcOfParabola' in str_geom: # str_geom='ArcOfParabola' # foundGeom=True;foundBSP==False;foundElly==False # elif 'ArcOfHyperbola' in str_geom: # str_geom='ArcOfHyperbola' # foundGeom=True;foundBSP==False;foundElly==False # elif 'Circle' in str_geom: # foundGeom=True;foundBSP==False;foundElly==False # elif 'Line' in str_geom: # foundGeom=True;foundBSP==False;foundElly==False # if 'Vector' in str_geom: # if foundBSP==True and foundGeom==False or foundElly==True and foundGeom==False: # to_discretize.append(j.Geometry[k]) # #continue ##break #print to_discretize #stop return outline, to_discretize ## ## getGridOrigin def getGridOrigin(dt): match = re.search(r'\(grid_origin (.+?) (.+?)\)', dt, re.MULTILINE|re.DOTALL) if match is not None: return [float(match.group(1)), float(match.group(2))]; else: #returning default top left corner value return [0.0,0.0] ## ## getAuxOrigin def getAuxOrigin(dt): match = re.search(r'\(aux_axis_origin (.+?) (.+?)\)', dt, re.MULTILINE|re.DOTALL) if match is not None: return [float(match.group(1)), float(match.group(2))]; else: # #returning default top left corner value # return [0.0,0.0] return None ## def export_pcb(fname=None,sklayer=None,skname=None): global last_fp_path, test_flag, start_time global configParser, configFilePath, start_time global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global pcb_path, use_AppPart, force_oldGroups, use_Links, use_LinkGroups global original_filename, aux_orig, grid_orig global off_x, off_y, maxRadius global zfit, edge_width import fcad_parser from fcad_parser import KicadPCB,SexpList import kicad_parser sayw('exporting new pcb edges') doc=FreeCAD.ActiveDocument #filePath=last_pcb_path #fpath=filePath+os.sep+doc.Label+'.kicad_pcb' #sayerr('to '+fpath) #print fname if fname is None: fpath=original_filename else: fpath=fname sayerr('saving to '+fpath) #stop if len(fpath) > 0: #new_edge_list=getBoardOutline() #say (new_edge_list) cfg_read_all() path, fname = os.path.split(fpath) name=os.path.splitext(fname)[0] ext=os.path.splitext(fname)[1] fpth = os.path.dirname(os.path.abspath(fpath)) #filePath = os.path.split(os.path.realpath(__file__))[0] say ('my file path '+fpth) # stop if fpth == "": fpth = "." last_pcb_path = fpth last_pcb_path = re.sub("\\\\", "/", last_pcb_path) ini_vars[10] = last_pcb_path #cfg_update_all() pg = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/kicadStepUp") pg.SetString("last_pcb_path", make_string(last_pcb_path)) #sayerr(name+':'+ext) with codecs.open(fpath,'r', encoding='utf-8') as txtFile: content = txtFile.readlines() # problems? content.append(u" ") txtFile.close() data=u''.join(content) tml= time.localtime() now=str(tml.tm_year)+'-'+str(tml.tm_mon)+'-'+str(tml.tm_mday)+'-'+str(tml.tm_hour)+'.'+str(tml.tm_min)+'.'+str(tml.tm_sec) #foname=os.path.join(path, name+'-bkp-'+now+ext+'-bak') foname=os.path.join(path, name+u'-bkp-'+make_unicode(now)+ext+u'-bak') pcb_push=True testing=False if testing is not True: try: #with codecs.open(foname,'w', encoding='utf-8') as ofile: # ofile.write(data) # ofile.close() copyfile(fpath, foname) say('file copied') except: msg="""problem in writing permissions to kicad board!

""" msg+="file saving aborted to
"+fpath+"


" msgr="problem in writing permissions to kicad board!\n" msgr+="file saving aborted to "+fpath+"\n" pcb_push=False say(msgr) say_info(msg) if pcb_push==True: if sklayer is None: ssklayer = 'Edge' else: ssklayer = sklayer.split('.')[0] if 'KeepOutZone' in sklayer: ssklayer = 'KeepOutZone' elif 'FillZone' in sklayer: ssklayer = 'FillZone' elif 'MaskZone' in sklayer: ssklayer = 'MaskZone' print (ssklayer) edge_pcb_exists=False mypcb = KicadPCB.load(fpath) pcb_version = mypcb.version sayw('parsing, pcb v='+str(pcb_version)) edg_segms = 0 for ln in mypcb.gr_line: if ssklayer in ln.layer: #say(ln.layer) edg_segms+=1 break if edg_segms == 0: for ar in mypcb.gr_arc: if ssklayer in ar.layer: #say(ln.layer) edg_segms+=1 break if edg_segms == 0: for cr in mypcb.gr_circle: if ssklayer in cr.layer: #say(ln.layer) edg_segms+=1 break if edg_segms == 0: for lp in mypcb.gr_poly: #print(lp) #print(lp.layer) #print(lp.pts) if ssklayer in lp.layer: #sayerr(lp.layer) try: for p in lp.pts.xy: edg_segms+=1 break #sayerr(p) except: for a in lp.pts.arc: edg_segms+=1 break #stop #edg_segms+=1 if edg_segms == 0: for bs in mypcb.gr_curve: if ssklayer in bs.layer: #sayerr(bs.layer) for p in bs.pts.xy: edg_segms+=1 break #edg_segms+=1 #sayw(str(edg_segms)+' '+ssklayer+' segments') if (edg_segms)>0: edge_pcb_exists=True sayw('found '+ssklayer+' element(s)') #stop if ssklayer == 'Edge': if hasattr(mypcb, 'setup'): if hasattr(mypcb.setup, 'edge_width'): #maui edge width edge_width=mypcb.setup.edge_width elif hasattr(mypcb.setup, 'edge_cuts_line_width'): #maui edge cuts new width k 5.99 edge_width=mypcb.setup.edge_cuts_line_width #else: # edge_width=0.16 oft=None origin_warn = False #skip = False if aux_orig == 1: oft=getAuxOrigin(data) if oft is None: msg="""StepUp is configured for \'aux origin\' reference
but \'aux origin\' is not placed/set on kicad destination board""" say_warning(msg) stop else: print ('aux_origin found',oft) elif grid_orig == 1: oft=getGridOrigin(data) if oft is None: msg="""StepUp is configured for \'grid origin\' reference
but \'grid origin\' is not placed/set on kicad destination board""" say_warning(msg) stop else: if oft == [0.0,0.0]: origin_warn = True print ('grid_origin found',oft) else: print('using an approximate PCB center as sketch reference point') #print oft gof=False if oft is not None: off_x=oft[0];off_y=-oft[1] offset = oft gof=True if edge_pcb_exists and ssklayer != 'FillZone' and ssklayer != 'KeepOutZone': #offset=[0,0] doc=FreeCAD.ActiveDocument ksu_found=False;skt_name='';pcb_found=False for obj in doc.Objects: if ("PCB_Sketch" in obj.Name) or ("PCB_Sketch" in obj.Label) or\ ("Edge_Sketch" in obj.Name) or ("Edge.Cuts" in obj.Label) or \ ("Dwgs_Sketch" in obj.Name) or ("Dwgs.User" in obj.Label) or \ ("Eco1_Sketch" in obj.Name) or ("Eco1.User" in obj.Label) or \ ("Eco2_Sketch" in obj.Name) or ("Eco2.User" in obj.Label) or \ ("Cmts_Sketch" in obj.Name) or ("Cmts.User" in obj.Label) or \ ("Margin_Sketch" in obj.Name) or ("Margin" in obj.Label) or ("User." in obj.Label): ksu_found=True skt_name=obj.Name if ("Pcb" in obj.Name): ksu_found=True pcb_found=True testing=False if testing is True: off_x=0;off_y=0 if ksu_found==True or testing==True: if pcb_found==True: #FreeCAD.ActiveDocument.getObject(skname).recompute(True) #bbpx=-FreeCAD.ActiveDocument.getObject('Pcb').Placement.Base[0]+FreeCAD.ActiveDocument.getObject(skt_name).Placement.Base[0] #bbpy=FreeCAD.ActiveDocument.getObject('Pcb').Placement.Base[1]-FreeCAD.ActiveDocument.getObject(skt_name).Placement.Base[1] bbpx=-FreeCAD.ActiveDocument.getObject(skname).Placement.Base[0]+FreeCAD.ActiveDocument.getObject(skt_name).Placement.Base[0] bbpy=FreeCAD.ActiveDocument.getObject(skname).Placement.Base[1]-FreeCAD.ActiveDocument.getObject(skt_name).Placement.Base[1] offset=[bbpx,bbpy] else: off_x=0;off_y=0 offset=[off_x,-off_y] if gof and grid_orig==1: offset=[off_x,-off_y] offset = (getGridOrigin(data)[0],getGridOrigin(data)[1]) elif gof and aux_orig==1: offset=[off_x,-off_y] offset = (getAuxOrigin(data)[0],getAuxOrigin(data)[1]) #if gof and not pcb_found: # offset=[0,0] ## maui to test position #print offset #say(offset) #stop if ssklayer == 'Edge': say('pcb edge exists') sayw('removing old pcb '+ssklayer) else: sayw('removing existing drawings '+ssklayer) ## removing old Edge w/ better parsing kv8 compat clr_content = u"" l = len (content) id = 0 add_line = True while id < l: # line in enumerate(content): line = content[id] if '(gr_line ' in line or '(gr_curve' in line or '(gr_arc' in line or '(gr_circle' in line or '(gr_rect' in line or '(gr_poly' in line: #print(line,id) if ssklayer in line: #print('removed',id,line) add_line = False id += 1 elif id+1 to push a new release of Edge to a kicad board
with an existing Edge
you need to load the board with StepUp first

""") stop else: #[148.5, -98.5] center of A4 page if gof and grid_orig==1: sayw('pcb edge does not exist, aligning sketch to Grid Origin') offset=[off_x,-off_y] elif gof and aux_orig==1: sayw('pcb edge does not exist, aligning sketch to Aux Origin') offset=[off_x,-off_y] else: sayw('pcb edge does not exist, aligning sketch to center of A4 page') offset=[148.5,98.5] ##sel = FreeCADGui.Selection.getSelection() ##if len (sel) >0: ## #sayw(doc.Name) ## for j in sel: ## if "Sketch" in j.TypeId: ## #FreeCAD.ActiveDocument.getObject(j.Name).Placement = FreeCAD.Placement(FreeCAD.Vector(148.5, -98.5,0),FreeCAD.Rotation(FreeCAD.Vector(0,0,1),0)) ## if FreeCAD.ActiveDocument.getObject(j.Name).Placement.Base[0]==0: #sketch created at FC origin ## FreeCAD.ActiveDocument.getObject(j.Name).Placement = FreeCAD.Placement(FreeCAD.Vector(148.5,-98.5,0), App.Rotation(0,0,0), App.Vector(0,0,0)) #[Pos=(-148.5,98,5), Yaw-Pitch-Roll=(0,0,0)] #shift_sketch(j.Name,offset) #sel = FreeCADGui.Selection.getSelection() #if len (sel) >0: # sel[0].recompute(True) FreeCAD.ActiveDocument.recompute() if (zfit): FreeCADGui.SendMsgToActiveView("ViewFit") k = data.rfind(")") #removing latest ')' newcontent = data[:k] to_discretize = []; not_supported = [] if ssklayer != 'FillZone' and ssklayer != 'MaskZone' and ssklayer != 'KeepOutZone': new_edge_list, not_supported, to_discretize, construction_geom = getBoardOutline() #print (new_edge_list) #stop #geoL=len(App.ActiveDocument.getObject("PCB_Sketch").Geometry) if len(to_discretize)>0: #stop sel = FreeCADGui.Selection.getSelection() if len (sel)==1: sk_name=sel[0].Name t_name=cpy_sketch(sk_name) ###t_sk=FreeCAD.ActiveDocument.copyObject(FreeCAD.ActiveDocument.getObject(sk_name)) elist, to_dis=check_geom(t_name) #Draft.clone(FreeCAD.ActiveDocument.getObject(sk_name),copy=True) #clone_name=App.ActiveDocument.ActiveObject.Name remove_basic_geom(t_name, to_dis) ##remove_basic_geom(t_sk.Name, to_discretize) ##elist, to_dis=check_geom(t_sk.Name) #print elist #stop obj_list_prev=[] for obj in doc.Objects: #print obj.TypeId if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject"): obj_list_prev.append(obj.Name) #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=True) #Draft.draftify(FreeCAD.ActiveDocument.getObject(t_name),delete=False) b=FreeCAD.ActiveDocument.getObject(t_name) shp1=b.Shape.copy() Part.show(shp1) FreeCAD.ActiveDocument.removeObject(t_name) FreeCAD.ActiveDocument.recompute() #stop obj_list_after=[] for obj in doc.Objects: if (obj.TypeId=="Part::Feature") or (obj.TypeId=="Sketcher::SketchObject")\ or (obj.TypeId=="Part::Part2DObjectPython"): if obj.Name not in obj_list_prev: obj_list_after.append(obj.Name) #print obj_list_after #, obj_list_prev sk_to_conv=[] for obj in doc.Objects: if obj.Name in obj_list_after: if (obj.TypeId=="Part::Part2DObjectPython"): FreeCAD.ActiveDocument.removeObject(obj.Name) FreeCAD.ActiveDocument.recompute() else: sk_to_conv.append(obj.Name) keep_sketch_converted=False #False for s in sk_to_conv: #sayerr(s) ## ns=Discretize(s) offset1=[-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[0],-FreeCAD.ActiveDocument.getObject(sk_name).Placement.Base[1]] elist, to_dis=check_geom(ns,offset1) new_edge_list=new_edge_list+elist if not keep_sketch_converted: FreeCAD.ActiveDocument.removeObject(ns) FreeCAD.ActiveDocument.recompute() #print new_edge_list #stop #if len (not_supported)>0: # Draft.downgrade(FreeCADGui.Selection.getSelection(),delete=False) # stop #say (new_edge_list) #stop #sayerr(replace) #replace = re.sub('\s\(gr_arc(.+?Edge)\)\)\r\n|\(gr_line(.+?Edge)\)\)\r|\(gr_line(.+?Edge)\)\)\n','',replace, flags=re.MULTILINE) #replace = re.sub('\s\(gr_circle(.+?Edge)\)\)\r\n|\(gr_line(.+?Edge)\)\)\r|\(gr_line(.+?Edge)\)\)\n','',replace, flags=re.MULTILINE) #newcontent = re.sub('^\)','ENDOFFILE',replace, flags=re.MULTILINE) #newcontent = re.sub('^\)','',replace, flags=re.MULTILINE) #end of file #newcontent = re.sub('/\.(?=[^\(]*$)/','',replace, flags=re.MULTILINE) #end of file #newcontent = newcontent.replace(/\((?=[^.]*$)/, "") #newcontent = re.sub(r'(.*)\)', r'', replace, flags=re.MULTILINE) new_border='' #print new_edge_list ## maxRadius # 4000 = 4m max length for KiCad #edge_nbr=0 sanitized_edge_list=[] for border in new_edge_list: #print (border) # [0] if 'arc' in border[0]: #print border[0] if abs(float(border[3])) > maxRadius: #stop #print 'too big radius= ',border[3] #print 'border len= ', len(border) #points=border [10].x #p1x = float(border [10].x);p1y=float(border [10].y) p1x = float("{0:.6f}".format(border [10].x));p1y=float("{0:.6f}".format(border [10].y)) #print p1x, ' ',p1y #p2x = float(border [11].x);p2y=float(border [11].y) p2x = float("{0:.6f}".format(border [11].x));p2y=float("{0:.6f}".format(border [11].y)) #print '1st point ', border [10],' 2nd point ', border [11] sanitized_edge_list.append(['line',p1x,p1y,p2x,p2y]) else: sanitized_edge_list.append(border) else: sanitized_edge_list.append(border) #edge_nbr=edge_nbr+1 #print sanitized_edge_list #stop #for border in new_edge_list: for border in sanitized_edge_list: new_border=new_border+os.linesep+createEdge(border,offset,sklayer,pcb_version) #sayw(createEdge(border)) #stop new_edge=new_border+os.linesep+')'+os.linesep # print(new_edge) newcontent=newcontent+new_edge+u' ' elif ssklayer == 'FillZone' or ssklayer == 'MaskZone': newcontent=newcontent+pushFillZone(skname,offset,sklayer)+os.linesep+')'+os.linesep+u' ' else: newcontent=newcontent+pushFillZone(skname,offset,sklayer)+os.linesep+')'+os.linesep+u' ' #print newcontent with codecs.open(fpath,'w', encoding='utf-8') as ofile: ofile.write(newcontent) ofile.close() say_time() if ssklayer != 'FillZone' and ssklayer != 'MaskZone' and ssklayer != 'KeepOutZone': msg="""new Edge pushed to kicad board!


""" elif ssklayer == 'FillZone': msg="""new FillZone pushed to kicad board!
Edit the properties of the new FillZone in pcbnew
""" elif ssklayer == 'MaskZone': msg="""new MaskZone pushed to kicad board!
Edit the properties of the new FillZone in pcbnew
""" elif ssklayer == 'KeepOutZone': msg="""new KeepOutZone pushed to kicad board!
Edit the properties of the new KeepOutZone in pcbnew
""" msg+="file saved to
"+fpath+"


" msg+="backup file saved to
"+foname+"

" if ssklayer == 'Edge': msgr="new Edge pushed to kicad board!\n" else: msgr="new "+ssklayer+" pushed to kicad board!\n" msgr+="file saved to "+fpath+"\n" msgr+="backup file saved to "+foname lns=len (not_supported) #print lns if lns > 2: if lns < 103: # writing only some geometry not supported msg+="
found downgraded Geometry:
"+not_supported[:-2]+"!
" msgr+="\nfound downgraded Geometry: "+not_supported[:-2]+"!" else: nss=not_supported[:-2] nss=nss[:101]+'...
...' msg+="
found downgraded Geometry:
"+nss+"
" msgr+="\nfound downgraded Geometry: "+not_supported[:-2]+"!" say(msgr) say_info(msg) if not edge_pcb_exists and ssklayer != 'FillZone' and ssklayer != 'KeepOutZone': msg="close your FC Sketch
and reload the kicad_pcb file
" say_warning(msg) if origin_warn: if aux_orig == 1: origin_msg='AuxOrigin' elif grid_orig == 1: origin_msg='GridOrigin' msg = origin_msg +' is set in FC Preferences but not set in KiCAD pcbnew file' sayw(msg) msg=""""""+origin_msg+""" is set in FreeCAD Preferences
but not set in KiCAD pcbnew file
""" msg+="""

Please assign """+origin_msg+""" to your KiCAD pcbnew board file""" msg+="""
for a better Mechanical integration""" say_warning(msg) #def precision(self, value): # return "%.2f" % float(value) ## def find_sequence (elist,idx, min_dist): """find point sequence in two edges""" ep0 = elist[idx].Vertexes[0].Point ep1 = elist[idx].Vertexes[1].Point ep2 = elist[idx+1].Vertexes[0].Point ep3 = elist[idx+1].Vertexes[1].Point if distance(ep0,ep2) < min_dist: first_pnt = ep1 common_pnt = ep0 last_pnt = ep3 elif distance(ep0,ep3) < min_dist: first_pnt = ep1 common_pnt = ep3 last_pnt = ep2 elif distance(ep1,ep3) < min_dist: first_pnt = ep0 common_pnt = ep1 last_pnt = ep2 elif distance(ep1,ep2) < min_dist: first_pnt = ep0 common_pnt = ep1 last_pnt = ep3 else: msg="""to push a FillZone or a KeepOutZone
you need a single closed Sketch!
""" say_warning(msg) stop return first_pnt, common_pnt, last_pnt ## def pushFillZone(skn, ofs, keepout=None): shapes = [] q_deflection = 0.005 #0.02 ##0.005 tol = 0.01 constr = 'coincident' #'all' if skn is None: sel = FreeCADGui.Selection.getSelection() for selobj in sel: for e in selobj.Shape.Edges: shapes.append(Part.makePolygon(e.discretize(QuasiDeflection=q_deflection))) else: selobj = FreeCAD.ActiveDocument.getObject(skn) for e in selobj.Shape.Edges: shapes.append(Part.makePolygon(e.discretize(QuasiDeflection=q_deflection))) Draft.makeSketch(shapes) sk_d = FreeCAD.ActiveDocument.ActiveObject if sk_d is not None: FreeCADGui.ActiveDocument.getObject(sk_d.Name).LineColor = (1.00,1.00,1.00) FreeCADGui.ActiveDocument.getObject(sk_d.Name).PointColor = (1.00,1.00,1.00) max_geo_admitted = 1500 # after this number, no recompute is applied if len (sk_d.Geometry) < max_geo_admitted: FreeCAD.ActiveDocument.recompute() import constrainator constrainator.add_constraints(sk_d.Name, tol, constr) skt = FreeCAD.ActiveDocument.getObject(sk_d.Name) if hasattr(skt, 'OpenVertices'): openVtxs = skt.OpenVertices if len(openVtxs) >0: FreeCAD.Console.PrintError("Open Vertexes found.\n") FreeCAD.Console.PrintWarning(str(openVtxs)+'\n') msg = """Open Vertexes found.
"""+str(openVtxs) reply = QtGui.QMessageBox.information(None,"info", msg) add_points=True if add_points: for v in openVtxs: FreeCAD.ActiveDocument.addObject('PartDesign::Point','DatumPoint') dp = FreeCAD.ActiveDocument.ActiveObject dp.Placement = FreeCAD.Placement (FreeCAD.Vector(v[0],v[1],0), FreeCAD.Rotation(0,0,0), FreeCAD.Vector(0,0,0)) dp.Label = 'OpenVertexPointer' #FreeCADGui.ActiveDocument.getObject(sel[0].Name).Visibility=False shp = skt.Shape #ofs=[0.0,0.0] edge_width = 0.1 edges = shp.Edges segments_nbr=len(edges) if segments_nbr<3: stop if 'Fill' in keepout: fillzone = """ (zone (net 0) (net_name "") (layer """+keepout[:1]+""".Cu) (tstamp 0) (hatch edge 0.508)"""+os.linesep fillzone+=""" (connect_pads (clearance 0.508))"""+os.linesep fillzone+=""" (min_thickness 0.254)"""+os.linesep fillzone+=""" (fill yes (arc_segments 32) (thermal_gap 0.508) (thermal_bridge_width 0.508))"""+os.linesep fillzone+=""" (polygon"""+os.linesep elif 'Mask' in keepout: fillzone = """ (gr_poly"""+os.linesep elif 'KeepOut' in keepout: #keepout zone fillzone = """ (zone (net 0) (net_name "") (layers """+keepout[:1]+""".Cu) (tstamp 0) (hatch edge 0.508)"""+os.linesep fillzone+=""" (connect_pads (clearance 0.508))"""+os.linesep fillzone+=""" (min_thickness 0.254)"""+os.linesep fillzone+=""" (keepout (tracks not_allowed) (vias not_allowed) (copperpour not_allowed))"""+os.linesep fillzone+=""" (fill (arc_segments 32) (thermal_gap 0.508) (thermal_bridge_width 0.508))"""+os.linesep fillzone+=""" (polygon"""+os.linesep i=0 pts = " (pts "+os.linesep first_pnt, common_pnt, last_pnt = find_sequence (edges,i,tol) i+=1 for e in edges[1:-1]: if 'Line' not in str(e.Curve): stop first_pnt, common_pnt, last_pnt = find_sequence (edges,i,tol) if i < segments_nbr: pts=pts+" (xy {0:.3f} {1:.3f}) (xy {2:.3f} {3:.3f})".format(first_pnt.x+ofs[0], -first_pnt.y+ofs[1], common_pnt.x+ofs[0], -common_pnt.y+ofs[1])+os.linesep i=i+1 pts=pts+" (xy {0:.3f} {1:.3f})".format(last_pnt.x+ofs[0], -last_pnt.y+ofs[1])+os.linesep fillzone += pts if 'Mask' in keepout: fillzone+=" )"+os.linesep+" (layer "+keepout[:6]+") (width 0.0))"+os.linesep else: fillzone+=" )"+os.linesep+" )"+os.linesep+" )"+os.linesep #+")"+os.linesep if sk_d is not None: FreeCAD.ActiveDocument.removeObject(sk_d.Name) #print(fillzone) #FreeCAD.ActiveDocument.commitTransaction() #with open(filename, "wb") as f: # f.write(fillzone.encode('utf-8')) return fillzone ## def pull3D2dsn(s,mdls,tsp,nMd,gof,pcbThickness): global start_time, aux_orig, grid_orig global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y global off_x, off_y, maxRadius #sayw('pulling 3D model placement from pcb') doc=FreeCAD.ActiveDocument if gof and grid_orig==1: offset=[off_x,-off_y] #print(offset) if 0: model_3d_name = s.Label[s.Label.find('_')+1:s.Label.rfind('_')] model_3d_name = model_3d_name.replace('.','') #print (model_3d_name);stop nbrModel = None if s.Label.rfind('_') < s.Label.rfind('['): #ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] nbrModel = s.Label[s.Label.rfind('['):] #print(nbrModel) nMd = int(nbrModel.replace('[','').replace(']',''))-1 else: #ts = s.Label[s.Label.rfind('_')+1:] nMd = 0 #print('nbrModel = 0') idxF=-1 for i,mdl in enumerate (mdls): #print (mdl,nMd) #print(mdl[10],':', mdl[12]-1,':',nMd) if tsp in str(mdl[10]) and mdl[12]-1 == nMd: #print('FOUND',mdl[10],':', mdl[12],':',nMd) #if nMd == mdl[12]: idxF=i FLayer=1. #print(mdl[4]) pos_x=mdl[1]-off_x pos_y=mdl[2]-off_y rot=mdl[3] mdl_layer=mdl[4] wrl_pos=mdl[6] wrl_rot=mdl[7] #print(mdl[10]) #print(mdl[12]) #print(wrl_pos) #print(wrl_rot) if wrl_rot[0] != 0: sayw('3D model X rotation != 0 (rotX='+str(wrl_rot[0])+')') if wrl_rot[1] != 0: sayw('3D model Y rotation != 0 (rotY='+str(wrl_rot[0])+')') dummyshape = Part.Shape() print(s.Label,': model found! Placing it!') if 'Top' not in mdl_layer: FLayer=-1. if FLayer==1.: #ang = float(rot) #dummyshape.Placement = FreeCAD.Placement(FreeCAD.Vector(pos_x,pos_y,-pcbThickness),FreeCAD.Rotation(FreeCAD.Vector(0,1,0),180)) ##FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation=FreeCAD.Rotation(FreeCAD.Vector(0,0,1),ang) dummyshape.Placement=FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,0+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl dummyshape.rotate((pos_x,pos_y,0),(0,0,1),rot+float(wrl_rot[2])) FreeCAD.ActiveDocument.getObject(s.Name).Placement=dummyshape.Placement #bbpa=degrees(FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation.Angle) #print(bbpa);#stop #if FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation.Axis.z == -1: # bbpa=-bbpa #new_angle=bbpa+z_rot else: #print('bottom') dummyshape.Placement=FreeCAD.Placement(FreeCAD.Vector(pos_x+float(wrl_pos[0])*25.4,pos_y+float(wrl_pos[1])*25.4,+pcbThickness+float(wrl_pos[2])*25.4),FreeCAD.Rotation(-float(wrl_rot[2]),-float(wrl_rot[1]),-float(wrl_rot[0]))) #rot is already rot fp -rot wrl dummyshape.rotate((pos_x,pos_y,0),(0,0,1),180+rot+float(wrl_rot[2])) dummyshape.rotate((pos_x,pos_y,0),(0,1,0),180) FreeCAD.ActiveDocument.getObject(s.Name).Placement=dummyshape.Placement # bbpa=round(FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation.toEuler()[0],1) break # ## def push3D2pcb(s,cnt,tsp): #global last_fp_path, test_flag, start_time #global configParser, configFilePath, start_time #global ignore_utf8, ignore_utf8_incfg, disable_PoM_Observer global start_time, aux_orig, grid_orig global board_base_point_x, board_base_point_y, real_board_pos_x, real_board_pos_y #global pcb_path, use_AppPart, force_oldGroups, use_Links #global original_filename, aux_orig, grid_orig global off_x, off_y, maxRadius #sayw('pushing 3D model moved to pcb') doc=FreeCAD.ActiveDocument data=u''.join(cnt) tstamp_found=False #if len(re.findall('\s\(tstamp(.+?)\)',data, re.MULTILINE|re.DOTALL))>0: #if len(re.findall('\s\(tstamp(\s'+s.TimeStamp+'.+?)\)',data, re.MULTILINE|re.DOTALL))>0: #if len(re.findall('\s\(tstamp(\s'+tsp+'.+?)\)',data, re.MULTILINE|re.DOTALL))>0: if len(re.findall('\s\(tstamp(\s.*'+tsp.lower()+'+\))',data, re.MULTILINE|re.DOTALL))>0 or \ len(re.findall('\s\(tstamp(\s.*'+tsp.upper()+'+\))',data, re.MULTILINE|re.DOTALL))>0: #kv6 puts tstamp in lower case #if len(re.findall('\s\(tstamp(\s'+tsp+'.+?)\)',data, re.MULTILINE|re.DOTALL))>0: tstamp_found=True #old_pos=re.findall('\s\(tstamp(\s'+sel[0].TimeStamp+'.+?'+'\(at'+'\s.+?)\)',data, re.MULTILINE|re.DOTALL)[0] #print (old_pos) # new_pos=old_pos.split('(at')[0]+'(at 1.23 5.67 890' if tstamp_found: oft=None if aux_orig == 1: oft=getAuxOrigin(data) if grid_orig == 1: oft=getGridOrigin(data) #print oft gof=False if oft is not None: off_x=oft[0];off_y=-oft[1] offset = oft gof=True bbpx=FreeCAD.ActiveDocument.getObject(s.Name).Placement.Base[0] bbpy=FreeCAD.ActiveDocument.getObject(s.Name).Placement.Base[1] offset=[bbpx,bbpy] #print(bbpx);print(bbpy);print(bbpa) if gof and grid_orig==1: offset=[off_x,-off_y] #print(offset) #print (bbpx+off_x);print (-1*(bbpy+off_y)) model_3d_name = s.Label[s.Label.find('_')+1:s.Label.rfind('_')] model_3d_name = model_3d_name.replace('.','') #print (model_3d_name);stop if s.Label.rfind('_') < s.Label.rfind('['): #ts = s.Label[s.Label.rfind('_')+1:s.Label.rfind('[')] nbrModel = s.Label[s.Label.rfind('['):] nMd = int(nbrModel.replace('[','').replace(']',''))-1 else: #ts = s.Label[s.Label.rfind('_')+1:] nMd = 0 idxF=-1 for i,ln in enumerate (cnt): #if '(tstamp '+s.TimeStamp in ln: #if '(tstamp '+tsp in ln: if '(tstamp ' in ln: if tsp in ln: idxF=i #print(ln) FLayer=1. if idxF>=0: print(s.Label) sayw('pushing 3D model moved to pcb') if 'Front' not in cnt[idxF]: FLayer=-1. if FLayer==1.: bbpa=degrees(FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation.Angle) #print(bbpa);#stop if FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation.Axis.z == -1: bbpa=-bbpa #new_angle=bbpa+z_rot else: bbpa=round(FreeCAD.ActiveDocument.getObject(s.Name).Placement.Rotation.toEuler()[0],1) #new_angle=bbpa+z_rot #say (content[idxF+1]) #if 'Front' not in cnt[idxF]: # FLayer=-1. mod_old_angle = 0 mod_old_values = cnt[idxF+1].split('(at ')[1].split(' ') #sayw(mod_old_values) if len(mod_old_values) == 3: mod_old_angle= (mod_old_values[2].split(')'))[0] mod_old_angle = mod_old_angle.replace(' ','') #print (mod_old_angle) if len(mod_old_angle) > 0: mod_old_angle=float("{0:.3f}".format(float(mod_old_angle))) else: mod_old_angle = 0 #say ('module old angle '+str(mod_old_angle)) nbr_spaces = len(cnt[idxF+1]) - len(cnt[idxF+1].lstrip()) #new_pos="{0:.3f}".format(bbpx+off_x)+" "+"{0:.3f}".format(-1*(bbpy+off_y))+\ # " "+"{0:.3f}".format(bbpa)+")" # " "+"{0:.3f}".format(bbpa-mod_old_angle)+")" #print(new_pos) ##cnt[idxF+1]=" " * nbr_spaces + '(at '+new_pos+os.linesep #say (content[idxF+1]) looping=True;ik=0 pads_2rot=[];old_ref_angle=None;old_val_angle=None;old_usr_angle=None nMdCnt = 0 while looping and (idxF+ik) < len(cnt): #for ln in content[idxF+1:]: ik+=1 ln = cnt[idxF+ik] if '(model' in ln: if nMdCnt==nMd: #looping=False ##if model_3d_name in ln.replace('.',''): #removing '.' not imported by STEP #print(ln);print(cnt[idxF+ik+3]);stop #print(ln);print(cnt[idxF+ik+3]);print(nMdCnt)#;stop ln_r=cnt[idxF+ik+1] # (offset (xyz -1.27 0 0)) mm # (at (xyz -1.27/25.4 0 0)) decimils if 'at' in ln_r: k=25.40 else: k=1.0 ido = ln_r.find('xyz');ofs=ln_r[ido+3:] #lstrip('xyz') ido = ofs.find('))');ofs=ofs[:ido] ofs = ofs.lstrip(' ').split(' ') #sayerr(ofs) if len(ofs)==3: x_o=float(ofs[0])*k y_o=float(ofs[1])*k z_o=float(ofs[2])*k else: x_o=0;y_o=0;z_o=0 #sayw(ofs) ln_r=cnt[idxF+ik+3] # (rotate (xyz 0 0 0)) #print(ln_r)#;stop idz1 = ln_r.find('xyz');z_rot=ln_r[idz1+3:] #lstrip('xyz') idz2 = z_rot.find('))');z_rot=z_rot[:idz2] #z_rot = z_rot.rstrip('))') z_rot = z_rot.lstrip(' ').split(' ') #sayerr(z_rot) if len(z_rot)==3: all_rot=z_rot z_rot=float(z_rot[2]) if float(all_rot[0]) != 0: sayerr('3D model X rotation NOT supported ATM!') sayw('X rot='+all_rot[0]) msg="""3D model X rotation NOT supported ATM!
Please fix your 3D model rotation X to '0'""" say_warning(msg) stop if float(all_rot[1]) != 0: sayerr('3D model Y rotation NOT supported ATM!') sayw('Y rot='+all_rot[1]) msg="""3D model Y rotation NOT supported ATM!
Please fix your 3D model rotation Y to '0'""" say_warning(msg) stop else: z_rot=0 #print(z_rot);stop #sayw(z_rot) looping=False else: nMdCnt+=1 if '(pad ' in ln: #print (ln) #print (ln.split('(at ')[1].split(')')[0].split(' ')) pad_values=ln.split('(at ')[1].split(')')[0].split(' ') if len(pad_values) == 3: #print(pad_values[2]) old_pad_angle = (float(pad_values[2])) else: old_pad_angle = 0 base_pad_angle = old_pad_angle - mod_old_angle id_pad = idxF+ik pads_2rot.append([base_pad_angle,id_pad,pad_values]) #print(content[idxF+ik]) if '(fp_text reference' in ln: #print (ln) #print (ln.split('(at ')[1].split(')')[0].split(' ')) ref_values=ln.split('(at ')[1].split(')')[0].split(' ') old_ref_angle = 0 if len(ref_values) == 3: #print(pad_values[2]) print(ref_values[2]) if ref_values[2] != 'unlocked': old_ref_angle = (float(ref_values[2])) #else: # old_ref_angle = 0 #print (pad_values);print(ln.split('(at ')) idx_ref=idxF+ik if '(fp_text value' in ln: #print (ln) #print (ln.split('(at ')[1].split(')')[0].split(' ')) val_values=ln.split('(at ')[1].split(')')[0].split(' ') old_val_angle = 0 if len(val_values) == 3: #print(pad_values[2]) if val_values[2] != 'unlocked': old_val_angle = (float(val_values[2])) #else: # old_val_angle = 0 base_val_angle = old_val_angle - mod_old_angle new_val_angle = ' '+("{0:.3f}".format(base_val_angle + bbpa)) if float(new_val_angle) == 0: new_val_angle='' #print (pad_values);print(ln.split('(at ')) idx_val=idxF+ik cnt[idxF+ik] = ln.split('(at ')[0]+'(at ' + val_values[0] +' '+ val_values[1]+new_val_angle+ln[ln.index(') '):] if '(fp_text user' in ln: #print (ln) #print (ln.split('(at ')[1].split(')')[0].split(' ')) usr_values=ln.split('(at ')[1].split(')')[0].split(' ') old_usr_angle = 0 if len(usr_values) == 3: #print(pad_values[2]) if usr_values[2] != 'unlocked': old_usr_angle = (float(usr_values[2])) #else: # old_usr_angle = 0 base_usr_angle = old_usr_angle - mod_old_angle new_usr_angle = ' '+("{0:.3f}".format(base_usr_angle + bbpa)) if float(new_usr_angle) == 0: new_usr_angle='' #print (pad_values);print(ln.split('(at ')) idx_usr=idxF+ik cnt[idxF+ik] = ln.split('(at ')[0]+'(at ' + usr_values[0] +' '+ usr_values[1]+new_usr_angle+ln[ln.index(') '):] #adjusting footprint if FLayer==1.: new_angle=bbpa+z_rot else: new_angle=(bbpa-z_rot) # 180+(bbpa+z_rot) #new_angle=bbpa+z_rot if "{0:.3f}".format(new_angle) == '-0.00' or "{0:.3f}".format(new_angle) == '0.00': new_angle_str='' else: new_angle_str = ' '+"{0:.3f}".format(new_angle) new_pos="{0:.3f}".format(bbpx+off_x-x_o*FLayer)+" "+"{0:.3f}".format(-1*(bbpy+off_y-y_o*FLayer))\ +new_angle_str+")" #print(new_pos) cnt[idxF+1]=" " * nbr_spaces + '(at '+new_pos+os.linesep #adjusting reference if old_ref_angle is not None: base_ref_angle = old_ref_angle - mod_old_angle # - z_rot new_ref_angle = ' '+("{0:.3f}".format(base_ref_angle + bbpa + z_rot)) if float(new_ref_angle) == 0: new_ref_angle='' ln=cnt[idx_ref] cnt[idx_ref] = ln.split('(at ')[0]+'(at ' + ref_values[0] +' '+ ref_values[1]+new_ref_angle+ln[ln.index(') '):] if old_val_angle is not None: base_val_angle = old_ref_angle - mod_old_angle # new_val_angle = ' '+("{0:.3f}".format(base_val_angle + bbpa + z_rot)) if float(new_val_angle) == 0: new_val_angle='' ln=cnt[idx_val] cnt[idx_val] = ln.split('(at ')[0]+'(at ' + val_values[0] +' '+ val_values[1]+new_val_angle+ln[ln.index(') '):] if old_usr_angle is not None: base_usr_angle = old_usr_angle - mod_old_angle # new_usr_angle = ' '+("{0:.3f}".format(base_usr_angle + bbpa + z_rot)) if float(new_usr_angle) == 0: new_usr_angle='' ln=cnt[idx_usr] cnt[idx_usr] = ln.split('(at ')[0]+'(at ' + usr_values[0] +' '+ usr_values[1]+new_usr_angle+ln[ln.index(') '):] #print(new_ref_angle);print(old_ref_angle);print(z_rot);print(bbpa);print(base_ref_angle+bbpa);stop for p2r in pads_2rot: new_pad_angle = ' '+("{0:.3f}".format(p2r[0] + bbpa + z_rot)) if float(new_pad_angle) == 0: new_pad_angle='' #print (pad_values);print(ln.split('(at ')) ln = cnt[p2r[1]] pad_val = p2r[2] cnt[p2r[1]] = ln.split('(at ')[0]+'(at ' + pad_val[0] +' '+ pad_val[1]+new_pad_angle+ln[ln.index(') '):] #stop # 'we need to search for pads in module and add rotation angle each' #stop #newdata=u''.join(content) newcontent=cnt #print newcontent else: msg="footprint TimeStamp not found!
Please reload the kicad_pcb file
" say_warning(msg) newcontent=cnt # with codecs.open(fpath,'w', encoding='utf-8') as ofile: # ofile.write(newcontent) # ofile.close() # say_time() # msg="""3D model new position pushed to kicad board!

""" # if found_tracks: # msg+="tracks found!
you will need to fix your routing!


" # msg+="file saved to
"+fpath+"


" # msg+="backup file saved to
"+foname+"

" # msgr="3D model new position pushed to kicad board!\n" # msgr+="file saved to "+fpath+"\n" # msgr+="backup file saved to "+foname # say(msgr) # #say_info(msg) return newcontent ## ############################################################################################################### def centerOnScreen (widg): '''centerOnScreen() Centers the window on the screen.''' # sayw(widg.width());sayw(widg.height()) # sayw(widg.pos().x());sayw(widg.pos().y()) resolution = QtGui.QDesktopWidget().screenGeometry() xp=(resolution.width() / 2) - sizeXMax/2 # - (KSUWidget.frameSize().width() / 2) yp=(resolution.height() / 2) - sizeY/2 # - (KSUWidget.frameSize().height() / 2)) # xp=widg.pos().x()-sizeXMax/2;yp=widg.pos().y()#+sizeY/2 widg.setGeometry(xp, yp, sizeXMax, sizeY) ## def singleInstance(): app = QtGui.QApplication #QtGui.qApp for i in app.topLevelWidgets(): #say (str(i.objectName())) if i.objectName() == "kicadStepUp": say (str(i.objectName())) #i.close() #i.deleteLater() say ('closed') return False t=FreeCADGui.getMainWindow() dw=t.findChildren(QtGui.QDockWidget) #say( str(dw) ) for i in dw: #say (str(i.objectName())) if str(i.objectName()) == "kicadStepUp": #"kicad StepUp 3D tools": say (str(i.objectName())+' docked') #i.deleteLater() return False return True ## if singleInstance(): from threading import Timer KSUWidget = QtGui.QDockWidget() # create a new dckwidget KSUWidget.ui = Ui_DockWidget() # myWidget_Ui() # load the Ui script KSUWidget.ui.setupUi(KSUWidget) # setup the ui KSUWidget.setObjectName("kicadStepUp") paramGet = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/MainWindow") if len(paramGet.GetString("StyleSheet"))>0: #we are using a StyleSheet KSUWidget.setStyleSheet('QPushButton {border-radius: 0px; padding: 1px 2px;}') t=FreeCADGui.getMainWindow() ## wf = t.findChild(QtGui.QDockWidget, "KSUWidget") cv = t.findChild(QtGui.QDockWidget, "Combo View") if cv is None: cv = t.findChild(QtGui.QDockWidget, "Model") if cv is None: cv = t.findChild(QtGui.QDockWidget, "Tree view") #say( "Combo View" + str(cv)) ## print( "KSUWidget" + str(wf)) cv.setFeatures( QtGui.QDockWidget.DockWidgetMovable | QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetClosable ) #KSUWidget.setFeatures( QtGui.QDockWidget.DockWidgetMovable | QtGui.QDockWidget.DockWidgetFloatable|QtGui.QDockWidget.DockWidgetClosable ) KSUWidget.setFeatures( QtGui.QDockWidget.DockWidgetMovable | QtGui.QDockWidget.DockWidgetFloatable) #|QtGui.QDockWidget.DockWidgetClosable ) ksu_in_tab=False #tabify_widg=True #tabify_widg=False #dock_to_right=True if docking_mode == 'float': tabify() undock() textEdit_dim=textEdit_dim_base KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) elif docking_mode == 'left': textEdit_dim=textEdit_dim_hide KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) dock() ##tabify() #.Visible=False else: textEdit_dim=textEdit_dim_hide KSUWidget.ui.textEdit.setGeometry(textEdit_dim[0],textEdit_dim[1],textEdit_dim[2],textEdit_dim[3]) dock_right() KSUWidget.activateWindow() KSUWidget.raise_() sayw('done!') #if (tabify_widg): # tabify() #elif not dock_to_right: # #KSUmw = FreeCADGui.getMainWindow() # PySide # the active qt window, = the freecad window since we are inside it # tabify() # undock() # #KSUmw.addDockWidget(QtCore.Qt.RightDockWidgetArea,KSUWidget) # add the widget to the main window #else: # dock_right() KSUWidget.activateWindow() KSUWidget.raise_() KSUWidget.hide() def getComboView(self,window): """ Returns the main Tab. """ dw=window.findChildren(QtGui.QDockWidget) for i in dw: if str(i.objectName()) == "Combo View" or str(i.objectName() == "Model") or str(i.objectName()) == "Tree view": return i.findChild(QtGui.QTabWidget) raise Exception("No tab widget found") # QDockWidget::setFloating (false) # KSUWidget.setFloating(True) #undock #KSUWidget.setFloating(False) #dock #KSUWidget.setStyleSheet('QPushButton { border: 1px solid #5a5a5a;border-radius: 0px;min-width: 50px;min-height: 20px;padding: 1px 2px;}') #KSUWidget.setStyleSheet('QPushButton:hover,QPushButton:focus { color: white; border-color: #3874f2; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #5e90fa, stop:1 #3874f2);}') # print QtGui.QApplication.style().metaObject().className() # # print KSUWidget.style().metaObject().className() # # print QtGui.QApplication.instance().styleSheet() # list all applied styles # try: # if KSUWidget.style().metaObject().className()== "QStyleSheetStyle": # KSUWidget.setStyleSheet('QPushButton {border-radius: 0px; padding: 1px 2px;}') # except: # pass ## QtGui.QFont().setPointSize(font_size) ???? to evaluate if still is necessary #Ui_DockWidget().destroyed.connect(onDestroy()) ## KSUWidget.installEventFilter(KSUWidget) ## form = RotateXYZGuiClass() ## #rotate = rotate_gui() ## form.setObjectName("kicadStepUp") ## ## if QtGui.QApplication.style().metaObject().className() == "QStyleSheetStyle": ## form.setStyleSheet('QPushButton {border-radius: 0px; padding: 1px 2px;}')