From 773fcb6b67892d1cb68fed4db8f2548c34ded1ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 Apr 2021 17:27:38 +0200 Subject: [PATCH 1/6] z Stack description language enhanced. --- src/db/db/dbD25TechnologyComponent.cc | 114 ++++++++++++++++-- .../laybasic/D25TechnologyComponentEditor.ui | 6 + src/laybasic/laybasic/syntax/d25_text.xml | 84 +++++++++++-- .../view_25d/lay_plugin/layD25ViewWidget.cc | 33 ++--- 4 files changed, 188 insertions(+), 49 deletions(-) diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc index 2719bfbb7..88723050b 100644 --- a/src/db/db/dbD25TechnologyComponent.cc +++ b/src/db/db/dbD25TechnologyComponent.cc @@ -119,6 +119,7 @@ D25TechnologyComponent::D25TechnologyComponent () // provide some explanation for the initialization m_src = "# Provide z stack information here\n" + "#\n" "# Each line is one layer. The specification consists of a layer specification, a colon and arguments.\n" "# The arguments are named (like \"x=...\") or in serial. Parameters are separated by comma or blanks.\n" "# Named arguments are:\n" @@ -127,7 +128,7 @@ D25TechnologyComponent::D25TechnologyComponent () "# zstop The upper z position of the extruded layer in µm\n" "# height The height of the extruded layer in µm\n" "#\n" - "# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', " + "# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart',\n" "# the upper level of the previous layer will be used.\n" "#\n" "# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to\n" @@ -139,6 +140,30 @@ D25TechnologyComponent::D25TechnologyComponent () "# 1: zstop=1.5, zstart=0.5 # same with named parameters\n" "# 1: height=1.0, zstop=1.5 # same with z stop minus height\n" "# 1: 1.0 zstop=1.5 # same with height as unnamed parameter\n" + "#\n" + "# VARIABLES\n" + "#\n" + "# You can declare variables with:\n" + "# var name = value\n" + "#\n" + "# You can use variables inside numeric expressions.\n" + "# Example:\n" + "# var hmetal = 0.48\n" + "# 7/0: 0.5 0.5+hmetal*2 # 2x thick metal\n" + "#\n" + "# You cannot use variables inside layer specifications currently.\n" + "#\n" + "# CONDITIONALS\n" + "#\n" + "# You can enable or disable branches of the table using 'if', 'else', 'elseif' and 'end':\n" + "# Example:\n" + "# var thick_m1 = true\n" + "# if thickm1\n" + "# 1: 0.5 1.5\n" + "# else\n" + "# 1: 0.5 1.2\n" + "# end\n" + "\n" ; } @@ -157,6 +182,9 @@ D25TechnologyComponent::compile_from_source (const std::string &src) try { + tl::Eval eval; + std::vector conditional_stack; + std::vector lines = tl::split (src, "\n"); for (std::vector::const_iterator l = lines.begin (); l != lines.end (); ++l) { @@ -168,6 +196,64 @@ D25TechnologyComponent::compile_from_source (const std::string &src) // ignore comments } else if (ex.at_end ()) { // ignore empty lines + + } else if (ex.test ("if")) { + + tl::Expression x; + eval.parse (x, ex); + conditional_stack.push_back (x.execute ().to_bool ()); + + ex.expect_end (); + + } else if (ex.test ("else")) { + + if (conditional_stack.empty ()) { + throw tl::Exception (tl::to_string (tr ("'else' without 'if'"))); + } + + conditional_stack.back () = ! conditional_stack.back (); + + ex.expect_end (); + + } else if (ex.test ("end")) { + + if (conditional_stack.empty ()) { + throw tl::Exception (tl::to_string (tr ("'end' without 'if'"))); + } + + conditional_stack.pop_back (); + + ex.expect_end (); + + } else if (ex.test ("elsif")) { + + if (conditional_stack.empty ()) { + throw tl::Exception (tl::to_string (tr ("'elsif' without 'if'"))); + } + + tl::Expression x; + eval.parse (x, ex); + conditional_stack.back () = x.execute ().to_bool (); + + ex.expect_end (); + + } else if (! conditional_stack.empty () && ! conditional_stack.back ()) { + + continue; + + } else if (ex.test ("var")) { + + std::string n; + ex.read_name (n); + + ex.expect ("="); + + tl::Expression x; + eval.parse (x, ex); + eval.set_var (n, x.execute ()); + + ex.expect_end (); + } else { db::D25LayerInfo info; @@ -191,16 +277,18 @@ D25TechnologyComponent::compile_from_source (const std::string &src) break; } - double pv = 0.0; + tl::Expression pvx; std::string pn; if (ex.try_read_name (pn)) { ex.expect ("="); - ex.read (pv); + eval.parse (pvx, ex); } else { - ex.read (pv); + eval.parse (pvx, ex); } + double pv = pvx.execute ().to_double (); + ex.test (","); if (pn.empty ()) { @@ -239,28 +327,28 @@ D25TechnologyComponent::compile_from_source (const std::string &src) } else if (args.size () == 1) { if (! h.is_nil ()) { if (! z0.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given"))); + throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstart already given"))); } if (! z1.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given"))); + throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstop implicitly given"))); } info.set_zstart (args[0]); info.set_zstop (args[0] + h.to_double ()); } else { if (! z1.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given"))); + throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstop implicitly given"))); } info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); } } else if (args.size () == 2) { if (! z0.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given"))); + throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstart already given"))); } if (! z1.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop already given"))); + throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstop already given"))); } if (! h.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Rundundant parameters: height implicitly given"))); + throw tl::Exception (tl::to_string (tr ("Redundant parameters: height implicitly given"))); } info.set_zstart (args[0]); info.set_zstop (args[1]); @@ -268,12 +356,14 @@ D25TechnologyComponent::compile_from_source (const std::string &src) throw tl::Exception (tl::to_string (tr ("Too many parameters (max 2)"))); } - m_layers.push_back (info); - } } + if (! conditional_stack.empty ()) { + throw tl::Exception (tl::to_string (tr ("'if', 'else' or 'elsif' without matching 'end'"))); + } + } catch (tl::Exception &ex) { throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (" in line %d")), current_line)); } diff --git a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui index b1901ba63..a550fe8ad 100644 --- a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui +++ b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui @@ -44,6 +44,12 @@ Monospace + + QTextEdit::NoWrap + + + false + diff --git a/src/laybasic/laybasic/syntax/d25_text.xml b/src/laybasic/laybasic/syntax/d25_text.xml index 5e0877b74..b90e293f9 100644 --- a/src/laybasic/laybasic/syntax/d25_text.xml +++ b/src/laybasic/laybasic/syntax/d25_text.xml @@ -5,13 +5,33 @@ + + zstart + zstop + height + + + + true + false + nil + + - + + + + + + + + + + - @@ -22,22 +42,52 @@ - - - + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -46,19 +96,21 @@ - + - + + + @@ -73,4 +125,12 @@ + + + + + + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 4aaed12f1..e115b55be 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -246,40 +246,23 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) } else { - // compute vector of line of sight - std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); - - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (ray.second); + double d = event->angleDelta ().y () * (1.0 / (90 * 16)); if (! (event->modifiers () & Qt::ControlModifier)) { - // No Ctrl is closeup + // No Ctrl is "move horizontally along the azimuth axis" - double f = event->angleDelta ().y () * (1.0 / (90 * 8)); - m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; + QMatrix4x4 t; + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + QVector3D cd = t.inverted ().map (QVector3D (0, 0, cam_dist ())); + + m_displacement += (d / m_scale_factor) * cd; } else { // "Ctrl" is zoom - double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); - - QVector3D initial_displacement = m_displacement; - QVector3D displacement = m_displacement; - - m_scale_factor *= f; - displacement += hp * (1.0 - f) / m_scale_factor; - - // normalize the scene translation so the scene does not "flee" - - QMatrix4x4 ct = cam_trans (); - initial_displacement = ct.map (initial_displacement); - displacement = ct.map (displacement); - - lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); - - m_displacement = ct.inverted ().map (displacement); + m_scale_factor *= exp (d); emit scale_factor_changed (m_scale_factor); From 173ba147b1bc88aabaa406a91ae4b0f6037ade1f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 Apr 2021 18:48:00 +0200 Subject: [PATCH 2/6] Rework for D25 tech management - compilation happens dynamically now. --- src/db/db/dbD25TechnologyComponent.cc | 46 +++++++++++------ src/db/db/dbD25TechnologyComponent.h | 50 +----------------- .../dbD25TechnologyComponentTests.cc | 51 +++++++++++++++---- .../laybasic/layD25TechnologyComponent.cc | 8 ++- src/laybasic/laybasic/syntax/d25_text.xml | 2 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 20 ++++---- 6 files changed, 91 insertions(+), 86 deletions(-) diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc index 88723050b..62f9b24e4 100644 --- a/src/db/db/dbD25TechnologyComponent.cc +++ b/src/db/db/dbD25TechnologyComponent.cc @@ -170,22 +170,22 @@ D25TechnologyComponent::D25TechnologyComponent () D25TechnologyComponent::D25TechnologyComponent (const D25TechnologyComponent &d) : db::TechnologyComponent (d25_component_name (), d25_description ()) { - m_layers = d.m_layers; m_src = d.m_src; } -void -D25TechnologyComponent::compile_from_source (const std::string &src) +D25TechnologyComponent::layers_type +D25TechnologyComponent::compile_from_source () const { + layers_type layers; + int current_line = 0; - m_layers.clear (); try { tl::Eval eval; std::vector conditional_stack; - std::vector lines = tl::split (src, "\n"); + std::vector lines = tl::split (m_src, "\n"); for (std::vector::const_iterator l = lines.begin (); l != lines.end (); ++l) { ++current_line; @@ -254,12 +254,28 @@ D25TechnologyComponent::compile_from_source (const std::string &src) ex.expect_end (); + } else if (ex.test ("print")) { + + tl::Expression x; + eval.parse (x, ex); + ex.expect_end (); + + tl::info << x.execute ().to_string (); + + } else if (ex.test ("error")) { + + tl::Expression x; + eval.parse (x, ex); + ex.expect_end (); + + throw tl::Exception (x.execute ().to_string ()); + } else { db::D25LayerInfo info; - if (! m_layers.empty ()) { - info.set_zstart (m_layers.back ().zstop ()); - info.set_zstop (m_layers.back ().zstop ()); + if (! layers.empty ()) { + info.set_zstart (layers.back ().zstop ()); + info.set_zstop (layers.back ().zstop ()); } tl::Variant z0, z1, h; @@ -308,7 +324,7 @@ D25TechnologyComponent::compile_from_source (const std::string &src) if (args.size () == 0) { if (z0.is_nil () && z1.is_nil ()) { if (! h.is_nil ()) { - info.set_zstop (info.zstart () + h.to_double ()); + info.set_zstop (info.zstop () + h.to_double ()); } } else if (z0.is_nil ()) { info.set_zstop (z1.to_double ()); @@ -356,6 +372,8 @@ D25TechnologyComponent::compile_from_source (const std::string &src) throw tl::Exception (tl::to_string (tr ("Too many parameters (max 2)"))); } + layers.push_back (info); + } } @@ -368,15 +386,16 @@ D25TechnologyComponent::compile_from_source (const std::string &src) throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (" in line %d")), current_line)); } - m_src = src; + return layers; } std::string D25TechnologyComponent::to_string () const { + layers_type layers = compile_from_source (); std::string res; - for (const_iterator i = begin (); i != end (); ++i) { + for (layers_type::const_iterator i = layers.begin (); i != layers.end (); ++i) { if (! res.empty ()) { res += "\n"; } @@ -407,11 +426,6 @@ public: virtual tl::XMLElementBase *xml_element () const { return new db::TechnologyComponentXMLElement (d25_component_name (), - tl::make_element ((D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::begin, (D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::end, &D25TechnologyComponent::add, "layer", - tl::make_member (&D25LayerInfo::layer_as_string, &D25LayerInfo::set_layer_from_string, "layer") + - tl::make_member (&D25LayerInfo::zstart, &D25LayerInfo::set_zstart, "zstart") + - tl::make_member (&D25LayerInfo::zstop, &D25LayerInfo::set_zstop, "zstop") - ) + tl::make_member (&D25TechnologyComponent::src, &D25TechnologyComponent::set_src, "src") ); } diff --git a/src/db/db/dbD25TechnologyComponent.h b/src/db/db/dbD25TechnologyComponent.h index 5e4a1d51f..3beade105 100644 --- a/src/db/db/dbD25TechnologyComponent.h +++ b/src/db/db/dbD25TechnologyComponent.h @@ -76,55 +76,8 @@ public: D25TechnologyComponent (const D25TechnologyComponent &d); typedef std::list layers_type; - typedef layers_type::const_iterator const_iterator; - typedef layers_type::iterator iterator; - void compile_from_source (const std::string &src); - - const_iterator begin () const - { - return m_layers.begin (); - } - - iterator begin () - { - return m_layers.begin (); - } - - const_iterator end () const - { - return m_layers.end (); - } - - iterator end () - { - return m_layers.end (); - } - - void clear () - { - m_layers.clear (); - } - - void erase (iterator p) - { - m_layers.erase (p); - } - - void insert (iterator p, const D25LayerInfo &info) - { - m_layers.insert (p, info); - } - - void add (const D25LayerInfo &info) - { - m_layers.push_back (info); - } - - size_t size () const - { - return m_layers.size (); - } + layers_type compile_from_source () const; const std::string &src () const { @@ -145,7 +98,6 @@ public: } private: - layers_type m_layers; std::string m_src; }; diff --git a/src/db/unit_tests/dbD25TechnologyComponentTests.cc b/src/db/unit_tests/dbD25TechnologyComponentTests.cc index 76080ef09..b83365ea4 100644 --- a/src/db/unit_tests/dbD25TechnologyComponentTests.cc +++ b/src/db/unit_tests/dbD25TechnologyComponentTests.cc @@ -30,44 +30,73 @@ TEST(1) { db::D25TechnologyComponent comp; - comp.compile_from_source ("1/0: 1.0 1.5 # a comment"); + comp.set_src ("1/0: 1.0 1.5 # a comment"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5"); + comp.set_src ("1/0: zstart=1.0 zstop=1.5"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - comp.compile_from_source ("1/0: zstart=1.0 height=0.5"); + comp.set_src ("1/0: zstart=1.0 height=0.5"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - comp.compile_from_source ("1/0: 1.0 height=0.5"); + comp.set_src ("1/0: 1.0 height=0.5"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - comp.compile_from_source ("1/0: zstop=1.5 height=0.5"); + comp.set_src ("1/0: zstop=1.5 height=0.5"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: height=3"); + comp.set_src ("1/0: zstart=1.0 zstop=1.5\nname: height=3"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=1.5, zstop=4.5"); - comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: zstart=4.0 height=3\n\n# a comment line"); + comp.set_src ("1/0: zstart=1.0 zstop=1.5\nname: zstart=4.0 height=3\n\n# a comment line"); + comp.compile_from_source (); EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); + comp.set_src ("var x=1.0\n1/0: zstart=x zstop=x+0.5\nname: zstart=4.0 height=3\n\n# a comment line"); + comp.compile_from_source (); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); + + comp.set_src ("var x=1.0\nif x == 1.0\n1/0: zstart=x zstop=x+0.5\nelse\n1/0: zstart=0 zstop=0\nend\nname: zstart=4.0 height=3\n\n# a comment line"); + comp.compile_from_source (); + EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); + + comp.set_src ("var x=2.0\nif x == 1.0\n1/0: zstart=x zstop=x+0.5\nelse\n1/0: zstart=0 zstop=0\nend\nname: zstart=4.0 height=3\n\n# a comment line"); + comp.compile_from_source (); + EXPECT_EQ (comp.to_string (), "1/0: zstart=0, zstop=0\nname: zstart=4, zstop=7"); + try { - comp.compile_from_source ("blabla"); + comp.set_src ("blabla"); + comp.compile_from_source (); EXPECT_EQ (false, true); } catch (...) { } try { - comp.compile_from_source ("1/0: 1 2 3"); + comp.set_src ("1/0: 1 2 3"); + comp.compile_from_source (); EXPECT_EQ (false, true); } catch (...) { } try { - comp.compile_from_source ("1/0: foo=1 bar=2"); + comp.set_src ("1/0: foo=1 bar=2"); + comp.compile_from_source (); EXPECT_EQ (false, true); } catch (...) { } try { - comp.compile_from_source ("1/0: 1;2"); + comp.set_src ("1/0: 1;*2"); + comp.compile_from_source (); + EXPECT_EQ (false, true); + } catch (...) { } + + try { + comp.set_src ("error 42"); + comp.compile_from_source (); EXPECT_EQ (false, true); } catch (...) { } } diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.cc b/src/laybasic/laybasic/layD25TechnologyComponent.cc index 5c7cb2c15..753d67c05 100644 --- a/src/laybasic/laybasic/layD25TechnologyComponent.cc +++ b/src/laybasic/laybasic/layD25TechnologyComponent.cc @@ -72,7 +72,13 @@ D25TechnologyComponentEditor::commit () } std::string src = tl::to_string (src_te->toPlainText ()); - data->compile_from_source (src); + + // test-compile before setting it + db::D25TechnologyComponent tc; + tc.set_src (src); + tc.compile_from_source (); + + data->set_src (src); } void diff --git a/src/laybasic/laybasic/syntax/d25_text.xml b/src/laybasic/laybasic/syntax/d25_text.xml index b90e293f9..13fa5afc3 100644 --- a/src/laybasic/laybasic/syntax/d25_text.xml +++ b/src/laybasic/laybasic/syntax/d25_text.xml @@ -24,6 +24,8 @@ + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index e115b55be..10ca0d720 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -418,17 +418,17 @@ namespace { const db::D25LayerInfo *operator() (lay::LayoutView *view, int cv_index, int layer_index) { - std::map >::const_iterator c = m_cache.find (cv_index); + std::map >::const_iterator c = m_cache.find (cv_index); if (c != m_cache.end ()) { - std::map::const_iterator l = c->second.find (layer_index); + std::map::const_iterator l = c->second.find (layer_index); if (l != c->second.end ()) { - return l->second; + return &l->second; } else { return 0; } } - std::map &lcache = m_cache [cv_index]; + std::map &lcache = m_cache [cv_index]; const db::D25TechnologyComponent *comp = 0; @@ -440,16 +440,18 @@ namespace { if (comp) { - std::map zi_by_lp; - for (db::D25TechnologyComponent::const_iterator i = comp->begin (); i != comp->end (); ++i) { - zi_by_lp.insert (std::make_pair (i->layer (), i.operator-> ())); + std::map zi_by_lp; + + db::D25TechnologyComponent::layers_type layers = comp->compile_from_source (); + for (db::D25TechnologyComponent::layers_type::const_iterator i = layers.begin (); i != layers.end (); ++i) { + zi_by_lp.insert (std::make_pair (i->layer (), *i)); } const db::Layout &ly = cv->layout (); for (int l = 0; l < int (ly.layers ()); ++l) { if (ly.is_valid_layer (l)) { const db::LayerProperties &lp = ly.get_properties (l); - std::map::const_iterator z = zi_by_lp.find (lp); + std::map::const_iterator z = zi_by_lp.find (lp); if (z == zi_by_lp.end () && ! lp.name.empty ()) { // If possible, try by name only z = zi_by_lp.find (db::LayerProperties (lp.name)); @@ -468,7 +470,7 @@ namespace { private: - std::map > m_cache; + std::map > m_cache; }; } From 9c1ef3c6ddf231935eaf59cb5e3e4235059317fd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 Apr 2021 22:58:39 +0200 Subject: [PATCH 3/6] Enhanced 2.5d navigation --- .../tools/view_25d/lay_plugin/layD25Camera.cc | 2 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 64 ++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc index db7d059b7..a9a585e7e 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc @@ -43,7 +43,7 @@ D25Camera::~D25Camera () void D25Camera::init () { - m_fov = 90.0; + m_fov = 45.0; m_cam_azimuth = m_cam_elevation = 0.0; m_top_view = false; } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 10ca0d720..3238edb1b 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -256,7 +256,7 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); QVector3D cd = t.inverted ().map (QVector3D (0, 0, cam_dist ())); - m_displacement += (d / m_scale_factor) * cd; + m_displacement += d * cd; } else { @@ -277,8 +277,65 @@ void D25ViewWidget::keyPressEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { + mp_mode.reset (0); set_top_view (true); + + } else if (event->key () == Qt::Key_Up || event->key () == Qt::Key_Down) { + + if (! top_view () && (event->modifiers () & Qt::ControlModifier) != 0) { + + // Ctrl + up/down changes elevation + + double d = (event->key () == Qt::Key_Up ? 2 : -2); + + set_cam_elevation (std::max (-90.0, std::min (90.0, cam_elevation () + d))); + + } else { + + // Move "into" or "out" + + double d = (event->key () == Qt::Key_Up ? 0.1 : -0.1); + + QMatrix4x4 t; + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + QVector3D cd = t.inverted ().map (QVector3D (0, 0, cam_dist ())); + + set_displacement (displacement () + d * cd); + + } + + } else if (event->key () == Qt::Key_Left || event->key () == Qt::Key_Right) { + + if (! top_view () && (event->modifiers () & Qt::ControlModifier) != 0) { + + // Ctrl + left/right changes azumith + + double d = (event->key () == Qt::Key_Right ? 2 : -2); + + double a = cam_azimuth () + d; + if (a < -180.0) { + a += 360.0; + } else if (a > 180.0) { + a -= 360.0; + } + + set_cam_azimuth (a); + + } else { + + // Move "left" and "right" + + double d = (event->key () == Qt::Key_Left ? 0.1 : -0.1); + + QMatrix4x4 t; + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + QVector3D cd = t.inverted ().map (QVector3D (cam_dist (), 0, 0)); + + set_displacement (displacement () + d * cd); + + } + } } @@ -940,9 +997,12 @@ D25ViewWidget::paintGL () vertexes.add (-0.25 * compass_rad, 0.0, 0.6 * compass_rad); vertexes.add (0.0, 0.0, -0.8 * compass_rad); + vertexes.add (0.0, 0.0, -0.8 * compass_rad); vertexes.add (0.25 * compass_rad, 0.0, 0.6 * compass_rad); + vertexes.add (0.25 * compass_rad, 0.0, 0.6 * compass_rad); + vertexes.add (-0.25 * compass_rad, 0.0, 0.6 * compass_rad); - vertexes.draw_to (this, positions, GL_TRIANGLES); + vertexes.draw_to (this, positions, GL_LINES); // draw base plane From 53f2c0e28fba96c12c68728dedd06f4745f72409 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 3 Apr 2021 00:17:28 +0200 Subject: [PATCH 4/6] Doc for 2.5d view --- src/lay/lay/doc/about/25d_screenshot.png | Bin 0 -> 66927 bytes src/lay/lay/doc/about/25d_view.xml | 147 ++++++++++++++++++ src/lay/lay/doc/about/index.xml | 1 + src/lay/lay/layHelpResources.qrc | 2 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 4 +- 5 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/lay/lay/doc/about/25d_screenshot.png create mode 100644 src/lay/lay/doc/about/25d_view.xml diff --git a/src/lay/lay/doc/about/25d_screenshot.png b/src/lay/lay/doc/about/25d_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..4515ae7225e5be96adb654cdc0d9cadeee7f7843 GIT binary patch literal 66927 zcmd?QXH?T$*DuP}Z37V-s34%yLsNRk4M^|3hu)iX0wlT-lrEu32O&TbLZk%;MJ??SJs&M zgKp;yP|LoD*`OO&?*@Z=Ns8%MU%o9aKe!gjO!M1Z{&gcY@x|5UGL^eDG>?H6L!8>bD0*@072mHX zeueHZx?dMGJ+QGHlVnML4Q9QjU9ax>gStO$Mr&<-O?}tY-P9fwua|mtIdZsXuIF#9 z99_XB_4lFY*^G+MQ=6OD*baFtqgbzm+0p8+y3cZ5Au!yH?_ODS%9%L zH2j?{7C1^HDX>QmS2DIQP0{ctTceVX_r0-Oczk8v=`$gAnja;@>s^T{fH}mLtkK>$ zdk{DE-qfPu&EpF+<-JY1P55K`$d$>0W!I2dxD{kq@MNe7Y!t~T8z;GPjBBtBnF&X) z3mwnE(aq0W0Kq09uZIW5Q0if7!DjK5r^DVygq9Ez7T2oIb+0SDa`r?qgtXoSdC!1* zz!|c`?Y%;e3>vyeB#*6JXP}YdItGVV2PUNTphg)WTA%pwk6kBzr2FcVUgJz|mN!>g z_n68jWSyfKzTNtx_Xru%Y%Hiu+5LLH#u9QGhCSAS@Z*1Mth9{L!vmQ(GC!xhv7UIc zc5)~$b5oMbIdFCJ+wtsh?qFp740;9MpqYnzo*o=I_s>khH~9s53ZTU<&uXHoDR3(4 zx${lnn7D7NUQ1US$UjaxorY%6d2V7*_FDxMcU+W>-?{?tLViKvQ#dbFMzp-&IB0MH zQe?XXykoIgX_JuE+xE0H`a|E&0|)WV8!m|oyXC5bK6$6FfkTq`3G`6CmwsxD@pb@# z79mYc=WkI{Jlf^iEsovxn>Tcu@+Hk|P?TFXD|8!qRMWgFhqIjk!5eykV+V{N3ySYD zf=&@l+0a$rUTH=`jO5oVbf=%kgY({6dkc@LwQl`mBSt>ovQ&3g@!Eweq%-K2fGZrFSSImC(7tyHLwDb?u-Z_H=4;6_qVPypV)ad z%^ntN1BS?$t^3$~R;*pHQG_bjHwcfttM=2d}6+Xi{pZ z_S||$B)fR(X6i~R(meHEtj%veGe|7q3ey{E@1J_07i>vklYJj}SY3H7D7XPuDhTR* zt$<)SJ=`F5d-t{rfj=#+*qj}JTYfeNx9`oMC=9w9BYo+2A^MUhyA9Rrrt)XRc*cy9 z9~VFabl8PHIph@hxEyeef6r%#{dh=`WFx$iN!ql*lAfwsRf01W4(H6uJH_FbHfG9d z|CQOvMR{y{4ORwB5%zq>8eIKfB?s zHzbqJ5O}EH{=gY7=@UoQ3^#4Rc-xbKexjc6RriIyep0+5>)3PweY6`pkM@tNm+Gmsm_}6GN z(qOPr+djVdyn7}#czqbVZXsWI*n zoeeE{7tt;#o_DBrlx%hSbpevpYJeU}Xy9i}zuNqb1CB~^QOuQsVAGfX^E4>r`3Kg_+ksQc{PUt!Jw3AqaMQCP;>5&{p& z+#B^g>AJR3HZ}vR2W~!+-(XNE9m0MVQS%)nE3%`$1GqIxZx$D4_hoy4_g)_@xcZDd z=MQwAXATwOa4s0i3%Nf$g@4{UyX%tKtatjl^|;vPX!VjUOmWpCB?eL`%f_Sm*T*go zfi*)pl3{s86Yxj(nAv>}=folagYhkQv)(Pko4-uOYs#(}QAc<+jbFB;s&oc4btyy( zT0OtNGIa)OQt~~iZQXob#^1DYMqC4~4B}hMfSm<0MGa*}viTwTmW)GDA##P!jYQZ_ zi@<>kFwjnhByk9cuY6q)>>1#STKT4!ft z5l8Kyp?m1>O=te8dvdo@We1Bs76ZSFC#xHp_RpLe$!}p4hosl;$sgiFNU`A#Ac3GP zA$L)6zcoGb!EyiAWg2rX!&0sK_6Sh=$(JUf`g55$5lL0zo+n)hkMCKlwSU*yG2Zki z<_wTEu#tbycV^$=+ijXcZauvM&@{AXs5k%(&H|I14{`i`hB;HiogXGXqlMaieBFQ^=L1o!w)+oEdA!b5?L* za(<%OJz4Q4J;j65HqCcX=>S}1%lcan3AcOUyCeIjb*=tAY&vzhaIJhuY_QK&UT=`o ziGa%`Fd~QjiBoCva;0p60cbT7J8z)uvgkguJue@)b^b-_H2D^|y#^B%4MjOmfZgZ4@T3*YMGDb`(70qZ#prD{u#^7HxAK~*ekW*L9-qJg}%(iGV z;PKBpci<}0kkchd<;29qg_aHv!OZ@t65)s~AQSZkGALZIxJN^ zpms*HVVk|8*KC7xZLjN`dorAP?@`%@G^}xtoX@_a4y}w7DKTpyn$QRBG|k1Z`1fdn zmSpjb%Y^K_JVCxO7EC7#C_i$dKH{{Gi*GCwckHYIn=0fU8m5<={!l>xDMPFi19zJC zo`z~Db`E=iPN%ebY9Oe<#^uHOH#cKnadUGAo*pjsi1lJ2cdY$hQkO7YU2JsYmvVe0 z{`1aKWh>PB)~#Fg7x+lN4h~9Np^j?qb{fR4jaD^bY1d+qWG?K^$%>s2-rkjYJ*cMXJq=4_NYq zs1do9p(ZZ(q~$(A8tx1gJ=#U~<&FhhZTp@_MaphsSQVHhD>a}vqEJR=XIp=aX`T|< z$r(8C)6A0|)DbbMY!|%|S~h#cU`3ACv#!*K@i<)ynh}ST__EV|9WiW{eZJswLgG;< zf;62-_ujo@8X+8T9DGtxA7P*uYE888>!OAvA2NpNjS6swPY(N94P7tIBnD2%!C(&a z(0q^3r0||ml-0=SC`K&W^4T>uxpFO`#uDPZvf=?Gs2%S_9*{j%Mw7(J!;ofM;S=J- zQHMAC2+ZCSp2l`CyDuKvjwhWpZ)?{z_6<{EtcMLH^9%+AFZj1e>7ODjCGTGE>-(~Ep5%W3(eM(_f+npVomaTuf_{QgS^V0739$fkU#@YEuM|&hy>|cnN z`n+S#OK>;SQaL|Fm-A`f_-#H}bFjHB4Zo3z-vGuvLIJAGD;DSF(WfN};Gy)R?|5r; z&uM=Nm50fs{Z_J|)mvzqd=XFn-LV+_c##{}+I#t1KyBEO+T^~qs_^V0Us&&I)dumT z;K?q}8KrX)OiI>bqq9EdIT~&^T>P;FNb49rKta&St7oi{1v=(cR zXeu_DcSn!bAF?P+ZNUKqU8YLOo8=XUn~@55z8j^)VCTU7<&lEp1|RIt6VRb&4J~d< zk&M#%4C{F(nw_Loq~7rOM?YgMpv1l1r5n>IU1_Dbb&npmGIi9}tHYeDXP?SI41k;! zR-W!E5}RMZqjtiCsDtZX32GY-2V%7fS`IJD%gI#zN4N%&eFEA-1Ld#T!J)m$Ssp$< zrVt2ZhG0EoMhp@TT)}$gkoWle^JOn~Gn#U8wwKL!107Tece3-?PD);^bpxay~1EZ~E- zm<&lBqoJXpIF(1_JGO3(PQ*zbeqBa!HHjPp0fOOheeKM9XqPqmJ&R96Q4yiWCD@`T zXzgVOX3Re_X=lc^$4baZ(i(D7of)6HC-h#Qw&rcg zKAjWx9Ztfv*g$rO?3^aZoUD<=h=W8YhA*OBp z6V=Y~d+ReuV?bRk0VGtk#+rC0v0v}JJ3jErFbhVJ?{}%0tZ_}o_{>ah21x7|z&ew- z&8Uu%5MbR-n4}yyP7b(;9p|1dzM^(h!%KMY?2+OKSVnjE(f!dl0gT^s6M(86d34sc6y>^ zR_XXWTE2#OSsZ*-k=YN)|85}PF_w#ejJisUw9$*W7A&^T)i02o?is7tX<}8`lFYvv zhvXOz-T2`45hYYmbV6YRZc=WQW_+ZY_NDYms?IVI`=W-9dXdbeS=ai2CEc0O`9v-K z_qk1b$;*hmydCgtC0W7dj5No^YtSdZKV5vMbu*g!v@f7W{yZfGX?uag9qHY#RT0d(47hm85rkWcjGsPZGT-&Xsb zp0!}py71W@CWY&`?4F~k1s|{3Sm|J2ciZXD;}xT8&l(q(s=ZPC*#AOGOJq>cTHP3N z(i0tY%>44H-3_g@c`}HcSHS)~1PuPTDR^oB)0t@L(!L|mJFD>Izcn(-+i0hV`oPnN(uf>6#h3n{vUVo|A4Lk|I#?yl$v9t z7E@C(W)sevDV#cbdXl@j<^q_sXEJ=ho$`Dvz~>8lJ$o9Bt~CWj^ELPr@9#P$YO6!T_hz~M_XIH`$CbQl=Wp} z)5nP0xrk`EYG%#9`f_Y$ytfgDpivRR>Zg9T28XJ5=U%?RVzISUEBPnDYERdM?S5Bg zaHrz$Xi9|;c*K0>_~ONj00kdk0)qQCEu3mJL`6lJnVAg*n2`5E5sCc>a;}>g;`HuhoDcMxMo&&`TYGrIrURoR>m(RD}*l{&&XhEoLT=|U0hr&4n)s-TADg) z-TBS8rzTV@hTR?1VzHFCd)4FQh^)zZlen@n@bdg6j~KWr(5ySUdSGBcDc-7hsm)UJ zR|1HJM&AVWxc2(a6+6 zw})lfO$4c~0h*@H$=U0zLtI|=yIlg%QK({e+4;|MBru9C-b%>e$d$TcNTnF*&#f5y zJIb`XXAN3`?XdB1rDHTLY>rKpk!dQw1I-U#aYO5I!X1$gz}>5w(3`O;%02u0`vt=>Ynk>4iAg-K$iDGBFS!D3<2 zHR6hjilPdVf749#(tX)rFWXy&>N`0ikz9W`qz+XodQ1IKeNma4n57#%wSQABEiL;- zl}84b)%QqHF$sxMyT|62fuu3C*`t*2zYt$}1`%d-~LHu@Yrf&!jZs!6y8N(=Wt( zXPK!u4$H{bM%C!Rjh21WhziScD(uyS8A6TU$vM{9{}zl79rV|(0hkk+EXyulx|G>+ z^*?%o)P0kp&bd48n9RqP%%7run=0m=)+o#qqyJD+gQbwkWH=g)_IO97D%yQG^HGEO z%U_(Nnigo>5)`mOdpA*)j)!5kHL^)nO^wcm$&BtdYyD_HSe8}1t-gseGITFQh-Gv; z1g+6UE73+PP1gJ38F2G^@Y*5A3yAh zg1WJ5!x87w#P%c1c&uJj|FJ`556WgvsN`y$dRWB&)t&TPxpMzqoSNDA`0{*^RY6G| zd*L7B@R1ME+yoRabGXy0`p1D8y=!SIV9b6$;5I{JEcld~R%v&r5@-4E`W4jrf`6AA z{)ax-pncNHiUIF!s$<}nQ+GuqSh=g&`KgOF4=LzNB2`1Z^CL4Oi$Zhn6HbAIMa zc_I1L6BR$V8aU4x3s!DzJ+GOJ^7i)bv0(b8&-`99*dQW_H1zS~3#uE@!XD>&P}3fs z3c7!CiT{53^p9l`^MdWl?`+-2u3x)%)bjrd;`!eMuKY2g|7St8|Ft;}+h`mA*I9ty z>iU11{{8;~qKcscslg02zowy9Yf?s$)moogA!cyOJ0>u#hfm%esHhgo;|e8Kx+G`f9ax3$0bhgqRhwM^<0Zsf-m%+HO{M+`H`TI1T7~ICtLM##>H#=LEo8 zKx@ZPN4a75vbl$Kr>RuLMBC4yj<2*CPC+dhY1LT8;Lvtuyyow&KBn}k zBPFIRA7>|8kNC&qw4j0GcZKAFImHdPjwYQYYSZun7nw-+z+tr$V~wj*?%kP{p|WqG z5koqS+l?hYka$ib#kkWI#Wnhr*wyCz{D#R$;A_ku?LAzIx?n>iF##Pq5K zpP5fq8{9q;vTL>@=wm~V?w?kzH)8t@PU8FYRqewx*IANX#3~a&vJ!5pP;GZr!^ecd z4g)mjiZ#At8s?u_pa3R`WPs+*@nu#>lv;WlnF?j?W@0W{)JHzSBd!Sl<>>QIQvS~w ziokE{cnK5Mv^X8&7u@g}T8?-p%PBtjWPQQmVB6CKQ^~_p(}8LacAvsPr%-%oOjtk- z7hA5pLBp9oVwZxsiC6r!=1*DkQcO?wI^sDA#4p~el^dqv-N?6d8AXGYmY22}Tcc{) zu%3(GbAQyCDw(0y?LSq=-o4qHDy1 z6(7}tVjXoR^JI!QXGFQ=bC`#WDjni2a^{94fLl!=-9Ls9_f*)=l8z82J zmAdXWijUlf-ssu4*_f}d^rfBa?wuY*mk0CIYe6TC+6k+3q^B}w@TMRaTz(eo5FBB6 z5GoYp@67=5c%EJGFC%tDV=zE#xUQ08(sXA_6J^LR9$!G7@r-R;;3Mfy+;cZ+7B#Lu z7R8WEuggY$DVvcgUI-6e`u5MvJw>sN?cWZBY2qC&bi43LK~SXX(tWxd+$E!Zqxnr5<5VsgL5*ofGFG95$>ONP1 z`UAsWb0EP_X(6K#qO@k%{nC_7dD!G-F> zjC{8zFXZiI-Jl-C(GPJb_z2^fvh~1pMX{<&A0~i#T!6k#bHY-@48-|M(OwvReS_z< zmFT=JE;kwcK6D{svS7e&r$SdNW~4GW=pH!o;qF;EKl^7WKzF`bn7tgT=?Mg>D{8Ts z&!$_b)&WBIwhT*=AhY64=Jjrjao;9|(T~tkAfFB}-7E0;@Okz|qDQ#G->`2Eb>U)X zcXZM(zF9n8i$`fqMKa$P5RfG7m^8E1T&R7iUg9_7ob8|Wyqz}Qg)2R2tx79Yr_oca zI^lr6PUnJVYqH@wCA^%UkIxz^g1t)dzmXUI2;nU>Rl*x(XlSbw!xLC5XmL55%H@}7 zTDFY)HU&R5H6~ZJJZppz@v<_s$>N#b{oDOr*c0PCLWW_T5UiFOvcI?xe<+&0=UUsZ zXyOkU-gN7X9o$k_)0z8BZY|9aXD!Y6!t9n#%>=*vS$<*sLLn8>#qWg2h};@afa~b4yCx6Pp?vP6=~K^nw)?}dEiFGi(=Q=i+P+`Y z5QL`*!K87`iitpDX^?LH-ysF`6e^P?@LJna6h zPiGdcYZQ>X55H-?Dul=AcB0m~;0XV?6=E0)Lyy=Ek8Nz+4VX zGw5dHn0p^+STx7;Vcz!bo)7i1g|BeM*Z-~1@j}0-Uue?~0B|8ug!VnMlxan_-lSy` z_YTSSW}sKHnIZfcu{=mE%Gpc&YNJkaX~d_`B1Wx5l<|lZ>QpVUJ5^ZhHm`Mi-gE&F z&(!wr)|(P*H|U~|EB^=GNenD9r@cAFd84WR?FKtlG(S8F27yKxeN2*X@J@#$m6Z{< zlU-wKdGc6LA9w;Hx_auFMwn@EgY(yPxkoVbN|-rApR=BNZzC-LOi-b~(i_DqDE{w| z{gmTq5UH9geb87e8zNTB2Nb${d+bqVQMuS{!I;Y$9$k7c`FWoqtjbn6KJ}XoLt_An z>){a6g~ROG8U*v~N`Pr+lxTD!7=QE$F|*oTXKW7iWU1vzg_ekxmM$t745~!`Fgdkk zB8l^C_cVI-7wjI=I!9D~nBnBJb<6X361OCkz59CDp#gDG!1FBL`0}%%({H(yMMonq zO`{sj^>@JjLi=*dZ>#t2!4I=XgE78ONZ7j0tCSPXg9zq$Rf33o5W`brqDa%*E*{K5 z1n7CI)#BX6YrNyX0$~0}Q*8RKA-;10>@8g{eyXNo^ili+w@qK}9;|FLIb&tx@V%GN4Uq2-H2M+bI8-{P&bSbtnTXV4=5 zX1^wA)HbWXUZ8dnI0SRxUBz1wGbC-V5$8m%KN;qw2pzYk4QRd@T5JhsfPrA#3)_I{faX;&*dRzi}kK zPKcW-oDBZ(S*6MTHXHg2=nHccS6^v4~HfpVm>xY*uP-M|Ywgjft zTvn4MuQjeUt51)6p`(D}&;E6BcJfTxFfr4cVx#~N&3m~wSt5j?S}<~z!UHejlcws& z3zNCf2Yl$cF>~-JLIWZL4C^+@03{ZVa-H{hlD#W~myTZI`c`YZK4VaxtB@gG*(nlr zMBTG>2EMKn$hL0Ysm@J6=sdNjcahwo08z#BRHybh9jQ_YFhm*`1LVgn8Iq+#5))|0dN!=|dk{UaSN&xY zZTh|<@GHPn?&CTROycF{56>OUoXjZ zmc28sQ#Q6uH*GF^Zvd|tDM2Y+Xr6gIJWpow+1NDA*M8a^U70jzeW&_44(N!{7qxY* zj`rU(Exm%n)m9vCYi1iQ2DiQC$q}qn&J!4MkV59O?emO?<5R0l-=)1b7|CbUf6h{y ze&O45E4ty(L~nCXZd^1%!Ue$ec-O8Ui=gD%pj*=WFej_t2^yKm4~g9ynQ>FXb35)I z6=I{apx=y(?$4k4Jr6H6WZsWX+v{EEF}RP+GSuhD2FB-$>#oAxj715k@b{i>$BiJ> zM>2*Do!`*JiqX#E$;T-IG-qW9FXU#(6o@vwoL$Fa2H>43}K=!_@f z%Pjnqt80ig25*c|Dq(6+XwF|F_SGl@!=3#s>4%D_=Qti<<@9*-705aT^gZL-)Kz8g z4tE1YA@j#g$;DsnDKQjiPkGtBx^mU5>%5-XF-u)Pr&6QNfL4BhB7noU-Qr zv8i}VFIEcy8xt&CaH%o5!p?}VL5i@l%)8RPR~u(jI!Ujbis4vZizyP#i8EoR1ofn1 zs#$fN7RYL~3O9zi=G(grj-ctXXZPs|*t{S`S!8k1yQG-X;r!Z;E%B9F^L00@yC%3j z&IV>JbT6hBELG1SDK1I+A_oXaKWE0ZxAsVb_0W6%N$=-K85)gS40*1VRSW9W0=(zgEIkSHcV(&Q$5 zv&CllOfj=$&4hdY!+2CZdBSr49`xGSgzuF1)O0I5NZZJ!%c=K=Gd|OraTXUk!`7+< z@X2y#_0DL9JJP1N=oy`s^0`N_;pM5>Xgm6ei7>3~O=)U~)}``#lqW#+VY`Xt^Z1-b z+Aqi){~m>0*5T9%`$*xFaghwBbnW!}Cf+3;Pm#lM0R#m&tB!383W?NzIAdE($}(oQ z-0SZnb_Ig^PIMC|02Z$ondom6xdbWq4?xE(3a-Kh zCrWy6{nKlH=IC#s@i1(J-FN7aIO@g=ud?&GZ#VAj<|t=oz_N37Nmz|D(<#)W{|)AD zM>n8=j7i+C&jA?wrD8NLbsU3rCb7tgdSs^<-B?qqM&jWhs8CnG+^PB{Q)Y(D{F}bq zcjbqn;xT{_qZ+nU<{T&c*5qEZVw53l<~WXP(-ZOY9bZ$Wlc5G-bjstT>YCuUgA6bVuILbLQ{2VC=>yvLmS6^?)e2O>6)!liuu= zsAO?)gI9T>Jvq5(J~2F z5|7%5kNpWOf+QjA+bg`Qt5d`=8}OmEO>~vg2*$wN&^%jqR3K{@d#hbTL3Nw*g_^_& zv|IlcUzfIF1g#AVHFt;qR2VP!OB@x}2Gw`@xoay8qr@JOa>7)nt|@ zsfOUJlrYm#L{<0f!d91wW2Z-s=#4nB8@w?s#fMR{YUiIfa?YTw-(uZ9G!oxE5WQvC znV>~9?CrYYk$A^a^B=fAx2GpX+emhM{?%&1+#5V_J1gPf#NYY6zwAM^?girNnUKl5 zccpHR-WiwKF8~fq_O#;buI-K9pnxpK=v-eSHt$V941NlkZDH21Cl+m^yX0u5){2sM zswbo2i><|tFPwST_P=r{zdR19A9^hI{=+p-tr0R5p{HP9wRiHF3|O7w@VGlECZ6ue z+Fft0VaSVz`R0^-%E$zr)@!&UyEKWFb)ookX zQx-Nf_+7utu?<5z;`cz`e+u9aGrde{D42Wt^pf(x=hSb2CaakMUla4f+1a%{?d9ms z07sPW!3oz?^=NP&0|lGmJ7t+{5`j`t(&Sv6#;0m$Xm`9#t5uvVF3a)Mlk(2{5b^|v zx6x3x@}_7UDm-G`*P_gGFzy*>9kyEf7sO2o85WbQe!+ySsIsY=w`9o$KtXB1e zE%69eM#$`TL6gKT(m5M~5gS5n-~CRT?u#FL7*xUrvIpe~`M9`jA{?qWTiaf~xagU{ zTAexc);LVXWxydi_k;bgS2|)0VT>~AK6mT#d=++on-6QDy0^N($K3x_hPUK@QC(F zMdr1jhUbTm5#E!xt1-Xk_eKLHvon8)8qeoE1^PKLMgbrTOV z4$fY+m;fLXsp?;I`l3^3X&*j=>Mqo8#6Er6_VQ!yl?PbTwCx+GbP{9Pr;HMSk&|#V zk*)L=Jk`O!ipwz0bY;x7hb->+;P};!hrF3S{7G zfGQPotS)C8ZDD}GOa==Q;uT7Dc#`c3yyU_IzSp<-#{W&@Y(FOJJz6!6A26=F57rsZ zv;NdKDS_%Lx!LWVtlOqvwpMiw2c(1pAc|`-J(fA$`&MwV)r<`sbT|Vi{LtM|gpcH> z{h2VBbtob~FoQBbk6%;ewtJ_iWe9s*ciST|$g%>z4$XjHjG~6Eo|<|eOdyL`l`@{s z*zvlexuxC)Lo8mdRPhO<_7$wnpM{}M^HyRmZdxhLGM!mw%s9wq?t_e^Pa2;UK_<&s z&*E7VG+_HL(tWG%)#pa?11-Zvv!E2F&v|5zkDeZd&ekFI33twoti;-{-KFb?N9vUA zC?2ioWE=P#K{8#n*S|;6x`OC>p6!T!qi%BbOP}?=Wvn~~(-Zh_r2lJL`>A_S6B4C6 zKB@-YTin1u|M@DL6u6Pn19LhRr>gEGe^(xPjNUl0I;pmvhiKNK`}a960L>_J_$KlC>hO6uNuTk+$<^S!1e2z0H)q2W+G~U zqoh(Z1um?y<*d}qu^)nT4dVf6DsuXa6usy#{^lM`Gb5JIB)7MB@qQ4AzY;%cyYp&& z?8yweI3VLw7xEM4nm|{zV7$B8{-N)89()x z-^-vM5?JluJDFs21w6}X5S032BgEw;sJ$hFbe1ISlk8l}t|1O=NE((nv~3VejPC8v z>1MsEspk&ii&5P=&iUc^y|89aKBK`;Oh&0Dm>lCH$kS|=2GCXI8;n+)k4oXCDoS^F zZU$I-LNM;B^VOz?Y+#RG1TV#L$1PSitJhVq4Q_lAy%_sAT6|^YM{UgQQS}Ef6!H7L z*bXe?E+0ax&cJsp8Vgh-hs~79%NU%+nltKt$;(sbTpB(YfG_1%zxYri_jsZ0(>$Y+ z5(k#!1}g(0X@@OyI5k&a&tIuURRZ_QY^~cQRIXc{Sh)#^SKNAKV!>L?nqCVqZHY1P zZ^_=-o+8R6t8FtfjGIppx2WnMaxVzPqyVepTPeakmmki^QJ_G`xlV}dm9`T4UReO? z!n8e4OFq6Co}uMb&ah(1-mpl1>-5(G8rfZDKU`MF?o=^4KG|JVew9YKpQB?maJ=>b z_3u3ZdJT`keL(rG4lOZRpHLxO06fsP*O5-)J=wZS12~n`P(oEdiGBQj?R* z&&rfQQNbR`K0s*o&xNWs8_M4=cycQJ%*~z^gSJ+^dj0hK9kUYH`ki*i&z&=^8gy;y zFUU~^jeU6)tn-EIFi;K;3X;=rWaKvw*3H+FGxA_q#D>liA2+8Nt6N#p?(cJ_`V)l1 zSuR^NO|MVG7(Y~7tia2}-a#Ts+p#~6^_(o%sS?BKuIHz16WdWLg{x}{w1Q zj)TpC&bh;O4<4AER$srX9iSV^J5djVo>&29I-PL>{CahfpG4OD`4+c$p^6skTh*Ip z8XARshwIKx(Unv;OM;GWw3xMYl_XtyPpe7m{(f`f!uHvno29jUxKJfSvj>Hp;qjO5 zGdXDdYn;coMb}fIq;*e#gT5Bv+}wCZ$)D)Pr+B;+$jA5p6cjX6?FO;1KYIG^bGG}Og{ShtQX7pOwKoxINOtN;Pw&I%X%YyGtjrgDGUa<%wFPvlVNInVBjzSzjbVI z=5SA|zF0QZKrivuw+yp{0=MP*rjFs0ox4^pz>3Ku_4XjkOl>B>;K&|BcsthDsbrz* zWyA+T22;{@da%3wExuuBx!+91ZYY@Dl6ZC@vDT$A&GX)Kckn;YBNLgPX>k2D%jVXt zm?=$BiT=Q4HGx=T=`DpXs=RkOr~#r)N;L`m(|fNuooh|{Gqe#;3fA8Qq+Q7Na9r;e zKZ3X@)QpH9&FTs|BhxsYBOZof`%fA2QRrB& zFZ#N+3#poiGnE|;v(mcTh)BB%By#3u`sVRYSAO_bOcm)~7qa=Of1^3$yMl4RU#76- z&&M|is{DaVO*1k$FPk+|-FFeTD}N6|?*orasRz@%JA=APj=BMb8R^!Ux8^3#*pYRd zV%4_6Ec07XEgntXe=YaSr-Rd44ebD9UnRlSUIIW=fR07SdB)E*QCEydd2UYTTC3L+ za$jlIr@MQ)dvx9Le02T09m+JD7R5=ne78z@jaMFK$ zOpc5$AGC&9!333z=wliP*YuDZl>g5deX$CrAy|03ao8N!%jH@ym{IK07a6amuWE)hE5rG%R+p&o&H~ zL{wl663dDUGCtt8BIyG~vXF8tF*{^t=E0|@b;%dbF<-t&SAR#juHU1|61Lbgdvo&x zO6lmcYraL*s6K_V$8{4l^22U!553K^&kBC{UuOYsRqkgQTM37MAoiFkE!LpN+r2Zl zswo2T82TQ-SNkDjY#*}TkqDo@zcZ6VUFZF^62N3aK| zjC{tC?g2`34*?JLq~8vm_VBsJ%f@a5f#5D)a@6cy%+K2Dz1LoD=hmWBMBm+ae{5Vv znFl0^Y!MX5oPw!5f~Tao2JYM72TFBVYT@BF?LuWWHSS?8_Rk*uB`(g$uy^5{E&bIb zzT3MV)PFlfMzYX(4ov5=K(ckmHbg#D*dvZRTbo7nJsbN;3X!(^f)*1W_O4zslnIbC z^w04Dd!~wW!vUmp}v?RI^g6y`S*uc*(y)_rh4~nWFLD zyEw+QsuDs+R>@ z554rXU>o+e$Dq@2#>|pX>z4E9H^tuqp z5`1^P1g{uU&zml^^7qS!Jnfg?&vmG3IQMd}3R(1h`h}U##OGo0Nt9=8)1pW^_xX3G znw{b(NjDyQ+C+;V((Zo#(oHo+vNEzej0IO#4iq#7qfjjV=>-=omn)r2Vtkg@(tE_{ z81v$}xtXuA3)M(feH@E=O}mftDy;W=Q8?)Y7TaEnI2h}IFw^?C>@~@1`ET9modku)Q~*oa8;7)pB~fG&zyGw zeY}$L{oMhFf2BBB%v%;{dqnyRu-is#|D9c{kY%jy2rY5+7;Te55>P2?J0Q)Tn~AC;3%w_qyNB7^FZqyuQu|X9QjaT>lCIU&O7P$PCYaeZ ze%_+;wHohmX{pkVYOT`D^0M2^TAi0Y(N*TXSvk&c>a-0A?kn(%4xsrf zky%r_oaZ~%4P9?Eqn&K~GSd3n1;*3H%;cQLkHM6&iHg>Yjn8|Vj}qx+m@Y4yx8l!^ zoD4G)*fTi2zcQ%h4_;&)=#Wm2QiWXa4{5q%ZEDo6S=LfP&HWxUNEpMPVv%Xv@Q=0s zVwe!Gk4mXq2=9kdQxJk?V{Rd*eywYLbAr$kjw0*mO|ya5gaCSy+o>5UC9axa{-DtD zvQl0JcC$zhdFWij#YaPA2~ZmfSgM z`NR_!Fzeein>LwDFlm>_kVuAkY}>`QYd|aN`HdUj;GkIK$yO?A zr>eGpW0|0?UplRkk~BQ8h3k5O_evbcGK*xhS(caANu`oOU&ZlTBfR(LMy4_-tYP#l zmO3hao{#XWygS8KJ;EbA!npXmzR$%VoapuAB9|=@tN+&GB}|ryzn(e`8ex9KUqAm?lBfsm{g&_Z8$HAn z_Dz%35kdmG+X_VOy%b#2`1MVUcv-f!TI9h3QFG|3WpsqQhKQF5h$gp!4nJu=%ck<% z(ckX|hTNY8GuAzg<32cHe0KE_Ml;?+G$SMGl4W&_ z)ozbW_vdM1m@zjivUo2>e8RqmznhDe72Tgv?$eck0AJ}6nz$PyS@HE@?TRns?du*x zqqHv?J6+qu@l#1ABhow081HT`z}9Y_{8-lM3u#NOP;O6>XEGBr7X|a9CEbZw=ml`)yu$ z@q5VUmrr<$99tYcYiLMv8c7n*UQJWTW;LXg5qX3F6m^3{B29RAJg>!r`zfAz=6P0E zL|9#;+3aX*xE^|?`k`Sps8lM{>lWp5rL&ze61cn~AfAn!`&Y3-BI%-Cvb{|${r99ZlnI@NXP+ z_Yp)tFE$!nvaGSk(qb@H7GngfoJ>YWGQiK=y&J7xK82#(sE9lMy2Gk^jk#f?eYv5G zMW%Y)Z-2n2h$P0yZip}pa933){+;(4L(nE!cVi@Ll<^c2vG^E*c0ab~yG^Qw=c3JxOns3ZZUiyKlj1p6`BzhF&m(;I zV$W=c`30f9r^2&S4Dk~Kb7gl-+fdpqK%?cd&m-OG3$T)%cjT%)>p*4JzG=(F6TodTVsK9gC9JYsDx*-0l*Hy%l& zKf)u-2|v5M%tsT6$T%DcJxIiBz-z$+?n}PUFSS}{LaMrPH+VVyzZ)A-9>srhd>k1r zli)SrwIFnBUVT0LE7=2y`FrIPzTY=js~38C9`0xww*wE>*yJiUozGt6-T}6 zAM_aqW8~I!V^VdwVaJ?Ckk;LFnw@l-{}|gg&1;=ST-PkZED7_KW+ltr3*mUW+ttq{ z%i{I>_osfpqwkLy+BR*BLtf^MvlO04UH=Kd$FJ?;*LQL3a^xAB^c*2BU%tSdJNM9a z4N1yHB+Bzzq*6&duSJp;7cXwmY&LNm7uR)ZG{kd0%u7mSL^+I_L8L+_`E?veLF%ge zY}+o5Q^mGzY}<(nK6CUG`Sih zSGR+4@%ci5MC|hk^tn#)aRq?ybJ?=E(r84>NstTvL3TrwFw z5N!zB<0pVzhtPwn%GkCWc$ZpHUs9<|2kDBVu(;B5dk9I+VVZ5PplLSYz$YYA`+GO} z+~+>W3opEiB#DTmOh~E%@r#gLW%M9iFCtT&<9V?#xGe+QIq^3qz-h_UBRoPs{Ftuu z)4_|rkx20QGj30ac+Ip=9X`&Q6@6ET=JM;B7TxR9-oI-Bu?m;l!PxkdrKRYc<0A5U1rpt1yC!XCNMjaa z_n``qNN_)qz^79|@HDuQ&vPT6k6qs)S1gO~J8p10y}*@*#pf5V1bm1=m}&+lrP~8jYUUJU%Dg9W+w|@O>O0Ynm)u76(Jd!pvp6huq_- z2||nz;x`k8y*aDrL8q5&l&4%`{WTJ9Td2dY4Spvs#B+;I9IV~n#>NvEnKbFN9C?k( z<+gVu47+obw(voE?fdvv#{t7L-Js>xK+Qt2Q$o95w`eq1kmaLje@zpFrnpHBH z_BhJrjHp1YBY(T!+q79~*0N*1eWw4u0M7ETaitqRg7x8W*Byz5613rz09fkhg zxN)0%JAaO>bcPc~c|?kWEVoe7!TA*_TBcGqsgx4!D@hrQY+D4$wp|Vn?r`zqMRK`p zhtNY@fCA_fo_F#tD@rB`?+cfEvA=8jCd3;N3a}vHCdh z@jzVQRzRlSTv{5LSWOeNqCs1x#htUM1KEN2{loX!vMjDx7F&(RMECG|K&r0CyjRSSq^6B|dY|SoRT96K zi(?WPN6AE<7r9wOvg;%OdBhO7(<0tR_0_+^TG%eT#CJaULPscGcl*NcR+dGnhw_qG zZfy7zGmA))fvT<3*mwfpbE#ItYvXe9;zl5u?Se17L=D@n4V!x4esUa_hLxh#>UAGG73~1__inIQIO+U|@`)NYvYdis=VXkcNE`)qnN(Fl zRaHDsMp4}k!lkG(x~`L7%(Ji{vRjMAQK?|Nt^50<5g}R5b`hzQh`eMI)GB@G$kZdu z7a!}h-fvs4eL{Q@_^(ghqW%btud9~D_a7bcsiSRRn-W`uMn_Sz$5u7_81U=B|BmRx zFzi_+wl`~hTKkYK%ixV;QCPKGjj~(CvbS2L&NY7}R(ow{v5`o9#YL_6wpwHV{ zo?qL%$hkry5#7T-2{v5~L@Bz(DaOGy(~LY<0JnlM@@C+@nq=IC<1Abzprf6Ww6&K+ zdq}A6h7#>lnEmf=JBaHH4nKSEi)?Fowk(U!1yaY`+Q}Mw;n>Zv5BM0tgrpCw9Txek zhx=@68a3dZr6r1IFcATW+Ea5r*n~TwUspOn!Q+!=FuJUIuZS_3P9aogpkrFD) z#wDnl$Vv)DZr44^FXTFED7mhO6MjzU=g@G+o7Z+N4WSBo2yY?%uu2^Dlhjgg0t_ z2s~4#%$I^Wld1Qo;8B{Uvf3jI#;-0e^2Rt~2}AI4;3n{2F`iLB3ieEsHBAHGXIswi_U8wCrPi5dN9)j(~QW?{o`LWpym5RS7>tvn+v{;l*V}FFfl*tYZ?#yJWj>iqc3eM2QOK9eyu9BYOV=h; zkn6_$19Cbss@Eb;kfH4nI?esO9(?cmPJ7JX3cOdhG%X@q%%R$!v`MiSdGqpX}r zP)Z~yhaRkPYGC#GO@-|ae>0F_%M|18XYYNRIw~4U^c;iEFuQPT$B``y9ayofk;&As zaWNBObdEw{=Ye_mRF);Ont|)OCknMXszJiI2*yP+x)3eMWQ5_q{k8uO$1Wk+HRNW@ zdt}E*!+^X)Yx6QbA|%Ye`W&UQiC1kC-Fm%Ap^)!rQ?Ej@zzHvsSquA?!**Q2$97!m z2=d@K6{_Vu6s7&%RTUXI_?*dPF|t~R`dzs0G<6)wi#$XCtycSUuQ0WQJj?l%|d}&n#RxV?hfC14E!eWi^0EH;>|*VHwy*cI6C6h<755{>nrRb`qIzHsC6y7 zw{HQ@GCG-xqo~<@d9lVD$Lp+HCyS^KgYaCf0zV#Xc?)CABG#`n25B0C&~x(F17h`= zrKR46?U=^Twx;pvFci?Trb4X3_Wt#t&E-Ub+o`tj_d-|u7GmCpe2=!j=(_x5_r?(m zI*x)`*ZQadHY=8L=0YoRFHd;iw$o|0)9I;A&Rn&ujxo{gy_$qzg#_i(ae@_`XRFbe zcnmK1z8GI!9;{L_$yLjmsI9#aZ~rHEcUhq{P|$bQp>4|pgem4O>M#KTJ5RzBeMKS0 zz^Ls`By5(JI*rP7x?OK4zbMo-+cMvRAnoofVU}DR)xhfF54ta-8&{$DA-D4lN-K{6 z3Chclp=&Oi8`Y@BkdeKHBpFCv3oon*)O2YyN?iXw}5M(EY@S~X-@CZlIgc(ocqsGX(>&r#^rYBpU=t4XC? z#5T>oUYSH9dxA&}f+9)i+7h)|+mlqQne4?9hYk)678cs~Rw&~sh+0dXB&%LNVts9u z`xJYVsWH%tP%0^z=b!!FQyn+N;t&yw-ffufFL~|)6n_!W9qQK0cn$aYBC{tIr@^+S z@y|9lr)JQG_yxo@_(i7grRtt7eX~%Y5E*e|3{K{lpA2EK;1x^Z^P5Le_}^>C1(v%) z4&$MJ!;4fu!HZ*(sczJO@oC*j#EHr+O^ZV71|nwQ^qb+H6|q|IeO_>f2D+ab-J~dv zK?(WYC=U7i+~NCt>gXsUPSbogjEeX*(}MH=XyCp2x~B0b7f&A+dBS@YoaZ#&D-h!^ z<$3&e}5t-yDD;wWSJ$1=6dXq64X5;z^QH7=|#o%^>%uS{0E(bh%x&2BY{wIQvP6w0*DX;g-^XscsHzh6H_R21r1sl2RI}4G89ghZNQ*cbLA2t8 zs@ZJPY}zP_)N@?8b{X^wC$=9Pe}|@BWmpWsAZ0uNJs?z-!)?-r5!GrM6^?UKcDxrp z{?VVrajV?geXH+2qli!J@829srux^I>O|pTQS+My&Z=`~P2)}w z&N;1nd)2b|`M^kwh}9HkR%b=5hT$~V_q!Dd-{;N6MOa+y>4};q9?-tVqh8;YN!l@j z_oAQo3IxGgv8;(mb66O5#j+^QL409eA_!-7?qQg@5?~xt3QLp=FMzU0B6|t_dKiAD zP`dji_V>PpY}ZhNkUF{1&QDqF;~_j=ejHV4a9CI&tqCLYP}j*_eTLfMy^gRoGngo| zZG~Ju^lWtYNtUG(-XaKV4CxUGYYR9CSoH_g>lXF8MX?A=E0=)Kt0VfUYcegbh6pPZ zlXRi#UE8CTCy@|gY|WTQt6r~T2i~lHXjrwV)>8yuwA}|9b`{$(+kXh)Iu2&N5wxfM z-7r^3mK_|o1azw1rE@7!U#e+G-nJ0odN!Lwk_8zH31ZWBG3)&ZA^1Z^wJZ-WxnP=2 zl$6Y+wIAT?H-B%)@w(gM35A1dEjR+CEUeuvj1RJ{v5S}Dmu56GA136GC$duW=2mR=U$F$#DsAv0g8 znAGcmP=aYwt#&<8{Px0-`%jFHijWZ5wu|Yt*uUEzH{E1PRaG)ZTMj=?#P;KQ-!ZsO z8R&J5hvDQsFdGNd%HN#eSfis--422a`#*E~dlChdl*&*4`2U2eCD}jzV9XXwuf`NS zq@JnY2-aT*t_|9GirBL(m_@AzL_dCkhr>JbWxW!8S=@6tTHL#x%oJ-&;5C=5@x)G54kX4e5IEkDlc`VWZ@ zA)%_?f!ZBx`v|3Zx)B*(JG89F$|q%GmRUB*EzA!I$0@DDZJX_ zr~$Rjn$ueaS^vvEL+V*&xhzKZ5j&zvn4+q0%pV+!U(rb5pHz!v-L zozgQE53Bna)6co-hglH`5Z4IK7opm-EY^Au$XN+f!0d9VikdcA=+A_Rhxzpr1UPG< z0O%hMHEVCx)i|*V#?v$~4`KzvaGy1?Dry@4TGRN~gnjuoS9dLLX?eEOd2VTW4m2gS zCC)8QL$3M>!i*hS`DkJ#!Z5GP!3<^5M+yCtQYj2WL({Gc#k9@qc%G1={?q^PFIY+o z!zfd^4}jX7Zm7^GqePY^*49>7UJ*|O{mM(hUtYqokNC=aeVq+w*h z7oG|qFtV3v+A`o{nl|-%6VtTOwH0dh@ja8aH8`)RI~*_cYB_fAkWF_2LfRjbJ}1Mv zTf=jvGU;WmEd3-mcE3QqanN_aVL4N-Q>D>380z8bb)1}X*<|T@pZ|_w%xq9{Jh-ZRl>R%-9(kwA?&e#+&NX>81d2Pp~TD6enWHck6$*8z) z3nN>=Ns0-+(n{MvQkqpV#^cD%Dv7EvoC%ksVvO2S_O&Ota^(W~#kSEKhTg^N4moPJ zV+ru5PyRI#9@z89pslA##KaXsVv&+Z?g?^GKS`F;q|@OS`}^@EDbsU1v=#0S4{bC( zi|dK0jJC3!6nM0RH!Gey)MzwuU5`v=30amqyjh_T!trqh+b%`NN=e;?RRbe^0FjcF z{?9z8%X<`_S1bkDJ=}jApF|`h9!ZM(L{j6K(A%Y}=(@t~+lRP8yZg3lMDNXcgX>g> zQmSDgc!N-H98fENYoce4DycNx5uYssaDV^pXk8C-1`TZmLt7!Eb%y-at>F?-a}V<~ z*r!j(XYluS>}Yo#p78GmpA0_j@G<%;flM2rAj6weuD=w=Z!*8o#c5zxc~mf=m#WVU zbTOTtnrG^{KHK9pDin3R!oZUvZq&y=&yrNZjJ#Er#Tx|Ps>Q{rkt*mUgmAjLN8OkO zu^Kd1;|g=#{dwbfn{7*Yt>Php#q{{as(*fGhoV;HvoX2xS%g##t}-$^oMnjESUBfi zwNbC*I6bg!2iq34g+eHbgs!Dg6a&X~JHpy*HH)6T0!syCB}LXyNv4w6hd0>$*6)ds zJKsk+r5DS(at*U?QM+&j%Sv?^{$c1{rCebtRu8GWt#L?F0!T#OB1LYJ%NZxc6#%li zD;>fLOa_+8#s1}a_K&f(^j<$bAJFrDj6!;C6g%@a-xq(R;krQ z|GkzP5oqC!nclb`MSP(ag!Dy{;=X9X%GwILE}pfSj2ey0aXc4ssHw^91IH<$_fYZ! zpR|;r?(~0e^bR@n>3@yKc{=v@O7#k{bDb&;>i};End(JGv+5$t88M+!R}9EM(cw|@ z8IAnJ?_J$GIH8yuCsevNJfG?e$kZpf9u!VBdXS~j(3X##R?VDoFiY(7 zWx@9cA(>jCz&DpVC6gcF;X>aqyO>eaRI}Ksc531=5@Ny=!?3&iA*oan!_Yeu_R}hk zd&jBq{`()C5LW;&3^fu~@VpkD=i_-TQi;t- zCcIJ>Pnz(v6nSiVU*(*-NTu0+Z|f8Ox3Q9dVL?u>QuE0bAJFF_Xt_> zV?qi^)L`9pDx_6K6zr7^a=FeIr)|49PL-x*M7c$Sl=&vXpnRk7FY(a9_V*^k{?2<0 z3`^^iYt>H{J)2TEoC@z8{UD=|Q?XwUGG$jLzIONbghwe8^B`rlRn`m7p4hh*kg8Tf zrs>5BKwHDhG{p(ox%5oI#gXgU)P~gDsiTuYcnT&dqB<@@&lE6I?^Ha;Y+7XTb%v`3 zYMRESOP9EN_b%3$#^o&Ys6cq^h7rQ}{pmGZ+CA>NxlXr-XAu5-+a9Y(Yf%%Pf#t3+ zzi|7c*aOZ`KWr-ADjWJvtAxbrR_u58uoA1V`0hNz0Ret=_bb3xI`WLVqXQm=*PRc{ zLl%7)YPn4g^Q%AtrCCL3R-(M5c@Qi`kyu)48(^W)t))#Y%fT=fJCxzVe4PB^2B}mM zNlN0iS~Qw1_V(@}OXB+>A+UpxJ6p|?+4wO&02DetJ+NwZACkuS|ERZuy-FdAXAHrdD3aIPh7z+k&wvc+GCf~6Zr1}kC37$yc`f3MNwF+ z)p+^vu;V*tQA`wIt1Q=e`&8%dwcWn{9QlQ|2u7HT zrKpV^Qc`P*W%juS=^Exm-FBPVlj9k3=$o zqg1dC`>WLT!nDKg;t2bngc^{q9#p&)vbIi2z6I`d-dAtG^EqC5^^K9wKQGwL;Wl?C z+4@Gu-$HUF*4t!jweUOujw2==9M__5?Nhh*saf|%YTI1M)V~Loqfa+6Vqtoj9ywBR zvNS9d@WI5Pf~UtUy;HNoAVPI`c!;KH=(-*es&V7>XI0=gj$aEsLUGT~7+#`DFsN{A zFJ-au=QYkATe0xEtyuE-SrO81gx6NHEPga-(>sfcyi+)R;o~@ZdD~#R#422d9;=6e z@U%9r;aqu))cu>JDu=_raK>oJGHGYPv~e$-aek6{{UuU6!q+@)mAZcDt}UhMX_l7q zIF9gcgyCC;p`+_EndRqz=SZs(k3F``Z~xjq6zy$vda-)pVD%XQifhk60(7kj{xQ~- z>(osX^U8G=^3P%3Z@*W=T0;j1+f+)oSXfx@dF*s%_`U83y@RNbY$hWk%SkdBIgr*C zp>$12#W93RU8_r>ZkBMmLhWQ_kyo|S_1x3mM=ZH+jah(DN(=&e7c^&2?Lb2DSPBc!g&>&+RP8#Gn zO`Dk!JBU#2?Ch|#w8YZV5|=Jrf-z>~;+Ul{#c41uDVi205dfGsLQlr5Jd54v^=cgV z9%!Q~hE0fZh}9@#>tPS4^NmH^$6q1!rVPb50n2qGLUmP^SsX#4`7B|pSHY%viPD7=q@WT|y;6l!Tl_en2k(-n+JdPyigr~@dg@rVShr8HnE@*oW zxDd7b?WK=mWS`_@5dbxU+cSD<)^+nWEcq+WNZ;{bk4r7lGx(Lm`^scSSu zcv>QnjzSQA1Ne9uG^wszl&Kr>E&^~25P$d;DhgTt!5%AHjdkQ zJwsxp$~6+L4~4+Ok}%NI$|CFQE5J(ZIO6HGw^tmfnm1i^_RSvR?Nj7lo-e<{W7bPn z9>5(E1~_S-`ziTlv{l?@k=h7(JKZ>`peL?Vx$+^C2u7$nWZ!L&PU~sRg#{Q_TV=`(nGJhb_*9OuavRmlGj=-Jb`A1U?7+t4SL-26~0P z)o9DTPtzFx^yVi2;KuB-VM0RnH;r}(MCd{XkR%DiFsK|IF{uFNEQKlJ=r+n{Ns30r ziZxo8-+3chvlN!-g<0niO%riq70P)(EN!1AlF8)ooz4M{gXd02x8Do$Nb;!^`GqB} zDhi3}`I_ueRTad-Fyi%k0t_}Hw0WS8p1CN~Tcr;pN>W5;C#Oo~qwh==gpDEn`Fy;f9fkYA*p) zCV1d=hWMRdRmw>)NpD=cGL%U53P~IkK04`Dt(!#-i|>-rS247;X#3^O7f$S3v)b_! zRcnt_kf+s%g%&20os^=VS1PY9bJQ3d-Ox|CViph{sFSeAAa8T`Ozlx)kE4Otr!+`4KvjL$n#Vif;kPgl|2W1@e6R=om zD139SEtw3nltB+M?^;dM#90NLoF!ygM%OhYDMfcBn1=}^&m)&z#JwU4rzWb$xJQR1 ztH&q2Kl8@ILY4(1i|cyA5O-WiBuFIUA$GvEXVEhX+h6%DO2_xfm4)Ksp!V;t3AL-@ zT3hu>*Y4A}{yZ&zk;e6Bahw`^d*6n8_kl!*irvEE0pI7kJxY}aIIT|U@Uw*Lws2Y=W)S9RS`A#s<>255$92gVNfryj zpv)R7Ak^wyr%W=HMAJ1iy`9(DY}&~30?D9wt{q65pBL0?W{|gJ30d#}A+Z9`GQA78 z2oKPNxkm|xvVmbaS*O9E`c)+Q=PwQOX!S$gnk=Y0tF5uREJ#jBV4AK))3q?PH7eGA z-%Hu)C2Q|dDu00MRH#{d?9nN-Vrc6CF`^~zuc5C+e~Y8xS`^eQRckLIUQM?hCTd7x zvffY^SYLjYcXvO}@yOEhvnJuakFU$wk-bxMAye^`#u_^?<)V`P>i#^;Sh1|Rcy?a4 zZT`>0!~TS7aS;rI#oFYi<2>@HLc{z4XOn+Y)HG7*bjQ7V<7k^V3tKZj^Xl+jhG2P! z0L47KRkN&*^$62JI<1n==V&%OgmU}V|`Y~8fjy>GEp(2=Ah>2wlRl}M!$e9^3fnxnYd_5i7k3ctJj8b}A)iZ=%wA$;>7odg z^L%WtiEX>o%sMqo`~x^B-eP6Bw~#@<5H80#`RQ`hcN3U9Q(MV&XQPHkY$BjOk5*Ww`CFByl`(EZ?n}XMRk52cAS<}%_660T7{U;3yv-bqxfj^88~|Hj9b%*YPH7h zu6V)9w#~y4>^B;OcSVHOHPbno=^QLv#K=BP7KKFR28r@b5|!B&B1i=Phw5>fmd0zf z$mdfiN(#?wQLQ?ZN=;NX#mY(n$8oW&Mid&@Oy}59mnkd{CQ_1=j0~aBDASTM<Pi3Nxfy=f-j@Ob;{h>=~P<`f4_pni1nN!G zrOBpLHWsdQ94p+nWEDHs$Auyd4lf?%!8dg>b@AdwbX`ADtLW>59;Dfjp3~HtiWgt) zRfsh%!M#?mvuIh7+S#W!Hz{h`)Q!5hw_w)!Q_!DKrBW#>l?rzj7fCHFkSm`~_H`cK zwzKq7og^G3u3BH=mbT3b3fC?8JndQJ1I>n0T#9bF?QyZUvckQECGbaAsOs*c);0Fe zL(ba6hWDTU81Fy-8jFRGAj>(D$prk{^F^lO@HVa^%ZGSJqvh<(1IIu7*8r$oewNbl zU7jqyk7=452IUe=v%fN#BuV6Q;@R&wZj=LL7&@Bq7_2`*lwBc72#j8!rN|xp^7SZIRYwlBp~?<9Qm1 z`_U1?Fi0Vc0JU24#PJ>16+{X~3MU%}azs={J7}|MBgqDbN9}PK*0;(UYQL}bz_dD% zzz7WPaJhT<)xKnEQVb$tvkBiD@A~9ZO^P!RXzV9IF-^otO&oFW(E8Gg?d`hQ9!{QM zT!OXlp=Le^j`JY4KG`^^S$imMgS31yztM5Z{CP}%ezkBaH(SD)vw{`KO0MvpU48zc)&^g1nf+Xe0=GLg2HsD7B7o!DJoSi5_ zJ4{gYt8~0iGEp13Anj@J;m#kDmJ|$aJ-W`-2@L3I5u!mMeuF~eMj>m1>NX9VKA4_@ zq3D}ymj^1@^$!mm{n=Wx9z^RO?mX!DTX?VI_gxA4m`$i;b>aQ79gCXzfWyi-MXLqW zK7tP2CPpE@uHBT{fUAn5<2-f@0+OD1k)0 zL^qKd?fEb_GBr!jl));0pAUe^2+?Wq#_{o7JRtK@#(TlC_$PaN{G;c@-HLk`_xJZH zt17QN{vwUj|LiTzPgjd0#QmG@$a-=BsFd$XfLVNA! zC~m(+0+YiIt*AcwUQg$wEnaTwL1ZdtUi($>vm=u6SABy7MI}wLNz4AF{eC zW=>P76v?DaGASmG;tF2I(H5Sd|xav=!sGao=8tmaMRnHH}owc4}MbvcUkNXukpJQW6gsPip%nG5GYL>y0D+7cax>XIse5$XfpTIyEAepX- z27~=*ti{)%J?84bD0iV!8En|fvVkw^|%Gn=B@i7g zDUMmYX;Z7#Nv1sfz}Kxj_5;`-er9UtI4xH9cj4Lof0tzmGc9H$i)pcA)>p`tcV?bz z1hBR$&YemnkW)fQE}2Twuv}890c1X=Upk^LYH4kVuZIVf!N~KD@ zZbb&IZahxL=zI=3u8SvT4ERxKpy#!aU7u7!!L?L$Z3WkH$Y^VnYWq~XW{E?B)Mz+(t#&+=>$a%Zb}1eA zHt3U*O-kye&Nm%%G9*Q{+HGW|z;S60U029tq^Oos%dKD-dRvjsZ-4jxRA$$vYZ^Ot zZvl!~oX;J4u1-wqq3K5iPhuK<>pVtQ-ykh@DiAgT?`Fd_(fkrkZJnys>AeXt$fKo6 z%d`vQ4Et`(D>Vcs&v|zAtLaWgruqpwWaKpC9#QXctg|Mhy)f{p-Yb{rBVD0#FWYv< zey<)L!r>vF=k@gx-C&>E6jaDUjB~7Ui|3vEBhhFGrf}sG1@luWH?=8hCFSZofQBpQ8zi?k?(UB<+KVzB>vLn*_=n z$y>`AKMF}3Dh|;P(<=n=q3ln50G}hawfS+sZTkm^Fo-2yi6+fI`6#w z@2K1tq64||T|nHI{f^wClQ~D>-~HARi}@o+glbj63vZ`A8RObD?v&mFAQPxutt~wt ztphY09{cx&v9x=*OQmt2l@;-<({&A9*GQ%o&~zQs5~QJ8wND+1@cl3Sr=aZ0h=NZ}5>XuGndlbI2A6V=Q#Bt@4{9yd zozdPqY9EiDIbzMG;W*uIWmvw|Bp67hW)(V@nX+dJVDAhI_l}BqbY^$C2BXLNi!)a6 z8wDS;ZGNs=<)3BSvjdfKk%fg7Y{#RL(9vK16!v?cJ@vWfl}82Q8eeC+;uOQ`H9f*? zuvRQ`cR@V7Ly>_-qrv88+n=Ys^wX%#R^Z{ec_!^VEmDPRG%F7{UV9d`Azn)J%uUjD zjjLBLM(g|g8rdM+O;u&8l`OvBex;;RDI_`9;jJ+(hjdz|6blFZ>d<)kqNqYU_1T{5F#jzvD}nSI{&$QeD$EiHugD zbS$i`a=ALTSU{z5fbAcS^&OxmA*xiW;3rOah+2LFKQUPL8^CeI--<=?yD3%TN~Cs<}(e+@)&Wr)ExnaxDxG zT!;|C(X|qH^gsrj9Y6;?3)cpP3ScWA&AUnyru{}tqvsHqZ!8` zVHeAHqwQ^ywb`-W>B(wi+uS9uuhB$7ro}54e;6~^HV8XI+Se&SA^Mb)(j(;YWxRP?HeXmM&;6{{zncz`@fyKU9&3i+poVF{^E?uVQA%+fDec6 zcivz%8XWgfD(i;u@La{LZ_|A69;pX6&W*QfwJ3~6$0-e*X%(S#29_-0I4;HXvt)e* zBlir+yMHv(+@w~^N0uctEsgK{D5`{M3NMvydsHfQ5(x{>6ZgV2Eo8-J|DgLCODrvQ zh64^lcOH^spRU4H-P+^k&L4M>DX&X(A(SP*bN7hD<0CXpyegNLG_qNZ1PgechpuZ; zNT6j64+#XJT(xh08{}kLupyN1wiEa*B4xht@3JC6;-o3!I4<-kwAx00qr!G9_6|C8 zKC1N+&8AJU*d}|bs*=kY*tUnFqyl2;vsBR0)FqCO#lDrw_$qXX9;#-!%ms3pPI3is z@8HXn>iu(6#y6k_?>`BdvQHsCF+5*tJPfKaH5~+ECPTvT`Uzn;_Ay`n)-OjyYIEfk zpiQg*G+7XsXDm|NHS&Q>`C>q_o(QDM56uUWSL<*HLCU2xp1JgwJGLE};ddWI>(eG$ zafH_-6Z5)Vl{=M*>w#cJsEj9f{l*R^5VX)v1&4vHr|KZl}{v#@Pzj2B)2@ERm``tj? zi|O=C!~8yo5#L@UI28yqNRreRpGbZ;m0=x>R<=MZTOe6IB7J!H(0Hq4Spr|==s1o` ztJNZteTL=Lr{UW3Bq}#JKHjDA?ca|)R-@Fx>khFiYzSqz##1k2H5?jGy@Yjihr^&I zP`z%kyz(;nyn&*maNQQZF9?+DdQr_I+p);^DWMbQ8i61M>9mBN5o5Jmz#)!QQo@OB z_S3ys!&)kypJb4VofU-n?d(*k*Y1(<+aZCuoPq0lEG%eb7j!(&r)hgst2VCNiU?;U z2VSbtsF$$hljrm81WZ3w=Ts2n>WHKyJUW6Qd$muM^Q19Dpf3y!IQ?ui^vmrfUcwC0Fk-_f9yu>0xgtdldfu-eTu3ZzV zIEJCKy1ImsYocpe8kr>wy$K1GYd`V#`S!p0hgc83b?S4zStw8go;f}~*Tm?&;ijhX zOKV~lEF?{-REqdxDXbqR%Q6YyA)CqIT2&@VC!a;p>S@EZOOQGF02TF;egI8bEdF+FaqRiW;drTt}>c|VcQ!h zeF|^SpsO*8?07HC-di*p)-;We>lgUyk+?p4BRs#SS8r)J!ZA@bcj4em(e^ML^yJt8 zEeONYvOzdrc8h9Okc?`5mzsI6$X7Dt1=fOnM0lR?3e}tn^+2UBPO@ZL9rcyuk$W{7e88Go&eX6o z&Hor@RJ3N6keS{t4 zR!^(YtT1*H5xD|kM4vN<-4rqSKJ_DR!$Jysf3JP6bxmjeLVIH7D@RAXdo(Kf9Bph( zn}Ai#d-NI`V!0WKNQB<3w7i6En=I(pSd&aP$7Zn`*mGi{rqXbR4Vw^wg4g zZcjsM0U#5QEU~=Qwa?&b)am$f&W7Zi{Pt{ouyZ3*-6?sq_8gsuT$mNhn!9&uJy=fr z`rf^Jm-kEJMVnh#L6X*~Tenb@B}zv}e2*Zx6HlTbYj_&`x8O=uiW8q@@h&!EAI$rb^9&Xgxf3n12-FM4l2N%+K>rPA0f+3uVlbWuf=!_HFUo z(*r`KXT&mNWXNWoVZjwvQ>|{Yw-+SX+vn56tDL&P?dc4ds(Fu=5A?{fnhoY3^p?{v z@YLGhqGI0b_%{nIry3{xeCrcF(0!?owhxOX^w@86zkav%(=%Vks4 zu8MKhY?QgFt2CQ7`|p2%ma)+@{XB#|b#pvpJD!)6&!TgP&y;LS{cmAn#tbjWkF zaTAygd7C%Vebf)9kCW#}p`s$Qu4N}CQ+nVQ@+CQ*~}+sz#^$_pGtb+2>7#QrV{|N;`_!!dZIYZy(6BDXg zl8z8Z7hd4YKlQ%?Cp8#S$waU{!RJfwMB8OqW`WM|InB1A*_>%xXN^{F3GIje3f_b7 zV1M-w&XiQ`{5QYcbNh=|KFZSluVKCS8|*yz+R)>us*0>AWDP-tT~~*efg0rYEaaEa zv>a(=BPPs|LXvW17s8Oc=Q#E(c9g{^3=l@IjTEBg-C%8Xk+thbW3nHk(0F6kOLu*A-eVas68@AFJV>5N`m`Bn2zcP4p7M z|6-}jS)SVZX#lE$-1)uYTU5>7!o<_?DfgBFKZ%Cvv2&-)ja@;QjEv6OnvjGylo9pb zhqYxyiTUv{iBWV`(gvqPPQOTTiVAzZ^SavCCP9K~eYb;10W1X4)Vka@EG?%FT;)bX zq)b%$jO%{qvJzWZiSAlJgL2a1o|w_^=b+rONoCzni>5RDykqE;oPaXZV6t&Aov^y$ zM>(JBdcD5AvwveCVLA=YC}(OmwWeyUV#M;X4CY5PYFm$S{KJ0(0Qc$d2cY!D--0jx zR;+#WT!-^Pj0WL}9yP~CnT4AMp_(<4$ppG4NRia_i%6x~>}qADZJ@++jzXM$>FVhr zwf;ET`Y&LA^$)PW`UeB|ot-ja|CjsKD=%^N%1g*{k}rSlx7c~`H69c{K-WbMfUfIY zxpE03yGSaPLYA{Etp;B>>Wr-#^ zis@(h#kFTKvQJ~=p6SuT{xB_@ot;w9!y_`fA1K0U{QvB|S&$?7ec$=PkqID?I0{Fd zU0q#0-P1jsgL8@G48@~KOB#`qElM(FIqc9%tKA2$AG~3EBmA&_@J4KekA3lOZ0v(A z`B<;5!#b>s5+$|BA!j%w=RSI-&#JDYs|tmx0+5*iGI8ufW&(-(M%B#V)6tQIL?V$$ zBr^Zs{QZ94pW8i$AVw(aC33kUy5752&}rfKN(f%5_IJBvAHyhFrVVwulGh4ont7&s z@W9;PKA*_>3s(_E2~qUmcDrizM!H@mr|3C89tGChLjaexnVU@K5P?RiCFG5?Nj{vIUY~%d={6M_{`G( zKw5i1(a_k<+$Cq#3MzdzAT|vsCi@w>uYDXzZ_|oOndD1Z(#HqH=LR455AtcZ5ALw# z5J=mpJ7W_}-8eNdH4Ua&HzjY}=DVI4s;UvZWuCXR5V-tG{;I_;eJZ=|X`uyJpVE^zY@8A6kQt7*9} zjcT<*E@ws!4MRoA=FqH^xe8UJpqG>5dkupFL3E*)>c3&H;BBcza?-mF$xYPvY3BSg zg_6c`;gF+Dp2LHBqu6t+bG0V1Qb0t@>z1e*&IQhi(UgiiD6zz&tolDeD6mYqWL~&R z<@(6Kly{L;%`>iDmn8ZG1YhHAhH@p0VKk-_6^lCQw1Vgh)EeZ5WU}EhZ@OMa z%V{XuE{bB+0~$vhocw@LaFvzhXIciH&4hxhHDQ%ix7bO&%#qrU!`HY5A1=Vfqsu?f z*75hqIvQUpiZ)ei^Z;e-&m~adkSeX!mDQ%{*<&&;dFDAL5?mz`TxHd|mzGht5yd%< z3g(RfGfFfJjr-Gi4b7?BW<=GIQEv*YRdd?r7>`)Tv^{lho4Kvk`eSXEihj(|{XeWN z8?zeKK4kS%`uzEyu05C1b`iz7nuw~;_b@<0Y6A&rnUXiqR;-Omx0i;H}d$+;OFFtQxG7ViNdr}{`1cu10vTKnfStcm= zUyJ0zg$p+UuS`Ie3AsgfDh9dyE~=X6{?;M!{2cLk5Gf$mL{+(5rl^-F8D1vo|7J&! zO2rZ`Ph*N*vE)K7uc8?3o6;Iaxn~33ey}4@>7#sGVdalW_{$oCPcmHsRr4~IQ@43v z{cXU!lfROD&iZ?`&AXUHf@?&AYrM7bTV#~!)QjA>_uEKgZvDA5;vJj>sguC{@nFxL6aOKpUw#QC1cphj?yVFVvmBJpw zN~_-6vrSIjsR=AwP<1=b?QLw_Rt3c)kUcj0A1Ru7K0mTV&C#Y)V42qAmy*v?(KEQb zjVH@_=^ROGnN%Cg@lHLA0InvVC2B>s53Sd}pZa0L<4MrmNVXZff79hiecMezRV8O+ zFmNGIMX>IA*ooQZddHLEXV*-P!dWq%Xh-@uRh!;)1dW%|kaAhqYeoOrOo~XvJn#iEZrL5XaJ!rA zoE?wdZWqFpuj2K(Nxk~J)7$XA82!|9Zt)2W;I)HySQiB5JOkQ*jS=T}H4bd?pm{IB z6TgK2;B^Wg{L#qkwQGumr+$Qd-W7j_3BC8l?bJ2ZmXDZ^Vv-w1Q-`v=!A3q`K9O2+<$Kxj;$OHtLcwmt$ z%Rf#=H3iC6>SX{j+W2okWv%1HKF;lJh3*0?AcXKpYeb|qin_);aGZIALKdPx)tUmdyP-6`-__*P^UkL6L?@B;Vc=B7lyllFjO_VJ z)89^->x@p@Z~_`b^`6+}hUX?%TQVJdqFAd*2?PQJ0)7I1FU5e5Y9)gd5b%0id*j&H z=}zINeg>Dz#la7MXL=jogV@en4bFY-cUzpYk!N4KltC_=(`Q;I;fd1v>Gxv zpSSUPL2il;iGmxUa6(WHQYaKr)C02FKSdOhEXJPcPgDh02wVL|vf2(x*&yGhA5lp^ zhTwY7^^FCW3s;BQ4|{v_mSb0;q>|5T4Zpy`^jNNgsnZsEd*u1|g+wr9Fgsb*v$3{(k5 z%(Na`P?_Q?H}n<^Yb0Rk^>pW~x@D$=9+<+&c?{W)n~BQj8>Rr7p;{3vmAo;TTfMbo zUEQjE@NjUI`MFO6_1|hUy54_0%@$a%IC?$Bq-A2#3UTXv>l9esC|c)?f_2;%tcJA3 z+PS1kOjsZmF`Mh;Eb|D(IH`%Nn#i)g?8b(=?U+ujlV&x^oj%htp6HLQgF9>=+&NnEPb8uav*Dk{%gPUgh%YXL>I8+|$3rxuqw0`St(6Yx{4XT9nEu zeA~^J9Q9pGi)OyE*W+O>T3_DdcHuK*N*<>dr;93PA0+kSm$;ED^35mBi`_7W4DHa3 zGD;TQu46)>AdzU8LcU13bi&5|H}DJdmZ<745*(IR2?P0(%5i@DO~d=g1$1wYEFvQc zGC_BRf?Fk>-zS&PpeRLd9p8rPK8uS zt(>CPA^zz{H*~#>p4KQDIWqZlS3w1U%N%4@fp_oz24D^}R~J4z`HdYrliB~d0i!~? zp6o<(rTBGzcDm=g&S`$9*=?!wyKEiY zZmMBweEe}>YGP`i505CC(VnHFBVMyy{d65X-Z}Ec`{?>HnsS0$=-e2#8Qm!J{)vVW z2(W$zgi?RF=r_%2b?He~m!9P1*Zw`*hd;pYauGP(Xjo>FBJsIUh}SM&1Ze1m1$8@j zp-t2*lgq);Azq(3h|!7$`NIsUd-e1-Nrs^Ou=U78{-G-bv_gv^LpK`D8Y(na-Pcdj zx~`K>>xiOAC=|r+4)@%Y=<$zK(QUA zQic0_-{XGan3Z`m<;*s6D3{w`{@h3;#dVS*Xhx2rvQO&h9=C5>w%W49`o$yx3Df;< zErPLJ<-yJxdwVI<&|nC3UFE}M(q1g;R?6dG5vHuV#k+U^3niD!YzW)-YdC%3*wc2s z?+{FVAxV&+s5HK?!+P4=dQYB+VBQos9}u~j8B9ox`}f-kI~w%v-TKYpaPU0g;3^UM zd{=ShoUVp1kyY<=czlD5dY_^(o$a3#roh@|KCsSw@M70xhyR6{uM7!MWFg98?fzi< z>&dv^y?GtZHEE?urX`NTaAxPGV5*w{d>jQ+H$GAZ6sJu)M>|jKGUX$4AUchnNt2|I z&r`4l=Ay54;5?{XSR#3a@80-*h{sP|P|fYU-80Qk(RL6kYTpBQfajn7CEmT0Bx`I? zoLhkVH}DlFR})aU`ZNH!d6{a}JSC>njThxi#!NrRW*yt%uixi0d|3pkF0`7BRoWp* zegwfwxqO1(Uq7ulo$Z=k?&?&a+P(J27FARsCncvtxdK+>`X@93}Z1qUh+o&cHW~z zg#d)$8dCHT%9ewrp>B+{J}3P)#El3gB3{Gs*vwvt3jO~cPfO@^BdgvgtKMh(z)XQQ znR2wN001BWNklOX8DVRgr6jBS-+>% z9gehZ(p_#gO`hr22A!3t)M$*Bo@WZCTeWp;r|vWes2QD^iKziOSgnW4e{j6f@LVvt zQaiERMhrsY;hLEGD5C4>kqYyF@{9k7khH>5NaUSA_{VLRIV-hhIcX|)bs(ZwSVKgW%&L;Uk`yjPyZy|Y95_229JecRO_LA?$s^wrOy70m%j-m0DX z{r~4z`d)u8L^+Oj(Ej^%e)odlMG8s{Vrmj7ii)DBwP=;k@1s~aCY?KA@90$)W6$>d zer)XYzXf2stg&_YeV}gb?W{%8i#pkCqxH8ODo}L|*I_KH-sj!B|JmG*;CWUTK1Za( zUFT1cYtVRMoUu>0(c2f)K&aMSe7Cb3=Or9Cb!)Pwr$)yUZFH$AU>XA-dokq{M+fH|1@q4NtF8IQ+wXmY?Hfw1@h8BeQi(;sh}WIP zAF$FgL{F`6$A`%dyB(JujeE11*1O&Mz>%q|9Pj-htC1)1O6Pd&r~f6}ul>KA?7TUa z?HxCgoTOrGqUi7O;3Uh%m7lDMCTQOW$?XPj(a(I*+%Fyv%sv0}#MU39@Qr`N_i|?H zis(xs`VuvP)NInw4L#E;G!x5LINa^g6YI?2j9B%5!EmMkE-zdGR|UZpr(CYGfAT{l zafy)B*pH>&AF0|h@8nn`oo-CckU~x|Rcfh4()B|ILGaC!&%M_&-xOB=$swxN23oHZ zO*7crsiy(j^;ME25Q#)kEzvdObch7kNb9ZVPoEG6rkOU=K5aP?yuc^dFVr4q)J+bx zeMCX7)%lE0wN^d2k@pjqR?uChXtJlHuD=_n8O`$dxJFiihoYC+LL)VCXGfEQu2Iod zn)lnNLaHCye^X~qwZ}8TG;ux_V(O1%nVY_P0pu-F zWMSnpSHJK-553OhxkphRyn^VkpN`E%i9~`3Vt{JZ93WOIAEuMS>G1yj-{I=o&k+he z#v?EM3vPVlf0??V@`)ZoviV(#K2O7Zn`)&($Ul$Q>jCoYW`2i@D_=(Pn-=kIM<1kO z!A$SUYh_%_k+JgqH7!qGSIFlVx&6%7+1q`+Hb7ro`MH6%Ihl}9M~*U>WR6ortR4?~ zK8+v^-`sVa*kY(AimGPw%I4?Gr7B**h0kYN^xHK^l}Z&QTf#8<_#Jj|*_$S|r~-4) zy}Ix@B63|=MddX(_)g#JQxvUMUv+M6nU$3wnmV3i=Clx~62<%sB2^G%qS2>#pxy&q z6Shzuoz;cUbLHF%WYkTz_P?Xdgb07!XLJc(kQa9GAL zO11Q{W*Ute>hl5tFDuI-biGVY)X8bXrJ9`;Vu5)AUKywo5KQO5a`owu%^ivjc8zq$Xdfwz0nqwzqi*JGyDZC+|?v-1nRqi~|5;p~Tlvy$T1ejWFl(&E#{2af5xwfUy7N;<^W zJOOV#&Ce7}&Ir>?Fr8IC7Gf$MH&6Ht?q%fyx|%`?FVto=7s%Vfsa@f`y#5%k{%LCn zyjM~1S+V)U)5TmE?1r`{F-F`ICP{>1b~%#vR+SN66(& zL89gBckc6fJFUw+X&&#j-W5$%ze_5;$x8A$LPInK6$CHwcof-+jCD3@sACuvaygxN zJb=gNLKHpZRK3NTejEtF#_!l^w9@?j_v4_|&YVG(<|*suK34Mmeunnq$1uP+DI*9K z0znT|E78_1YIs~#RMntZRESu2+fSF?cXKo9ChUKUjBA!=kPssz#8|PO&su_NM>W$#bA6`gHQ$tQq7Gn&(v4u3zE6`&@tbZ` zYS=AKA0M;`tkwC;4JmpvJ{*iJrtAT?J=hklbh`IF9!lkMZNA6j;j4< za8nd16zUFi;b@XepOz4$@frY&V!T06d199v9Q{t4=I^UE-}>I#iKc#O=VsUqQS=aw zt`ZJehhY^;uA3CA5rX~&mVf-`IQ-UsgS*2O9v?I`iZ$!uyj~Z%wI?_^`T_gN6{4|E z*9E~*KO(pN;5Vmj^Zw>z55m8RkM|rfoL3}MhRQ4hHylBM|>61M1 z<*QS#nQCX+(8<-V<+VlUJ=y-!?TWf$P3!lcb)$8jD0&EnLRZU@anh`6P z^{*3iRs-odD{6J3ZjjRUC=ESi8|-!Fv4%rjUEq8zYIULOYv+RNwe1EoEijdTQ2X1- zZYE86fNRE~y3L^_t`e4rx>}1|wTh}IlF##2>Sgw|t(HAdcN=WKZ(P7a@H`6+Lzn~0 z0{xwXmur7dih{0LtqJ=3?!s2|DIBdwp|ZviIsF)y7(=es&%a_*52opIR*WakE;AwZ z;iRjW`XjmCsIz&n?KEC+_OY3tpU3a_lTN2G41$k7T!t@uh+xkm%m18?oHVJwc6{pqYH?>WP`h1zs3z- zuM1iBkn-#f@JahWW$&b3CaZ4q$c3*r%nwVeWVP-W;6bhB!Hk@3@8yUm0>q;MJRUbb z!NX1;Hwjjkp0cJ-gTDZ@?-OM=fwjewudJgedhNMxrVag57m{_6R3CiVw!zMM{eCaH zUP4csL(!ltB8PldgV!v3*+1mww!%X6`L;quFQ%rR-q(qYvcZY6ITg)U7G3DpX}w~V zo9g-zg*)G4J^DLVw9Y2-nlo0l_T|W3RZvj_rHx%OmTkUi+x2$qrr)ofr=^9UrPt@6Kx_@wstpoGM@BP$F z5PjU}DyIHCk!TQ29*?I%9MR_Qa=FaO$q9lW5RFD_^Yi&UrSkYwkUk8Em%oC3`;SlM zy=@@{3>B@Ar20iB;BX_9B#D)kW#;GOEG@~%Qj(I9Avtd@@2oZIRD-1D4I*>v935C8 z?y70fVDf;B_4mr97kJ~J{der{yv+XIJu1~zX9<}E$$z^NGf}qvZ!G+HDGqubd@Uw{K4*#ra{>}cnfb8CfPvv9A}k@3ze`q2STitmUC7qi%)A z?LJu}!!&wL9j1Q(z}w2d18i|}t#5H+*DXanA|#*Z-v0GkYTz_5#nrzvx53)xezxCZ znKI3}qs;lDR9z=9rpCH8ab-2mwZ+vV3!fzsJkO#PVX~)3hT2vzwWDA~xeJC$6*FoC zRSMP(o=4|C13=dLEL-X=>v5;w__$3U&Mw_j{3gfbQvB{8+;r41RoP12YN?N!@$saq zn2JWD4Q_Q#(Uf;YSDfi{#bObU$3wMRWq*IaVSR5^Dtvk5O&*=qC9|<<_V|!s{NWx; zccxjKw6hDh5igyNpo(q0)gY*#yFhAw9_rt_GXg0H0*OSNxw#0kEaLSnQOK8Try#rj z2s+gunG}?L@`~f4Q#*&bYGWEz)e%G;IT*l*MNrh%2ueGnhW7KT!0@78%~uEpJYAMB z2V*&Hi}yEwgO%j-oSXk_!|-fS3;BM@7fNg>83KMU4=q1N^`U+4Nbj(_yUWJL2D)V+ z^5DS(R+pY&`@#Rp+Tu0V7Ox?BR=Jw|+xT49*~z|xKX$dYUa2ZKypLWy3r%4Ayr^qt$=af6 zG!T{7yS{EtzSuB5R_<|c|9aC>oqs1raWGr|c4U+dfQIc~jD3oLn5d=N)}zUd+fqSb zQbTQ9T)kKmSEh)1WZ|dmnmhzT3a*8J>z2th>I;THsMV@Ri)k|E7n2lEJ zX|v7KQRpWI)&6~yvD>1bl@!13uQUZyy|iE}b#o?|X7r@1m}+*bbBd-;*aPwW88TV8 ztm}N`+CSmZOMjzb{s+h39Q%72`cc~+7It)M8fFSkLXwCp8o(!bD4t(}+!dg+Kx+55 zeGH`2>4tf^T!o@8vY)zzuBXwITO=0;6Q?Jc6uxfS1XFydY8*|;5)K6sL?0f(KhXQs z8T77mnMsTXG{H4eI8 zI*UcU!D(??gvA)25MyNZBQnNut#Evj4D!7sIY+Q6p^`y!WzfiWS^8|<9q$#DniQiR z^Dazr^49HN2TWlSmDg+2Ud+ks%vsB+R((=R+Z|JHsQw~$8D#@E0p{f9%z@VZ?iH3< ziT*5`hA9A34Gn_F8zDTj*8SpLbGknF6j5=d!_vKukv{8~tZ~4jqS-(^s~=GX+*A;$ zHm36Rc~}k-e?7~bw(*25nCzv^2-75+LeKc9(^X6zk9p8Pin+n4oV1A=MFOj2$9ymP zF!er5E6=yBe-STz1>XLzV|~9~B<{b1_vz2z@whpE@e^eC-U2!cB%5ru+u+He@AvhJth!|$WlduPyA}yd^9Hx)0l`qDZ_|bvzSdbs?gGkD89A`dKm5sm z!z&wqz;~j*&&kONZ@lqF!?N}O?!w|%aMQlAwA0X`8PS&gNuNF+yUD zbHRtm>PPITcgS|VBRu&uG#iO95@8mWA3`I;2V1W)NTuQQ82)&e69(_OrH>(^(+?SC zqZawGQ|RoQ9=!-!k(Xe^yt$btVF6m zd1~RStR)`d#T&mzG#X`LF~R;`01wEmO)D8U#~JG>EnNjommYkv+q`jBQcnj9T?8tN^@K7fXR< ziXH{kP&h94akFedY+d6QnSg!BiiG%>>ho(_$nsW!E0rQvjdO%i78_ z6pIGu7R~wp<`yyTL>s6&vg>tDv$?AtR=Uw5ipa1*(`Mv+l_OUZf ztG@o4n8FlP0KQ=}6bHbC3zx&4PCG6U4qV{tpZdqVa_$4_%Q;M(Ff0%EeIT|lGz zr2{Yg_{G1&oBJ=aKc!w)%|t}0c8<#HDh0>3<_(p+^%<6eSGaXD{Tm4LLM1*o&y{s^>@lpk>P+X)bN}E2HVwi-N<>+wphJ z`djd@oP3tU53V%S7cDI>l1P}IhjPfgP&*y$+)w=sZ)Q}I`8nF50EP zIa1DjDL;+U`Q8j_u8>Gx<^TNEZ`U6G)y8j=(i$5i+UapLfD2;vX8@mUe?LcW4O;70VhkV76s-vW9ui7!^7Vknf4T~u?$w(C(H;wi5g z9PG7jb3xY-gx;y(%U+Fj$?RQ(bXp}~Hf7gc5daJ+Qh)3)bDQ^6d2-1# zOj4A*HC2&9O8{tX#NS!L7FHJ*e{P`HEy@>Iw9*Inl+D`tYhRg;xH?u3D64ynt43*b zCYRzjj_nwA9iy%_eSE-6$A4LS9G{yf5uG=u9mJLC=GXh3>w?L8ocdrUm}Y#a=sPmi zkB~acoMaF2`F#k2Il%OaF3MgXAw+@^D}v+@O&)i9J8yfwJue=QH`E#(AD^(WBw^@j zvWj`9t=YkjOMjg`;N<9g1m#QQ*7Ef5VCc!94PQs$velo+$tdo0E=P}{YZt!G^^M=` zY4BjzW*wgtYJ$pLEK}6WR4Ububw{OAg;@9;tKP?XDg7#<&kOE$;=cU_z)VbO-Pcbp zei3l-=Ki-jyskZ>n5YeghZb3F#(8O%#@+pQX!|)-c>Cr%_+zlJm}GG=$^PCU1wBQe zB}KU&O2>v}1J=<6+}1g}O>ZWW)!rmu2KRr$-588Loz;R~EgUUa@2LcoJ) z3B@q;9ASznJE|tj60t-*P0195Rg{eR{Mm@;4v+IZwj{5!WTg!5St*5Y?thEDk%iUS zzNs-7rze!oV%$D`d_em60PdNO;G8ok;)_<+rpl)ULSmY*?WQuG~}`mpm{ zC`8$Fi)h^JnC|wt2y+f4cL!N+jUZ`-`Uo|rI&8j+=KQ>sKG$4>6pkDaj^4x2kBG(= zyKa*p#?bXN0m+Y|)CX;nB%o==0CwN~)7)sd8<&@!;;T>p_YIGm`SZ=+1d&dz(t;={GnJ%D1qLZM9*+)e|O1B=L^6@K{_Kgr&~4zIua zZG52!sgyRh=#Gp2(zabd%K9DtmGU2$-@m}43ts?WUcP{lFV#|$lRQF6HE*hYp#G2t z$_BD5;gig54%YTnl}nfE`$JJQ6h-YzSlM-B-bFL*QI><&)ZF)XZf}yCl0f_E_!AwU zvqhCtT)n*YM`Xs-6RMHP`MM3pJD-^Qd?)CP(qePLb6Ca7U(Ic z0M@O3F8!ML$AusXuiQ~gLH!QzpWONPwL84cOs!9o7y>=lbrEwgc=qnFw--vS^V%`U zP$MkHcr^AYvU-{=@Zs@zkY%YRhC-1g zbR$TmT%}MbAqCBoaZqYJDPI--4!4ir1mGyY*Y{^0C(ZjWuPe10C4ju;`j@xrn4XAz z4uFh)$fojs?eFf4N1zCv@w))aeP&Xe1yMH+X}Hy5vj1m=*Ro8eOOFU z7316ce>(CujEhd9YEbL}|N6x>0uHkrr+7*oHVPY~*ce5!>vH-LRX5($@CV$iUi2Y{ zJj4nch>Zf|qNlIx(5luG>sq$UU9S6z=e_0b&7kF&p zi#1UNu-xYJ30^m1z>Tt_q9~?;i`*2|4N3wF3qkyTFNH#hOr}V|x=Cl(jrqh?9`&2; z>-V%>zH`TPc%9}=jostBK>fUB)60B1A|`ku_Mk!G6T z`MB_)uXRtBair&cIE*I%HV;nkx;10QhmnyYQ_lJ$8%gru54g!|6z;_YFtC{fk5~j;h>35M#C1q>u8% z;{ie;Qw$CysB}h(Oto`oeCWbY@WAE9xi^~ylFNGzZv|YekXI`Wa2^*t001BWNkl9SN8NlvG!)_JW|S(6_jA|`qB=*7P7qYoXi>hkei z@-m1aQ&c&kR1q=3m*+ph8%LX5KR6itEyr9-)GOLl=PXFS>0*m2Fzc$KT7M0c7p-)% z#l`yfWDBTf@zgKvT;$pbt3tlQt#|V*tcZl8W<{tR@*wIGe(!T!NIr*>-{ZmY>n)L; zRLb18PHBOfp_A6kV_DO6G^6f9QFR<2hkAc$g{H9jcLZgL^~FcIl6a1}z#>AxU8|{b z^5_M#CL~>Gj4ElGfmbj%I?5wS0>z?vvq;s9hBU_~G=w|Oc~Md!f#!Oyd@9u=sJhT zcQFiuKayqklja598A1BOm;bjheXP!Gr(SAs^b1raT4mR0WHTw^$t!&~uocC4g8)mX z^EELwZq6?~P5kL(E!yZ5PHm$|B{)_qE%V1s-O{&ykuT}>L5Dqf@23`|m!MJ&m1Y@` z2ew@xDSNndwPCO^SgXLs<)UJOFD!nIH;-QAuuZQN(U*kWjXu}gKT=gjbtwd^GKis$ zQK~O3EU+%iT;Jd4fqqD*u4`o#nDm3z)U9dcJ~@ukgvhj{)!?zssxp*HILeU1ft&|H%2qrfBZn;4CWmk;7R(c7rCfFV4TfIDFyL_Cy!h$5 z#;sc$?C!q{z`LPPJ#yrTAe|iFBs%x-;2*+a#NhS1iOyZ-G@dB_w_ zZ7AvrS!JK1u8_c08%(*Ze;0!?7ca{ElbdgmF1&|UK15DFf@iM2eePTmj`|aQle2#B z{v8aP;x`E6GTZ1kZLSk@@;Y+>wDb=eXta$u1{DPOCtvTKc+#Z~!0g{Gdfq7vhu&dnT&`=S52@-;ArU$El3c;9{ zV9d+;70_c9PIg+)L(LKJs$SvA*cZs_3JccXiVN7XFh%IJft zNr|LR3?n|81(mLsdzw-JRAUc;vWH9AKQoCaz33#Q0DPiMSca?u=yfN!W>>WJV350c zQ!sf-3FMaeU3(@KUHdpNE>Wc#3Mp-$V5CA&64>3**xuTly17WV&yzVSQOs9r&42v? zH-&tKeM@Nh18)4@0DfgU**V45-GY|&LrZEbA< zkUg1f^uk8T-QmJ|XUo=pi6^ddn5u`iAGT82E-yXJR^}$*;8yLqZjF2U;m9@mV8xC~ z8G0I*+sw_`+0AkFN*pQRMe}=WH4e>vlNzYdTJ+;Us>%ebozr~U2_*CJ#DDm~b$me&iMfR_y!W6?Z=abdc{9N@W5$P(saT`; z%eWfPKD_)i|KLaeN!#b%KKL$gr|P|-B7s%5iyLEkUz<-RKg;d=zt=FYT@%?-sY0O; z;P_bMD1AcJr6Kz2VrOS3M=~ifH|OWUULQh6hh)XP;7FJQ>tI!G5lLD0tjDJ(l161% zK{W`rpmHhgy=7DzUDp7b3Z>8jPtj7WE$&d<+hWBnxVt;Sr7Z=D7k7t*1QLq77bxyp zq&NhZ;DH;S_x=9g`|JLB*UDOxIWu$4p6z>|J?B8ojYVcp~&?z+{aYV{w$&6X?n(v>F}*Cb$-uvH8^7$W5fkW99#Niy15)h37{U&PtV&L699bp@0X{ z@D&&Hu)wS*qspvJF+k2je*wKP-dt+AE(@n0@(O5HV_B}qe=(>QK9Q%jgPoxGDL}~O z(hdA+OqCgl+annOBj;O_hUG`?Y@O+7i|nUEzx*RvN1D^6>5WB(o^^~=Lh+B2k|*SW zeiM~`eifOcEbd6DgE$adoG!n@dq)Mybanm$cu+gg(KWWH+~pA{xbP|+4a~Y z{cUkX_ZQ2~MQ7h7OhJ(0RtRBBE*H=OMpW|?sc0SUa;Bap;;eSH_Bq8!AfuzZ-~?cx z8PcaC^mn$`b^Mrynh{Dx%1@kuG+Te`a(gTQg@W1_i$O|lKGK+oY2>)O`=Cm zzevg%V3^PE6~!Nl%IYp{6dy_gYqkEI<+7Cr+QQEQVevVzI(bCs5jGYsj63-^@r>+s z)!N9Z#!W+d}#zof$Gx<(81(vcNDU z9A~wBPmHCyRaKL1=imox?}sanleo=+%LuZrGA~}ot*&xy#?cLi)z#6S8FIU5ki!Fv zixVIXk>@zfZq|=B@3i&glJ|gUN2~2n=zm3Sc@li!d(|1p;5agD9_LAaDLFiR?7#k} zO_nx``^^^JuZFksb1QPY@c1FOIrB2&HcZlS*zLDHDbQp}6ALS?uq@k$MsVMJ~b6?QH~2iB&Sn2AGpx|$o9ywM zj!Tz-H;-7ZT{0(ex|_hc6;hg`zYErYZeh~64P*tUg5vAc2Q&fw{?58)i^%#(HUv<* zljhL%r`IQ8zT4jxZhTP|^_4L^Z~4TB>aVdTEM17L3cKWlZIN%pEeU3OejJV8XD~)F+aIR3}!1H7kF~##;{rF?>CIFvZ z@|)3UN??Ho4}&b9XRq6pJ+%S8#M3Q^mlmgTB3}I1%1BvIFWM(2xWvT*oV<;E6OewX zlw_4>A)6K-@U+NNU5@q(5+)nMH;qHJ$0NeEmpEt?;{#~_@qz0x2`~nK@M)&(2u2M& zkbAuq+>=ZQ6fW+`ohH^gYI~bEqXIbWMjylv9UQ%{4};kdao3IT24@^l5vs`lN+85| zHcV!NFUqX8a^IWw@OmFAJd7*+CSl`Lnff`x?uQD=k-|frx!=ksCFkjGXz?@5u&Xt0TI+49l7 z9CMqiHbg*kd^fa(LgRLvM5$k}1TRJJSj>%xmQi817${ZoR?->&1r400Rd?BM+2O9u zwEdZmv?eQaZ6}n^s-eg19wD)C+ZkgU$fe@}yLl0D-&=dhrm9)s|T`l!Cb7G5T}GS0(4UwfEleB?-j_iZM~RApU*j;{>F9{^(`x6VBLE zqw!y)nyitr6)k43UVHF+9jY-sC5|+Wz|wG;73kZ&RZ7>BkY$xg^T|8Xn!_57b@>pA zjfvmZg>fl}t{t`$WsLY-81Z23=1)`2n8sz=NO*%gtiTMgjw3+(_t=XHMd_!j3-TJj zYa7|OR@Rt0anjOLE5>hSigcygFG40R)Ni_9B)tmUHrhG#m2oyXh|)7m4YI)Gw+B+W zwZQuu){wcRjJGl*tQ1`(zV>nHa^<{hChjlOrGf75-A#ymGL7GPvZ@WI$nrrZai>w} z=hVm5=sCTRmwfWEd^{W=uHZ<_q^}T9Q|)Do_VB(31wgpdo4h^R_S(UG!v|Z!-L6Y8xlc z#n*>{ayB!hf^Zw4bGM&zdS-DW`foIMY2y$k9fe`5`*6u;CrBcZJ&x|_hO{!L7Pp7H z5!4{G=|f9wDAe_hASpM}K_yKD(Sw=nHb2LGL)ipUSwbohq*XtFoth*ex28c@$j>^O)~veTYeBR| zHcGunchf!T;78j$SZVs2kHPB4Vk!Du9xIFTaW$T~1 zlCwG`tvi{)~}XT}v)kVxCba6EXLPf`2FKMU<& zZ_vd9#-k>SJQl_ftxRlclGx0pj;SY4!SvjQw|zq8TM^A0O}FX;m!R~SKdT4aWmGS? zdmE>gRuM|GRV{aMkCT`*q7C=;Rh4ktQywF|4EC5NiBbZ6Y(8Ke=3mRox%}Q_UD|Fi za9^0_TAB>#98Tw&Wl?GrP!&+Sj|wn9O#Fqx8IY?>+TiJNZoDPF6*xcz zllO}BQ{oxnAGCBZ1#xo^k7Xt;iW(bh4Qq29lbkk@kG?Clbq6l^R5E>xVl>a$lv1g7 zd@I5-1QgGaR3u?vp6Q*)%ca10C(VB{_#QkQD(<~`M(v@a*Ff(COZvV3-i`RvDpK=qCNd1~jzr}{cRQ;68ef{5Lb_^t0 z--sr5N{&e$42n@R&K9Hn@i_R4St>8h=H9%8r9meZ*Za^Uag2}C2wpa8kJU5` zH)*nP?RzwO0?i1%=%_@y9yHIM+i$X&3gExcq}D)82#r#H$|AzOxxD32RrxRue=s&V zvx#%=Kq@38BNy&2Ee)NOvj$wzct~RLoyhHM@TiTsp9&)1` zx&|dPQDRh-eSoRsU0|35p&Nr47QlQd*4hA zO*Wg}U^I3~)h6Z*ernGK>jyY69k}MxRCrbV@f1mSZi(8lio&QEtS`A7LXq z?~_Z<^~ zT{m$sxI>Y}jE}=T0@Y9_;v`rrJgmdoJBpq6($3%D_(MJP2a-%4?fgfwT~(hOrDksM71zi1HKf&5@E1-%#48bbRIfto zyHmeGOgU8VPGOapdb$r~wB+AKu|-C0zOcj9rh}H(p8RY!8(GaeluYNo=|Is&?`WER z!vkj6t{&^=>W2X;FgX+{!x(3|O046ll;BSNnbN^=ZgEi24CufUasB7qdnar9!P9f@LN#+6UA zJDlrI9Y+;J=pWykK25|W3oybHOfOz>ItT@cxJ*>_)M#R@}VUPEN}o0lF}5sHSTXV$n64?9+b4C*d0U$&|RZ*#>| z)fq+6QsD#CHXO5;%QTI_3I2{Te-(M|1sKlxG624IAf=>bEXv`q4Bqd5K&8Z`5h&mi*5b=};kE zB>Tm>POR3n6%Pw=2-;JAZB?}L^gVH2yseJIntb4TD>1EdWb3rDi^b%s3Xu% zxdO{4I0y4_^}5Ci^@;r_eoB*^#%N`c4*e!<>uwkMH*pk24pEHdZ9O_0V)Bt{3eYQ5 zAsp{I@MCflgLATR{*&DE225&iW~U0)l~~P}pg>v~=@=~EHK4^!8ZMO*Y;lz`Tya=F zYkR+py}uR)2G6-|awmGr|d9DQkoLlL*R{Eb+&fS**#G6~1Gx zaw8>xivDPk>Ek4{?lf8J$I*9@LX+mR`Popq{{QDC%ITxWf^0T8G9t6e>kNi3s0cosAC1UHfg))dN4AJh(UH%JGPsX zkj$c``dn++vWK~NioHBx49Vx4@7%O>Hy$~y6VLhFvLs85KQsn0cdCEAS%rUKxncso zMVB%c3%>+5dHo!0#MZ})T@snuxMU1+fQ^Qb(}+EZ*1E5P!-$E$XeumYscPHGEN+^> zwZ`Q%vR994vRF_9eZ`melRWZ!`bUQKrPkA@cYmV~5~#{u%h(f1DF?sqoFLyKRZ7dz zf*~9H1n!`t6N~V3k}mSv+<2;h^zQb&Jl>~-joPC$OSjcuur!TSUc5Z^OZr9Ej}XNQ zJ*b@UX(T=_T_$Y4S+~EFRbs>V_U!2en#R6t-PjZxcxm?uUyMqQrLlhZjt?*2@zt7D zVs!a;W*gr8tuUcC?d`<08s`Qri_cJhG2GAZ`?S`ld`5!_FPJBuhb6eMYxb)h@LZ($ z-1e9PxAxoXMj{x_bzhM&c|(kyPM{eI66R~@URE>|u-$ZXCbC4~dyUZ-*xLyKfjc61 zS5*T4S_7-hx7J#UN^{iBLVCpiBoroT>ze;s{7Cz@=7mqEnZSCp4m>gqwCht4PR`Xe z@h9SYr5J=cTSnh2*}HS1&}%7A?IlpP8FW1@>*V{)DhJ&5G%h``eEjzI;;c;NrnzCX zRR@VFVl=O0YKyvSZN83)Z4kc^ylvTSdw`>x@{R3m@txuO4=-GSQc_jQ^PnmcRu-+& zWWCMd9L=iy6@;|D>JFe}wuxw@)-*mNbUo)S z8&V%cS$7&x*2R=fUtg{|S}t2;Pl zC|6YOp{OQoWGSi%Unw>W7+M!L@W`LP1})U(aeUA`m>GrX;7P`0003~4r82EZg8k&$-~JRS11^M3hwKhrwHR6NB~H%0f_x-^ zTuF!ivRUdK#Kj4B`bjo^Yo$B&ghaQ8X-dyeie5(}NtXfe@nb%PMTy6FE-pbh6x4s1 zPNy?YY}duky#p2*YCl;P#iZ zIV`VMHuh=wsMEE|`Yj5x$+@1-nW2%s0Ld#ely`)&#iuCBdI@=W17F{ARWyB1c3!mZ zQi(Qu!519NqKe-s^5B*=L}g`?(vz{2d>#%!YyYqe6l(Dj;?`xiKnLGsWk_1v{EUJ$;lmP9|-{~_Z9&a=-Lp@+QNUn8gp68K! znXDbW6VEMxsOHBQ$YUGwwWucdl?_GSk9m)fZ!-2?x72*6ER-t2uoeDmlw|`^wth>e zkyEt#msP21`{a)#h!)4;=>}Q}{+(Rm#1uHw*S@lU6ak+%+MLb%^l3F1GIYG%7ZUsJ z64o)B3)d)Jc+DQqwZnUei}=(D6#uUlU=XQhs}a_&XW~ED@KXf056vvcNEx!eIP96J zE0L0oYF;UYM!+q3!WQGi=;h#9Vo9zRNdnIG@-46O`rN(wt5sn7Y(6mOKPO41cXH%s zgQ$iW&Z4ff!;3Dj*+<%8=Tr-AUZEA9>xD!Z*sS_7u#wGc9L_UxRO5%MA)4;YDj@6z z&DHR!9#DDB*ashL4?AtOR6^Jcm2-}8Nf!3GdPYRKM~9luegM(4+TqdafSl5kCHpP+ zdVjI?vNvYEM9``I2n>lanD9NlP)#x>TfaT{oii;@HPk=VdV7-HP08W%-LM=d!Qv__ClDjzX%! zY3oVBe)e+)ra8i_9p!o5%-+#m<0y@I-j+_K^j7m2SMMnhZCEH}-f?XJS>u$uR|mLF z3>B*e<9W&e03f>|v>IJs-o%R?HvvW@*nwysHMiiW_9Te7G5rA{iFB!sI!WQhsJ zPYIlrRN~WI)|5LJ=w(0AbQHPKoW~nR2L^a{?l@EAJq$C<2>5CAti`n^TKxrvU%X?D zRZdQljiw5alPjsRu^=qo!@-Hy?dh;+pKT+Nx>_)^L)Vl$v4!)^>z^UtnEKT&UvGL4 zK(6tsqE+qUn&+qWa&+SRoM||c5V|bafd-!{j^Sqv)w+-Dw5IV`VubKv)hWY8Ck$V4 z002xPI92``2T)ngW{-f^r8HvOvxlUpZib2Z#I-C|kt)uPDoy?;m^shVE?VQXsc1&{ zQ~b@_Vpyfiwf?KgqYXV8?di%CyGVMKLcK=TVb`tG#1a3Vb~o$fJXW={!9j-kQbX>J}z79ON4)vRa-5RT*vb1H>Js>8C1&8Q#K(29(*GVE*#5^ zz?E}K)H#+~s@b%!T{Wq9JY&sn{|4@-C;;BGB4$Meb7X2h3tYo3sEK8?UBtg4w7B#_ z!}=xPFCPs?O+UqYzEjgGryz{dy;H*x3p_0&bU$b#Cq!IGqwypU?SN9AEA|b=|0N`?V z+-MtkyHPAw(3c8hNyZg>DF*Fl`FRziSU;?NEM+JIK@ut~t{mvAt?qt&{{R5!4dW(x z`EsQM0#i4X1!KU$rShbzwDzG?SbR6a|54v7P-oRVKN!CL6)L{TmPC~Wd#Rvq%f%hYsHF_`XQFZnU^;F= zl$h)9(U$>=KK2xVYmQ+tVoVM#*E2V8&Q{+AOOV*YJyY0=o@pph< z-6FYYH3pwx2n`rw0L3tmpCrAK?1FcqrN0#NM+XoETG)3#kyF1{uKtXNkMHkOg>|Re zMV%xe^I`7EP1U9$ehr3FpP~xc$_&Qg9N6bqdU_7SGCrnFeCCqT+@N{akua}E_d=5E zS^X=~3zAQgA{cd>?H73%`n@P3LljtT#k}PDDbC18Yof3w_p8^vDgvNZ$MKltpPS3S z6LE*asE!o*y+2iZCR}C-u-lf4z1qX#8%H;lIdCPlw1#6$G2k$sp_#9t)(pyz6%&km zcs#X*72nb1+qIDg9)+661l#ScvLiDd{tJie1!`)mC8B6f>f5g^9jZI!EvQ5%i+|K- z7}YqMg*Nq`s_;%om@lE_k%A4*FX} z+z)S~=ig!lYcrjWp%wAJtTKvI-;nmld`=C{t&dUu_k{+-h9Q^hBD+fOWYSl}1VBoB z#rOLBb7kz)3wzWUF3Ucb}^YGi4Y^uow<=n&gUh+^* zF=^qQC>NKMnFlUtEqi|&zIUk^n0;~&=N~& z-QM;&4hC2NFGH+3Oo&=F5^SyJyGqUU9Me`e`BI~|rQsRoFTlp^;j8N>e}iteOPMjJ zZN@J&?wd_Mr+%c5Qp89tpn5fv8CVY&#Qgo7{zAhVXUG&YIG`yq)lTLfAAmNm@0dE< zdx!uxkGR*v{?mB(LL>U#A9~6KTasqI;GEWM+M5Cb$}ybx+EI+vH$=$+j??KW7G_WPeK^T|I_%nd!zRM8m*&W{kIW}nQQu&qJR-f3_|`L z9bion``^aTJZ$y#^$4W+Y-_*IzkPqMRCjW6iq0V@-cI;-uMhx0cz5bOqyS*_e~yoS z{oDTXHTb@@4Zw|ce|+-&9z_7I_y6MM^8+*7`_t$D{2#{t?c#rr{tr$6V~PJs&i}Lo z#)ka=sS!W$1-ugwnASeOHzEKfT-yIPYT9o8-opgEA3y8!%BZ5>Dc0Ki?X;_JOE|Rs z4}=;guEE`+R*%NMcW@AN`8WH%-z(X`P%? zQvB&U+gmGaTIp_ons44G z?49qHNA@QP^jcf`NebK00`yK{7-$M2a3Nt4xc-Uz9D#=cD%GvQpxc=qKdi>sD+Ry# z#I@)LOTXsIM7t1;T~A`8=8DV2vXHXP($$?^zo~Wee&w4*QCHG#U|g~y4Z=M~heftN zR-}4!F#MJG4r3qU_Pp>F{TyltXeho)K?UEqHWIw!KvADpj48 z9~H4C37z;1=MHfE>w-4&A@rXtCTn81$u)zJ}Z{(1y>jW-<4 zhYQq~SC+2h+Jgp@`OQ~Xu^JwM=N4UEGl}z##fZ$KYBz&;_j6-De$zywn|cQv!RnnP zZuyr-O*KiMSd2;-u^DYT<|W;F^Fq!+>OhOaxBX3BG2TWQE%n+|1${wHr*Jr-bDD9W z)ZUDC`sH8I>ZVm5_)?3m!mV$#`oo=*YBKwAV;R_qvC@VUnOnyi=W3v4i!p z1O*ZQ}~d&0-JkaYwJx0!d*j`q#WvX_Kb`xKQWVqJYdy_V0h!vqv=U-bRE zAc(>(E%2llpQMqFe9>64)hB<0PXqCgs-JaiB&{D+YyWm#ut8;KLkzy8oK|f9EN~*~ z%bRSmXRzgo+w=WtNB0PiYlWO0c=ii73#YO4)xF87aZTqWor&WL?dsiGHDUBPP6Z3B zNK#=usEja0BhB4;?Ba40c|fxWKKnw7^-Y$gYJeo-++l3v=0Ss(x^Y)EVw%i*BRwR9 z<>*rD9jP>44s1K=E(03?5VWsTMwcOC{7(t$<%bk!BVPI-8UioU@dvU?7GekE>52w2 z!57eJ`~GS#Cg;)ku}xN2nvJI&wn0pduWNwvC@cQ*K97!hC9;7^H|4aXAA03 zt(udh%qtaG!x2qRQ12|Gm(v=1M61Lp6NUx*sRgGA@oQswoIz!g*dT2^|57stC{ko! z;Ocaf$DBPxM11_zIJ*G9Mvxx$65d0$u6bR+qI^eVI3*^>udi3MLu?%gkx&v+F(h)7 z-+E_|kaMNySCUqZgoaw0o}KH?o(7Es7mIM3f&ZY zo#C^H*yuQTCm_1<91kw6e#D4jm4#8cJ?AXW#%sTzLovfFUZ4ux%VyX25tY~3{T8MS38xqEByK*hO%Yqhi^fN7-Gvt=lRXS*O1pr)<-RtQf?HK{C zm)ik8k;zOa>jESaDBpXATI-<(-c0O(OwQ)V->h31H&vVpOH-g)Tm1}uw3~~3%I1r> zA2mV)Qf`Iskba<5EbgL$_m&5vJ_b7*^YHQo$B6Lnnz3qLTZ4SEnk(^{h=e&oYnY5@ zgc@+?Q_*RY8jYp!x`$jyW9tZ!Hi4Re{x=7z(4CrDd(eRG8AxP@>zyW~R|WBv5`K3| z6_7zvgfKKKl-$;9Vo>%9s8-GpxGDB>+Jm+=yMs3I%KC5n1*$w&-I~SsbB(gE3Q4nn z;09epQeUxsJP)eW^z$kmvmQ18ueF)M15l>TS}yR7q64k&t1=W4WXRehWrA&pn%~rG zvCZJ;xPTJ|dIMvdT2++mJ=}(U9jlBWv09E3p+z)yb~~J>ZB+zSOU%9H%EM?RbpW{! zWYM@rxnDd`hwK;d&Jvr>5UhKmZHMkt@zDO2VvW#flD_j>9rRV1-iCMCSDh~C6Q9NE zqe@c7N@jj`AnruPrDIF?VcR)(+V*!R$@`S5lmDpv@QopdUuX^^r?%az{f%4hPQ8EA z`#SbKpc4E6J>8UHvbno%@at3PqejP*B6N#_L=$0M1d3BM$=Qi(V@EkD_C9|f4;nVh zZL^AxJ7b4wwXDr(SEJh=Q2jP2k-#I++Mgchy(~u|QNR4*Mrda-9`DHPrn6c-ml=Af zR2qeLiOFfdUWHS0a?tU2UuPe^1aM1<*!omei2c@9o*UxggS|57Ae`iGbgqWX#-jGP zReH!SNI;VB@Wy@3pt8b4DT7CrReN`$7gbV)P>pBRF41z(hMa^lcYFW(2a`f^O+`?! zU`)FQTr9}{$U~KW-rc=k4OT&UP4Ii&4eH&~ib4(ZN4MURMF1y<-H{CIZbG27er(=t z%oia-C-!io)=xpNZW3;}mc<7p))G$+Ma6_1!yCjJnSNvV~G@e zW$O!9-V4K5%a`s|u2)J|{dTg=Q%XJ^!Ko}`1g$#-o;dtn44%{UDvsKBAd&PC%`6Kq+v%pbL$7A4)%CToU z9R*3L|3EJAB|$qA<#zR+Mf{|4(i9uEi$fo-P~!r&pRT>>QIth$&3f96e@x$J5QBDp z>peRXQ>bixyfmXV<^h4G#~8EXMQR zK>0MI-0JHs?ryX|%5rM*@)7Fl4O=mnPyC+hJtUzd1^@i18uCQWPdrFPV$NxFyp_CY z2uBX-htlvp9V@uH_LE>)LO0vnYS?{@G9w%(c81?j)jWR8%EqQ>nwN)Lv9|rE{w|(j zU?i&F4`$ft)UXx@@i2_! z`pU_;uxwhGZry%%T4BQMlpbm9L<*}6T^cm27V;{*qB4MWiO8=B*BC^4c>rUzSgY3{ zX4`A>=%3x-XB@k^zU)+wFka~64f{}-PnSda#LX=9b!!F7d}x1mQD(xZyz=uB zkHNmLmK2_q*_S5a{BX2t?YH6StQ3i2PB)bR?1zoWv-7}_m99vYW^``s{;pbQq;I5N zRG`c=6^UCBF;rwC#EJFSUGCP zkImI5CZ!d?_<_&umUQ>nv8yCXZmiiIWp`3GH@BCbP4&6Dg>P^^AD>~xF1hh)-B=p@ zT&LE6*~*^%YFK<$n{DFii`VwDF|q9tfe6TL@*}&wkh;0AD;vg9wDDDxwfefU%3{cR zIf|X=a=49Hb%+WG?zjc=mUt$w6rZsvZteAFsqroYqGb47shmjlua0`=Urm#AzdN1{25!C=EQ|dVGT1DRH&S>vmmslYiD%V5K{5E$ zGYG!z>$M+i0|t;H396CNuK;P#4(yvrV+ zkx))O`;NpD5~H0Z0-(QM#lz{Kq9CV%i7!<4)k2Nc-3Agy2$j2B#=B~{>vQK(?O{>G z`ALsmU)LzaY!oVKPqy8YbH?P}qdu6;t*`HUtG+6trF^AD3)#Z$7Znpk+tVwhmx?Fh zFCs3zKCru!0058kmrFG?mh@3NVD>^cl&`9 zX~D!!BQFg?o#BZ3kI8L3^*?HD!fO;8n&)3ZF>%vavRrM-pU)kHFLS4~V>z3Z4*?6@ z=3g@p77OJF21oNjf2!O!tvVzkLUC@VKT`*LkQ}X$p(LX7@8S(sf91;bb~Vc-Ldw`%3l-o%Cpu znOV9S;jjHbU|_6Sl6|h;BoslhsM*_V$hR@;V0bei*)XGb27B`=@UF5!{P2IKFUM6De4md?_7_aF+B+;vwLS)oiyaJ%5)V#B zcV654P83L#SXLTrE`xj}$-Pi;nyj9>Y}knl+9~Ydv$*O>53tNzY~K5b+F#Ts8<+#f zpoLlT=kdr`4%(gsob_bzdD=*zM9x!uJzO)vw|klkiAZB?TCA#!eC?%1W7nlyJ=*K6 z0@l04yc44fRh7&?W5KPCYJZLhw&OlX>_E8#CY%STd;>HQZ#ix&#nWeHcdVnvKR+Pb zUS2mmy$BoLuCVc9Y6Lb}f1z%4`+kMr`oSHS5gFaO03Fgq|Lq9f0yE1AyP5rTN}n(9 z%W*`tX6%<8xfyx|mgD*wArw{~7Ls;?x}@`jsRceH2tL825O~$V4 z%gD-h0J2&WvrI{)BlMT;MSN@VCv2x?mB7e! zoM+rmLcDeKJEpb5+xz+#@eNQx$=f-HVdC~Z=JAPKj`1lNF>&GVZGt}EL6VBQfEL_j zw074ukAg%}YVObFV9RFTrmrG}S%KoU-)i7?O&%L@s~hK&pGDE0|IVnr=h<-6ZF;9p zJdxM@mb#@ZbQ5}0jt;Yrp%0>JbE_Af-$i^mz&L4u_q5wNuUH}D%#mGhA>+Hnxs^)q z-+#_jhFn3qr>=DVopZ7Q`_jqSO?{obS}1x%^3Ol2o2y@j_(_ux+}9^Jn#cu?inE@| z3>r*tBmT`ddkl_41LNsYncXc(40%CzrM|V16Ilff)Bo)a@cd%B_b29Nz=+iUm$|IN ZJO4cN`E)1jJ~0K5lU9~Od^Gv`{{Zh1+bRG6 literal 0 HcmV?d00001 diff --git a/src/lay/lay/doc/about/25d_view.xml b/src/lay/lay/doc/about/25d_view.xml new file mode 100644 index 000000000..f112260b7 --- /dev/null +++ b/src/lay/lay/doc/about/25d_view.xml @@ -0,0 +1,147 @@ + + + + + + The 2.5d View + + + + + + + +

