From b51abacc8cb9f133a745469c6506c222a497b356 Mon Sep 17 00:00:00 2001 From: hneemann Date: Mon, 3 Jul 2017 20:56:08 +0200 Subject: [PATCH] allow to show the graph data as a table --- .../java/de/neemann/digital/gui/Main.java | 8 +- .../gui/components/data/GraphDialog.java | 27 ++- ...esultDialog.java => ValueTableDialog.java} | 140 +++++++++++----- src/main/resources/lang/lang_de.xml | 2 + src/main/resources/lang/lang_en.xml | 2 + src/main/resources/measurement-graph.png | Bin 1275 -> 1061 bytes src/main/resources/measurement-graph_hi.png | Bin 2598 -> 1875 bytes src/main/svg/measurement-graph.svg | 155 ++++++++---------- 8 files changed, 200 insertions(+), 134 deletions(-) rename src/main/java/de/neemann/digital/gui/components/testing/{TestResultDialog.java => ValueTableDialog.java} (70%) diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 0e5f4a810..f7c004da2 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -29,7 +29,7 @@ import de.neemann.digital.gui.components.modification.Modifications; import de.neemann.digital.gui.components.modification.ModifyAttribute; import de.neemann.digital.gui.components.modification.ModifyMeasurementOrdering; import de.neemann.digital.gui.components.table.TableDialog; -import de.neemann.digital.gui.components.testing.TestResultDialog; +import de.neemann.digital.gui.components.testing.ValueTableDialog; import de.neemann.digital.gui.components.tree.LibraryTreeModel; import de.neemann.digital.gui.components.tree.SelectTree; import de.neemann.digital.gui.remote.DigitalHandler; @@ -828,17 +828,17 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS */ public void startTests() { try { - ArrayList tsl = new ArrayList<>(); + ArrayList tsl = new ArrayList<>(); for (VisualElement el : circuitComponent.getCircuit().getElements()) if (el.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION)) - tsl.add(new TestResultDialog.TestSet( + tsl.add(new ValueTableDialog.TestSet( el.getElementAttributes().get(TestCaseElement.TESTDATA), el.getElementAttributes().getCleanLabel())); if (tsl.isEmpty()) throw new TestingDataException(Lang.get("err_noTestData")); - windowPosManager.register("testResult", new TestResultDialog(Main.this, tsl, circuitComponent.getCircuit(), library)).setVisible(true); + windowPosManager.register("testResult", new ValueTableDialog(Main.this).addTestResult(tsl, circuitComponent.getCircuit(), library)).setVisible(true); ensureModelIsStopped(); } catch (NodeException | ElementNotFoundException | PinException | TestingDataException | RuntimeException e1) { diff --git a/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java b/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java index e55599505..3f1e2ff57 100644 --- a/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java @@ -7,6 +7,7 @@ import de.neemann.digital.core.Signal; import de.neemann.digital.data.ValueTable; import de.neemann.digital.gui.SaveAsHelper; import de.neemann.digital.gui.components.OrderMerger; +import de.neemann.digital.gui.components.testing.ValueTableDialog; import de.neemann.digital.gui.sync.NoSync; import de.neemann.digital.gui.sync.Sync; import de.neemann.digital.lang.Lang; @@ -33,6 +34,7 @@ public class GraphDialog extends JDialog implements ModelStateObserver { private final GraphComponent dsc; private final JScrollPane scrollPane; private final Sync modelSync; + private final ToolTipAction showTable; private ValueTableObserver valueTableObserver; private static final Icon ICON_EXPAND = IconCreator.create("View-zoom-fit.png"); @@ -50,7 +52,7 @@ public class GraphDialog extends JDialog implements ModelStateObserver { * @param modelSync the lock to access the model * @return the created instance */ - public static GraphDialog createLiveDialog(Frame owner, Model model, boolean microStep, List ordering, Sync modelSync) { + public static GraphDialog createLiveDialog(JFrame owner, Model model, boolean microStep, List ordering, Sync modelSync) { String title; if (microStep) title = Lang.get("win_measures_microstep"); @@ -78,7 +80,7 @@ public class GraphDialog extends JDialog implements ModelStateObserver { * @param title the frame title * @param logData the data to visualize */ - public GraphDialog(Frame owner, String title, ValueTable logData) { + public GraphDialog(JFrame owner, String title, ValueTable logData) { this(owner, title, null, logData, null, NoSync.INST); } @@ -91,7 +93,7 @@ public class GraphDialog extends JDialog implements ModelStateObserver { * @param logData the data to visualize * @param modelSync used to access the running model */ - private GraphDialog(Frame owner, String title, Model model, ValueTable logData, ValueTableObserver valueTableObserver, Sync modelSync) { + private GraphDialog(JFrame owner, String title, Model model, ValueTable logData, ValueTableObserver valueTableObserver, Sync modelSync) { super(owner, title, false); this.valueTableObserver = valueTableObserver; this.modelSync = modelSync; @@ -125,6 +127,13 @@ public class GraphDialog extends JDialog implements ModelStateObserver { } }.setAccelerator("control MINUS"); + showTable = new ToolTipAction(Lang.get("menu_showDataAsTable")) { + @Override + public void actionPerformed(ActionEvent e) { + new ValueTableDialog(owner).addValueTable("Data", logData).disableGraph().setVisible(true); + } + }.setToolTip(Lang.get("menu_showDataAsTable_tt")); + toolBar.add(zoomIn.createJButtonNoText()); toolBar.add(zoomOut.createJButtonNoText()); toolBar.add(maximize.createJButtonNoText()); @@ -165,6 +174,8 @@ public class GraphDialog extends JDialog implements ModelStateObserver { view.add(maximize.createJMenuItem()); view.add(zoomOut.createJMenuItem()); view.add(zoomIn.createJMenuItem()); + view.addSeparator(); + view.add(showTable.createJMenuItem()); setJMenuBar(bar); pack(); @@ -185,4 +196,14 @@ public class GraphDialog extends JDialog implements ModelStateObserver { }); }); } + + /** + * Disable the show as table function + * + * @return this for chained calls + */ + public GraphDialog disableTable() { + showTable.setActive(false); + return this; + } } diff --git a/src/main/java/de/neemann/digital/gui/components/testing/TestResultDialog.java b/src/main/java/de/neemann/digital/gui/components/testing/ValueTableDialog.java similarity index 70% rename from src/main/java/de/neemann/digital/gui/components/testing/TestResultDialog.java rename to src/main/java/de/neemann/digital/gui/components/testing/ValueTableDialog.java index 692f02bd5..7ae50cf40 100644 --- a/src/main/java/de/neemann/digital/gui/components/testing/TestResultDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/testing/ValueTableDialog.java @@ -10,15 +10,14 @@ import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementNotFoundException; import de.neemann.digital.draw.model.ModelCreator; +import de.neemann.digital.gui.SaveAsHelper; import de.neemann.digital.gui.components.data.GraphDialog; import de.neemann.digital.lang.Lang; import de.neemann.digital.testing.*; -import de.neemann.gui.ErrorMessage; -import de.neemann.gui.IconCreator; -import de.neemann.gui.LineBreaker; -import de.neemann.gui.ToolTipAction; +import de.neemann.gui.*; import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; import java.awt.event.ActionEvent; @@ -30,7 +29,7 @@ import java.util.Collections; * * @author hneemann */ -public class TestResultDialog extends JDialog { +public class ValueTableDialog extends JDialog { private static final Color FAILED_COLOR = new Color(255, 200, 200); private static final Color PASSED_COLOR = new Color(200, 255, 200); private static final Icon ICON_FAILED = IconCreator.create("testFailed.png"); @@ -39,28 +38,73 @@ public class TestResultDialog extends JDialog { private final ArrayList resultTableData; + private final JTabbedPane tp; + private final JFrame owner; + private final ToolTipAction asGraph; /** * Creates a new result dialog. * - * @param owner the parent frame + * @param owner the parent frame + */ + public ValueTableDialog(JFrame owner) { + super(owner, Lang.get("msg_testResult"), false); + this.owner = owner; + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setAlwaysOnTop(true); + + resultTableData = new ArrayList<>(); + tp = new JTabbedPane(); + + JMenuBar bar = new JMenuBar(); + JMenu file = new JMenu(Lang.get("menu_file")); + bar.add(file); + file.add(new ToolTipAction(Lang.get("menu_saveData")) { + @Override + public void actionPerformed(ActionEvent e) { + int tab = tp.getSelectedIndex(); + if (tab < 0) tab = 0; + JFileChooser fileChooser = new MyFileChooser(); + fileChooser.setFileFilter(new FileNameExtensionFilter("Comma Separated Values", "csv")); + new SaveAsHelper(ValueTableDialog.this, fileChooser, "csv") + .checkOverwrite(resultTableData.get(tab)::saveCSV); + } + }.setToolTip(Lang.get("menu_saveData_tt")).createJMenuItem()); + + JMenu view = new JMenu(Lang.get("menu_view")); + asGraph = new ToolTipAction(Lang.get("menu_showDataAsGraph"), ICON_GRAPH) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + int tab = tp.getSelectedIndex(); + if (tab < 0) tab = 0; + new GraphDialog(owner, Lang.get("win_testdata_N", tp.getTitleAt(tab)), resultTableData.get(tab)).disableTable().setVisible(true); + } + }.setToolTip(Lang.get("menu_showDataAsGraph_tt")); + view.add(asGraph.createJMenuItem()); + bar.add(view); + setJMenuBar(bar); + + JToolBar toolBar = new JToolBar(); + toolBar.add(asGraph.createJButtonNoText()); + getContentPane().add(toolBar, BorderLayout.NORTH); + + getContentPane().add(tp); + } + + /** + * Add test results + * * @param tsl list of test sets * @param circuit the circuit * @param library the library to use + * @return this for chained calls * @throws NodeException NodeException * @throws TestingDataException DataException * @throws PinException PinException * @throws ElementNotFoundException ElementNotFoundException */ - public TestResultDialog(JFrame owner, ArrayList tsl, Circuit circuit, ElementLibrary library) throws NodeException, TestingDataException, PinException, ElementNotFoundException { - super(owner, Lang.get("msg_testResult"), false); - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - + public ValueTableDialog addTestResult(ArrayList tsl, Circuit circuit, ElementLibrary library) throws PinException, NodeException, ElementNotFoundException, TestingDataException { Collections.sort(tsl); - - resultTableData = new ArrayList<>(); - - JTabbedPane tp = new JTabbedPane(); int i = 0; int errorTabIndex = -1; for (TestSet ts : tsl) { @@ -71,14 +115,6 @@ public class TestResultDialog extends JDialog { if (testExecutor.getException() != null) SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorWhileExecutingTests_N0", ts.name)).addCause(testExecutor.getException()).setComponent(this)); - JTable table = new JTable(new ValueTableModel(testExecutor.getResult())); - table.setDefaultRenderer(Value.class, new ValueRenderer()); - table.setDefaultRenderer(Integer.class, new NumberRenderer()); - final Font font = table.getFont(); - table.getColumnModel().getColumn(0).setMaxWidth(font.getSize() * 4); - table.setRowHeight(font.getSize() * 6 / 5); - resultTableData.add(testExecutor.getResult()); - String tabName; Icon tabIcon; if (testExecutor.allPassed()) { @@ -92,36 +128,54 @@ public class TestResultDialog extends JDialog { if (testExecutor.toManyResults()) tabName += " " + Lang.get("msg_test_missingLines"); - tp.addTab(tabName, tabIcon, new JScrollPane(table)); + tp.addTab(tabName, tabIcon, new JScrollPane(createTable(testExecutor.getResult()))); if (testExecutor.toManyResults()) tp.setToolTipTextAt(i, new LineBreaker().toHTML().breakLines(Lang.get("msg_test_missingLines_tt"))); + resultTableData.add(testExecutor.getResult()); i++; } if (errorTabIndex >= 0) tp.setSelectedIndex(errorTabIndex); - - JMenuBar bar = new JMenuBar(); - JMenu view = new JMenu(Lang.get("menu_view")); - ToolTipAction asGraph = new ToolTipAction(Lang.get("menu_showDataAsGraph"), ICON_GRAPH) { - @Override - public void actionPerformed(ActionEvent actionEvent) { - int tab = tp.getSelectedIndex(); - if (tab < 0) tab = 0; - new GraphDialog(owner, Lang.get("win_testdata_N", tp.getTitleAt(tab)), resultTableData.get(tab)).setVisible(true); - } - }.setToolTip(Lang.get("menu_showDataAsGraph_tt")); - view.add(asGraph.createJMenuItem()); - bar.add(view); - setJMenuBar(bar); - - JToolBar toolBar = new JToolBar(); - toolBar.add(asGraph.createJButtonNoText()); - getContentPane().add(toolBar, BorderLayout.NORTH); - - getContentPane().add(tp); pack(); setLocationRelativeTo(owner); + return this; + } + + /** + * Add a table to this dialog + * + * @param name the name of the tab + * @param valueTable the values + * @return this for chained calls + */ + public ValueTableDialog addValueTable(String name, ValueTable valueTable) { + tp.addTab(name, new JScrollPane(createTable(valueTable))); + resultTableData.add(valueTable); + + pack(); + setLocationRelativeTo(owner); + return this; + } + + private JTable createTable(ValueTable valueTable) { + JTable table = new JTable(new ValueTableModel(valueTable)); + table.setDefaultRenderer(Value.class, new ValueRenderer()); + table.setDefaultRenderer(Integer.class, new NumberRenderer()); + final Font font = table.getFont(); + table.getColumnModel().getColumn(0).setMaxWidth(font.getSize() * 4); + table.setRowHeight(font.getSize() * 6 / 5); + return table; + } + + /** + * Disable the show as graph function + * + * @return this for chained calls + */ + public ValueTableDialog disableGraph() { + asGraph.setActive(false); + return this; } /** diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index f7cc6c6d1..f462a3d3a 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -865,6 +865,8 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Letzte rückgängig gemachte Aktion wieder herstellen. Zeige Graph Zeigt die Daten als Graph an. + Zeige Tabelle + Zeigt die Daten als Tabelle an. Digital diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index edf409d0e..851a863e3 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -853,6 +853,8 @@ The names of the variables may not be unique. Apply last reverted modification again. Show graph Show the data as a Graph. + Show table + Shows values as a table. Digital diff --git a/src/main/resources/measurement-graph.png b/src/main/resources/measurement-graph.png index 605724d3a44d965eec65f3d73832de7f5810dd3b..39946219d68a6874dc5e6f181943330fdb5928fc 100644 GIT binary patch delta 995 zcmV<9104MO38e^-7YZB*1^@s6at+^4 zmdi^rT^z^1iuG7jmR+>6%czS4yNMB92#gkmn+j^vqJ8_Qf2N3tXwlX;=|V!Z2y5Xb zFGDhsBZ%T9M=W#nyo-3=Mb4Sc3(w;DEzgI~e9v$AotXpt?WG`u{PU*+$K-L#&AcTLAa5zk+r>BW1ii8kC z9v&VDt(;CL@p`?I?Ee0qtgfz7U7qKO5&({ljfe=4r=b)@5gf;1e}5lmXJ-Hauh&bJK0ZEV1Le5IVu8}_cH`yc1-rYu@OV7PX0tFF zjZ{ggRLTY_6bgST^Z9&Got&KD>gozwtrl*#8>gqISXo&?S67#$)Y#Z48z{#u77J86 zJ3G*7wE%#nr6p)I8dNG3sw4=4Y@i(Hc^;~Tg@ykXR4Nr_W@hmI{!W#KhK8`UwS|U; zhC253^#!NXNp;m~_4l#(S(lfWB$LUIQmI6Eo+q_hjU;~(2~sQ;$<56TNu^SxTrLxy z=SihfA<1Nt6bc1WC=`g>?Ixlq5}xNtxm+gcbeiOHIZ`YZNi-TIk`pjGI!fK;;NT#Y z0l>h(0F}k#@%m4jD2f;v8ToEsqB5BbE-o%GF)<;@IgSIvFp`WA0#z^=gv;fEQmKT) z;eg$4$K8M39eR6vC8f2sHHe}}?c($ElghkaFO*MDPtfc2SYKbq-rgR{<+AJpo12@d z3;+xU1C^OfCMW^G@Au>O_7)7o;McET$mjD^Np6AxARG=;SuU6R4%F#%Fqup!6beWr z5-^+1RH>t*LpE@He4NUv)#`Vk(P)Ij;Xr?XKRADmLm&{KO8=)rolXbU@bECs&(GoU zc)&0W=H}*5DwU`bZF+xKcXv0Hu`DZn#Rvw2uv)E>(&pwSnwpxZHv&Nr(AwH6$!fJ) z{U09yRRDmEjSbmJl{<&ASWLRZR4RqHw>Q{qHatE)!fZApo6VwDtD&c-2Q16NX0yTX z_oF|ruMg#N8G;~yVHi9=Kf`LZA{L9Gy}cbOl?usZ5>~4ffk5ETUlafS`WI^Q6j+Y9 RDj5I(002ovPDHLkV1hq)(v1KB delta 1211 zcmV;s1VsC#2>S_;7YZ2&1^@s6BEU{3ks%rj1{nYV1{nb|;iYGhMkjv+cu7P-R7i=H zmOp4yT^PoHxw&a;l3UXnlhoMAtwW_CsNf%TaHt}kif$rwkb)VNN=03YbQLF87j+Q? z!9m2$MT{nk5?YcPO)fF5xv9OC=C3{9kk8WFbNt|YA032wf65RFD@Hk)`n9&}yjQ(yt``KPUe*EC%$&=i=b<4$DtrohjqiLFp z-??*#FJHc}d-rY^cN_=eR##WW&U*j;y_kQUot1d8SQL)qNIaQL zip9l6iLbA(3$4*;(9_dHI-Le!eSIBO4Gs=UEED3{AA#cOM860>cadc7`j$8nS%!^6X9@7}%R?AfzSO-=Fi=~FtLj#6wGhQval z5WT&<5)TH0N{@d+p@2R!GeaVg0N~)kgGwPlsZ?TdanZ$W+a?eQNT*URmz5r-X`=sW z2Hh=Jt5r!DkH_)*{XBW{#Kr$MrIAR4J$v>@KHt85L*EG(78W>q^eC2Pkw_%AayL0S z$&n*Rux%T|FlaOyIF5s17ziOS3}Y*IA3l7*@As3pIbRFf73!YE*ij%8#lOg=@M_>zGeH= zZm(9WC9!t9t=x@D3(#mZ(9WDW!^4LUSzcada&nSFp`aA++Ott?_Q~)*Xu?5`t>X4&YdG3k8|wUF@FC1sa)Ovd4NsRRC-h@m2M9IPdm9aV~V{;`7Yu2loJZ{I$pN1;%V zV^OJ8q`ak4DU!(~$B!R(@p`?E&*zf{$8j9x0y~|K+%TWdQ>j!iO_T56zY`9JaU6$syN%!PM+kw>=VN1IgHR~M=g$|P Z`3IDIQMbyJ){6iD002ovPDHLkV1l3mQ4;_F diff --git a/src/main/resources/measurement-graph_hi.png b/src/main/resources/measurement-graph_hi.png index 8e69216d590ed3b2fcfe8c8061987cf5f296adfa..94241bea35ff94504163a8db909ecc08533824eb 100644 GIT binary patch delta 1815 zcmV+y2k7{w6w?ln7YaBC1^@s6sD?Wpks%rj4{87a4{8A+431%uMkjv;&`Cr=RA_ljE3XP5BYxBxqq4W1$%-k%fjIStbNpWcERI4s=;`SekB`x9l)f8IC0_x zF)=aJ)z$Io(`X5@>5{ zqqnygfP#Vo!otEpxXI(kkKHx>pU_$?7SYhqAl$0p($W&0ot^ac^|@Q{6$}gvuz&x4 zjvP56NvG54sHv&($gj<2V{mZLy$a6H&kMk-tE8kvaupsudgQfVw<;JM9E?`0<-er(^qeL2DM z@^Z4WvWSR?;L@c_w6(Q4m)U<)T3YIpKs`mFP;m0(NoRszzka2rriP-TA^=iSQizL- zqoSh1r$Ksy!{NYWGC32htgJ)`L415XLqkIh4Goc*nTb}b#csC)&MngKgvDZ^v9Zzl znb2r7EG~a85+5J`gJDxs6IoeV8{1bXf*mb zRB8D8`;(B6zy^&*gG!|$H8u4Iqr$>MYHDhjoSby++zh(@E4r1|pYi&s8DLPy`;-u}1m>3<_IFp!v-7yu?GCnag+aycjBucYZoCQAq)-??$Oq(%;|D+}s=h2M-=Z zCX=Dp>oFRQ7z_sX?AgQI+#LP={Q#&`Dk38z85T5>PJz`#K6 z+_}Tmt5+pEX>DyKEiFy5&RZ527pbbMaxOCI>FLO1GGsCtetv%J*s+8B{Coxm23&sv zPm)^=4jee(mEN_AW@cv6-Q7)BR~MzFr3?)Xk)555!{Gq%*$^rvCB-A%_d>ZI(U6c3 zl9G~K1`daV+}vD7M@NZ{j;5lb!Y6~LuC6W)9Xcdgr%))!$jJElcd)0Yhv48~!o$P8 z3ea1+ySqKoIUEi~Mn;w^*wN8JK|y~3Hk*x+kr5^*C#kNkX49rkfOGfqJ7KX{Xl`y^ zuHg4aG&wn$oSYmK3I%0lWmqg00J5^Od=jWPJ1D@p*-lGK6X(vITd_$UA0HQTxm=W% zmI@(+Fc=ITZFXB)T0A~{@dm5aDtdc+#ZTYCh=>T3N~Not*XeZL-ANC5@#24lM>@G& zj#{n$`G>x-v5~2%DfaE#=W%eWfu~Kc)oP`?yL&momX;PWGBOZCU^1C77z`8?6maq4 zMF5T+JGMFzuMswz4THhpOz`H-o0v={*NmBVPQdNG#VjkWKmUB#q{(v3Wb7e z*RHX!uz*&p1z_jSon&WcGcYhfQ&SV*TzS=MH9DQnH7qJDEJUePQc-hJf!SZ;Ba^(tvfq|5jm0`772@MV9 z?AfzSOiWN+T}^m+_@6tI`Og2v|Em4d*Fmha&N_cC{{l#@&3A4A2af;%002ovPDHLk FV1m{WaB2Vm delta 2544 zcmV51^@s6kBMs5ks%rj3^4!z3^4&Mq*e}*Mkjv>qe(2WgEwTdv(bXPjyL)-(a>L~gu=ip?GbfmH!rz!nXWWna$ofrs^>Y`O0yW}g4de%bp!|8rf} z{c^7Bx{pEEb)5*fckh25XU?1fVArl)^!4>|@!~}mELecWV&Ruxeqmr>fSjBhR8{4` zfdk~{=Tlf%NOW{GXV0DmVE69bba!`i`SNAb($d(xc{87V_8E$zuyNx?RHU*=P(+L7$Q07_xm|<;smQ#uZ|Rh8K|$XXaD~FLR_cQ$+Bh3ShQ%75dY(k zKjy_3Ulg_l06hYFdwX?2>X(s`5lM0e?%%(!`~7|)uHWyMwgu2lk(^($WC;$3LwE+i z{`zam%gcp>J2`(jiP31pWQu6U3>-Ofgsoe*3UN=LKK*b8M@L8XRjXF%2M-<;o`FB( z@ZrOHN=k|_|H6d}`oO?|5O;b8Po6v}tnYHUbYK(v)Rn2pMFXp5C})%)TvX# z(FHIV48q))m>7&kqm;f3a&mGAJ9zZyQ9k(K1G2KRrlx=Pzj*NAfs|-$Y^*T%>8GE@ zY&J{j%izZ!f1Gmg%9ShJzI_{u#lpUQ`}p$9FTMWR8*bgY#r^yDrSxS`R8)lG^Z7V- z>=?(7A18l5KOc+5g5U4wn{U1WptiPFN>t`VOixcIAt6CZUj}}^A5&#zC7GF-eD~dV z;epoHR;*Sl@4ox4)S&?o6%{4S&CJY{(w4Q|u zos^W6NQq8NObBz^+uIo$8j`hJ7_qUjm<}B}#L9n_D`yNSio*Hx=SfUVq_eY=?Cfk| zQ9W{MI|CCF6H$1-seR{MJ!ynP}rWXuCB<*_CJBm zW)pwr%A{(t+Pbdmt*x!P(Q36?BSoRSyd1aNjoS``uzD#m*G6sWzs;Vkn zE*C3TuH^dl>r_-!U^E)Z%*>>@xtXS>CW65rX0sWG!$C(!2Pr8j=(^7F$Kzr7^5rx&HPO`6L?{$u;lh7~R8>`R;lc&-^704-0yrEFTrL+mIXU$7 z^iW@4&+zat%a$#}>2%WE+&oo={o7Fsg+fyNxzw(ysnH`Ayw|T^CpR}2MN$5a@)x9~ zr3vrB?c2B0*w`q%2ScF{cDtS8;$k7b-ENP3$Shc}fU>eO;f%kwww9A8PYQ7X?%aR5 z!(^t_b)DBVR)XXOS00ZL0tg|z|Z&6_vnpv`9E^y$+iCnwX@)y1AY zdvG`$Y}l|tN)RAAI$D^Un3zaROpJf@Gel8SQ}eJMeCnyEC@3i4jW^z4)22;aym%3q z+9xvb`Fz6Mv9U3!)b5`kyayc)2g%9FxZQ3#J3IO0lTQE`85v>8k|n}|p-@Pe8wdml z27?mj%Hfq)UU}$XM@I+o@$uZaaf6JEjBsky+}teXpiDIp7Z*3DOSG@A@1cK#@4x?k zxIbyNS~+mw0N;QAJ@e+xlMwq#(w9MFV_~9`zG5CBw-g@gTA$~9z6iy}#1_N<% zapA2af>0_=CE-I*N*lXl`!i=FOX|TD6KNpL`OB!@$SLp2Q6gFVdqD3SoCJOWa+}@lGV`F0vmuOpCn{KsQ^>=^Xc}FiRE7OODhIC!m z^@@rL;U#hS@L_%N;>A+SXf9#1*{1FXKlnoRklsw%0eslxoi!a^xq+S}Vv+-`TobV;Tbl!eh~le4Y+^ZZcd##g~eh4V03g;N;EDm zPMG`bv(HLh?(EvNOUjn&>guU-$!4=rUS7_@g9jNK8)NzM<$UXc?d>dHyjb`d z=gyszDyV-SfBf;;>p_RZA>7%9sU6 zpsFfeU0rCJMtXWWUayyymKLsFy^5-;7>!0WO~Xv<^`fdOzyJO_nx~o6Ux% zX=G$%U^bi4G>z`=Zd6r8Q4};yV{~*BRaF@o86hbtiT?l`QO@CFf#%2n0000 - + + id="metadata122"> @@ -31,7 +31,7 @@ + id="defs120" /> - - - - - - - - - - - A - B - C + id="g4679" + transform="translate(0,-0.32345319)"> + + + + + + + + + + A + B + +