+ The "2.5d view" offers a semi-3d view of the layout. It's not a full 3d view as the layers are only extruded vertically + into layers with a certain thickness. The view cannot model process topology, but it can visualize + wiring congestions in a three-dimensional space or the vertical relative dimensions of features of the process stack. +

+ +

+ To open the view, use "Tools/2.5d View". Currently, the performance is limited, a rough number for a + practical limit is around 100k polygons. The 2.5d view is only available, if KLayout was compiled with + OpenGL support. +

+ +

+ +

+ +

Setup

+ +

+ The 2.5d view needs a technology setup explaining the way the layers are transformed into planes. + The setup is provided within a technology. Open the technology manager (File/Manage Technologies) and + navigate to the "Z Stack (2.5d)" component. The setup is basically a list of entries listing the + layer from which to take the shapes and the depth information. +

+ +

+ Each entry is a single line. Empty lines are ignored. Everything after a '#' character is + considered a comment. +

+ +

+ Each specification line consists of a layer specification, a colon and arguments. + The arguments are named (like "x=...") or in serial. Parameters are separated by comma or blanks. + Named arguments are: +

+ +
    +
  • zstart: The lower z position of the extruded layer in µm
  • +
  • zstop: The upper z position of the extruded layer in µm
  • +
  • height: The height of the extruded layer in µm
  • +
+ +

+ 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', + the upper level of the previous layer will be used. +

+ +

+ If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to + 'zstart' and 'zstop'. +

+ +

+ Here are some examples: +

+ +
1: 0.5 1.5                    # extrude layer 1/0 from 0.5 to 1.5 vertically\n"
+1/0: 0.5 1.5                  # same with explicit datatype\n"
+1: zstop=1.5, zstart=0.5      # same with named parameters\n"
+1: height=1.0, zstop=1.5      # same with z stop minus height\n"
+1: 1.0 zstop=1.5              # same with height as unnamed parameter\n"
+  
+ +

Variables

+ +

+ You can declare variables inside the setup files and use them in formulas for + computed values. Variables are defined and set with the "var" keyword on a single line. + The notation follows the "expression" syntax used in many other places inside KLayout + (). +

+ +

+ Here is an example: +

+ +
var hmetal = 0.48\n"
+7/0: 0.5 0.5+hmetal*2        # 2x thick metal\n"
+
+ +

Conditionals

+ +

+ For more flexibility, but of little practical use for now, conditionals are provided. + "if", "else", "elsif" and "end" for as in other languages, e.g. Ruby: +

+ +
var thick_m1 = true
+if thickm1
+  1: 0.5 1.5
+else
+  1: 0.5 1.2
+end
+
+ +

Navigating the 2.5d View

+ navigation + 2.5d navigation + +

+ The navigation is based on the movement of the camera while the scene is + formed by the extruded layout. The scene can be scaled to provide zoom features. + Scaling and rotation is relative to the pivot point which is indicated by the + compass icon on the ground plane. +

+ +

+ This is a short list of the navigation controls which act on the camera: +

+ +
    +
  • Dragging with the right mouse button down: change azimuth and elevation angle
  • +
  • Dragging with the middle mouse button down: move the pivot up and down or left and right
  • +
  • Mouse wheel: moves the pivot forward and backward
  • +
  • Control key + mouse wheel: magnify or shrink the layout
  • +
  • Press and hold shift key: switch to top level view (see below)
  • +
  • Up/down keys: move the pivot forward or backward
  • +
  • Left/right keys: move the pivot to the left or the right
  • +
  • Control + up/down keys: change the elevation angle
  • +
  • Control + left/right keys: change the azimuth angle
  • +
+ +

+ In top level view, the navigation is slightly different: +

+ +
    +
  • Dragging with the right mouse button down: change azimuth angle
  • +
  • mouse wheel: magnify or shrink the layout
  • +
  • Up/down/left/right keys: move the pivot on the horizontal plane
  • +
+ +
+ diff --git a/src/lay/lay/doc/about/index.xml b/src/lay/lay/doc/about/index.xml index f5c93bbe2..4a7efb65a 100644 --- a/src/lay/lay/doc/about/index.xml +++ b/src/lay/lay/doc/about/index.xml @@ -18,6 +18,7 @@ + diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc index 52e9fc2f7..0e0053df0 100644 --- a/src/lay/lay/layHelpResources.qrc +++ b/src/lay/lay/layHelpResources.qrc @@ -47,6 +47,8 @@ doc/about/lvs_ref_global.xml doc/about/lvs_ref_netter.xml doc/about/packages.xml + doc/about/25d_view.xml + doc/about/25d_screenshot.png doc/manual/adjust_origin.xml diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 3238edb1b..67c6bc8c9 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -1074,8 +1074,8 @@ D25ViewWidget::paintGL () glDisable (GL_DEPTH_TEST); - int cube_size = 64; - int cube_margin = 20; + int cube_size = 32; + int cube_margin = 40; QMatrix4x4 into_top_right_corner; into_top_right_corner.translate (1.0 - 2.0 / width () * (cube_margin + cube_size / 2), 1.0 - 2.0 / height () * (cube_margin + cube_size / 2)); From 52754ed4185de5eee4d5bbdc9d960ec3ad5f740e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 3 Apr 2021 00:37:23 +0200 Subject: [PATCH 5/6] 2.5d view and help integration --- src/laybasic/laybasic/layBrowserPanel.cc | 28 ++++++++++++++----- src/laybasic/laybasic/layBrowserPanel.h | 5 +++- .../tools/view_25d/lay_plugin/D25View.ui | 4 +-- .../tools/view_25d/lay_plugin/layD25View.cc | 3 ++ 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/laybasic/laybasic/layBrowserPanel.cc b/src/laybasic/laybasic/layBrowserPanel.cc index a6a16c9a3..b1421b426 100644 --- a/src/laybasic/laybasic/layBrowserPanel.cc +++ b/src/laybasic/laybasic/layBrowserPanel.cc @@ -95,6 +95,7 @@ BookmarkItem::to_string () const BrowserPanel::BrowserPanel (QWidget *parent) : QWidget (parent), m_back_dm (this, &BrowserPanel::back), + m_new_url_dm (this, &BrowserPanel::new_url), mp_dispatcher (0) { init (); @@ -125,6 +126,7 @@ BrowserPanel::init () mp_ui->browser->addAction (mp_ui->action_find); mp_ui->browser->addAction (mp_ui->action_bookmark); + mp_ui->browser->setOpenLinks (false); mp_ui->browser_bookmark_view->addAction (mp_ui->action_delete_bookmark); mp_ui->browser_bookmark_view->setContextMenuPolicy (Qt::ActionsContextMenu); @@ -138,7 +140,8 @@ BrowserPanel::init () connect (mp_ui->search_edit, SIGNAL (textEdited (const QString &)), this, SLOT (search_text_changed (const QString &))); connect (mp_ui->search_edit, SIGNAL (returnPressed ()), this, SLOT (search_edited ())); connect (mp_ui->search_button, SIGNAL (clicked ()), this, SLOT (search_edited ())); - connect (mp_ui->browser, SIGNAL (textChanged ()), this, SLOT (text_changed ())); + connect (mp_ui->browser, SIGNAL (sourceChanged (const QUrl &)), this, SLOT (source_changed ())); + connect (mp_ui->browser, SIGNAL (anchorClicked (const QUrl &)), this, SLOT (anchor_clicked (const QUrl &))); connect (mp_ui->browser, SIGNAL (backwardAvailable (bool)), mp_ui->back_pb, SLOT (setEnabled (bool))); connect (mp_ui->browser, SIGNAL (forwardAvailable (bool)), mp_ui->forward_pb, SLOT (setEnabled (bool))); connect (mp_ui->outline_tree, SIGNAL (itemActivated (QTreeWidgetItem *, int)), this, SLOT (outline_item_clicked (QTreeWidgetItem *))); @@ -218,7 +221,7 @@ BrowserPanel::title () const std::string BrowserPanel::url () const { - return m_cached_url; + return tl::to_string (mp_ui->browser->source ().toString ()); } void @@ -416,13 +419,24 @@ BrowserPanel::search_text_changed (const QString &text) } void -BrowserPanel::text_changed () +BrowserPanel::source_changed () +{ + m_new_url_dm (); +} + +void +BrowserPanel::anchor_clicked (const QUrl &url) +{ + mp_ui->browser->setSource (url); + source_changed (); +} + +void +BrowserPanel::new_url () { QString title = mp_ui->browser->document ()->metaInformation (QTextDocument::DocumentTitle); - if (title != m_current_title) { - m_current_title = title; - emit title_changed (title); - } + m_current_title = title; + emit title_changed (title); // refresh on-page search page_search_edited (); diff --git a/src/laybasic/laybasic/layBrowserPanel.h b/src/laybasic/laybasic/layBrowserPanel.h index 0b3ed3465..2fe7d8aba 100644 --- a/src/laybasic/laybasic/layBrowserPanel.h +++ b/src/laybasic/laybasic/layBrowserPanel.h @@ -441,7 +441,9 @@ protected slots: void page_search_next(); void search_text_changed(const QString &text); void search_edited (); - void text_changed (); + void source_changed (); + void anchor_clicked (const QUrl &url); + void new_url (); void outline_item_clicked (QTreeWidgetItem *item); void bookmark_item_selected (QTreeWidgetItem *item); void delete_bookmark (); @@ -462,6 +464,7 @@ private: Ui::BrowserPanel *mp_ui; bool m_schedule_back; tl::DeferredMethod m_back_dm; + tl::DeferredMethod m_new_url_dm; std::string m_search_url, m_search_query_item; QString m_current_title; QList m_search_selection; diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 5e509de09..04a6aac27 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -350,9 +350,9 @@ 0 - + - Press and hold SHIFT for top view + Press and hold SHIFT for top view (<a href="int:/about/25d_view.xml">more</a>) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index b973cdc7f..6c57d177e 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -23,6 +23,7 @@ #include "layD25View.h" #include "layLayoutView.h" +#include "layQtTools.h" #include "ui_D25View.h" @@ -53,6 +54,8 @@ D25View::D25View (QWidget *parent) connect (mp_ui->d25_view, SIGNAL (init_failed ()), this, SLOT (init_failed ())); mp_ui->gl_stack->setCurrentIndex (0); + + lay::activate_help_links (mp_ui->doc_label); } D25View::~D25View () From 2862cb4ffd0efd7b96fcc18d3609dc6e144e4d7f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 3 Apr 2021 01:39:19 +0200 Subject: [PATCH 6/6] Marked 2.5d as experimental, wording of an error message --- src/lay/lay/layMainWindow.cc | 2 +- src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index d40814b05..41cf82007 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -3942,7 +3942,7 @@ MainWindow::menu_activated (const std::string &symbol) if (current_view ()) { current_view ()->menu_activated (symbol); } else { - throw tl::Exception (tl::to_string (QObject::tr ("No view is active"))); + throw tl::Exception (tl::to_string (QObject::tr ("This function needs a layout but none was available"))); } } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc index 94b23b462..8a98ab2c4 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -89,7 +89,7 @@ public: virtual void get_menu_entries (std::vector &menu_entries) const { lay::PluginDeclaration::get_menu_entries (menu_entries); - menu_entries.push_back (lay::menu_item ("lay::d25_view", "d25_view:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("2.5d View")))); + menu_entries.push_back (lay::menu_item ("lay::d25_view", "d25_view:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("2.5d View - experimental")))); } virtual bool configure (const std::string & /*name*/, const std::string & /*value*/)