From e70bc04197092892b83a2654624b906ab5c30c6b Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Thu, 14 Aug 2014 17:16:24 -0400 Subject: [PATCH] Colormap functionality from pull request #816, mostly by Don Bright, includes new colormaps by shaina --- openscad.pro | 2 + src/CGALRenderer.cc | 77 ++++--- src/CGALRenderer.h | 12 +- src/CGAL_renderer.h | 51 +++-- src/Camera.cc | 5 + src/GLView.cc | 33 ++- src/GLView.h | 6 + src/MainWindow.h | 1 + src/OGL_helper.h | 75 +++++-- src/Preferences.cc | 43 +--- src/Preferences.h | 6 +- src/Preferences.ui | 31 ++- src/ThrownTogetherRenderer.cc | 3 + src/color.cc | 10 +- src/colormap.h | 192 ++++-------------- src/export_png.cc | 2 + src/linalg.h | 2 +- src/mainwin.cc | 19 +- src/openscad.cc | 17 +- src/polyset.cc | 1 + src/renderer.cc | 81 +++++--- src/renderer.h | 10 +- src/rendersettings.cc | 23 +-- src/rendersettings.h | 20 +- src/typedefs.h | 5 +- tests/CMakeLists.txt | 63 +++++- .../logo_and_text-expected.png | Bin 27067 -> 22349 bytes 27 files changed, 446 insertions(+), 344 deletions(-) diff --git a/openscad.pro b/openscad.pro index 2b2d1a0e..e16020e8 100644 --- a/openscad.pro +++ b/openscad.pro @@ -221,6 +221,7 @@ HEADERS += src/typedefs.h \ src/parsersettings.h \ src/renderer.h \ src/rendersettings.h \ + src/colormap.h \ src/ThrownTogetherRenderer.h \ src/CGAL_renderer.h \ src/OGL_helper.h \ @@ -378,6 +379,7 @@ SOURCES += src/version_check.cc \ src/export_png.cc \ src/import.cc \ src/renderer.cc \ + src/colormap.cc \ src/ThrownTogetherRenderer.cc \ src/CSGTermEvaluator.cc \ src/svg.cc \ diff --git a/src/CGALRenderer.cc b/src/CGALRenderer.cc index c59df6bc..b42dd307 100644 --- a/src/CGALRenderer.cc +++ b/src/CGALRenderer.cc @@ -32,6 +32,7 @@ // dxfdata.h must come first for Eigen SIMD alignment issues #include "dxfdata.h" #include "polyset.h" +#include "printutils.h" #include "CGALRenderer.h" #include "CGAL_renderer.h" @@ -51,19 +52,7 @@ CGALRenderer::CGALRenderer(shared_ptr geom) else if (shared_ptr new_N = dynamic_pointer_cast(geom)) { assert(new_N->getDimension() == 3); if (!new_N->isEmpty()) { - this->polyhedron.reset(new Polyhedron()); - // FIXME: Make independent of Preferences - // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_MARKED_FACET_COLOR, - // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).red(), - // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).green(), - // Preferences::inst()->color(Preferences::CGAL_FACE_BACK_COLOR).blue()); - // this->polyhedron->setColor(Polyhedron::CGAL_NEF3_UNMARKED_FACET_COLOR, - // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).red(), - // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).green(), - // Preferences::inst()->color(Preferences::CGAL_FACE_FRONT_COLOR).blue()); - - CGAL::OGL::Nef3_Converter::convert_to_OGLPolyhedron(*new_N->p3, this->polyhedron.get()); - this->polyhedron->init(); + this->N = new_N; } } } @@ -72,9 +61,37 @@ CGALRenderer::~CGALRenderer() { } +shared_ptr CGALRenderer::getPolyhedron() const +{ + if (this->N && !this->polyhedron) buildPolyhedron(); + return this->polyhedron; +} + +void CGALRenderer::buildPolyhedron() const +{ + PRINTD("buildPolyhedron"); + this->polyhedron.reset(new Polyhedron(*this->colorscheme)); + CGAL::OGL::Nef3_Converter::convert_to_OGLPolyhedron(*this->N->p3, this->polyhedron.get()); + // CGAL_NEF3_MARKED_FACET_COLOR <- CGAL_FACE_BACK_COLOR + // CGAL_NEF3_UNMARKED_FACET_COLOR <- CGAL_FACE_FRONT_COLOR + this->polyhedron->init(); + PRINTD("buildPolyhedron() end"); +} + +// Overridden from Renderer +void CGALRenderer::setColorScheme(const ColorScheme &cs) +{ + PRINTD("setColorScheme"); + Renderer::setColorScheme(cs); + this->polyhedron.reset(); // Mark as dirty + PRINTD("setColorScheme done"); +} + void CGALRenderer::draw(bool showfaces, bool showedges) const { + PRINTD("draw()"); if (this->polyset) { + PRINTD("draw() polyset"); if (this->polyset->getDimension() == 2) { // Draw 2D polygons glDisable(GL_LIGHTING); @@ -106,27 +123,33 @@ void CGALRenderer::draw(bool showfaces, bool showedges) const this->polyset->render_surface(CSGMODE_NORMAL, Transform3d::Identity(), NULL); } } - else if (this->polyhedron) { - if (showfaces) this->polyhedron->set_style(SNC_BOUNDARY); - else this->polyhedron->set_style(SNC_SKELETON); - - this->polyhedron->draw(showfaces && showedges); - } + else { + shared_ptr polyhedron = getPolyhedron(); + if (polyhedron) { + PRINTD("draw() polyhedron"); + if (showfaces) polyhedron->set_style(SNC_BOUNDARY); + else polyhedron->set_style(SNC_SKELETON); + polyhedron->draw(showfaces && showedges); + } + } + PRINTD("draw() end"); } BoundingBox CGALRenderer::getBoundingBox() const { BoundingBox bbox; - if (this->polyhedron) { - CGAL::Bbox_3 cgalbbox = this->polyhedron->bbox(); - bbox = BoundingBox( - Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), - Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax()) ); - } - else if (this->polyset) { + if (this->polyset) { bbox = this->polyset->getBoundingBox(); } - + else { + shared_ptr polyhedron = getPolyhedron(); + if (polyhedron) { + CGAL::Bbox_3 cgalbbox = polyhedron->bbox(); + bbox = BoundingBox( + Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), + Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax())); + } + } return bbox; } diff --git a/src/CGALRenderer.h b/src/CGALRenderer.h index 5a5b3ac6..4bb2c673 100644 --- a/src/CGALRenderer.h +++ b/src/CGALRenderer.h @@ -1,16 +1,22 @@ #pragma once #include "renderer.h" +#include "CGAL_Nef_polyhedron.h" class CGALRenderer : public Renderer { public: CGALRenderer(shared_ptr geom); - virtual ~CGALRenderer(); + ~CGALRenderer(); virtual void draw(bool showfaces, bool showedges) const; + virtual void setColorScheme(const ColorScheme &cs); virtual BoundingBox getBoundingBox() const; -public: - shared_ptr polyhedron; +private: + shared_ptr getPolyhedron() const; + void buildPolyhedron() const; + + mutable shared_ptr polyhedron; + shared_ptr N; shared_ptr polyset; }; diff --git a/src/CGAL_renderer.h b/src/CGAL_renderer.h index c66337e3..433eb7d6 100644 --- a/src/CGAL_renderer.h +++ b/src/CGAL_renderer.h @@ -27,14 +27,11 @@ #pragma once #ifndef NULLGL -#include "OGL_helper.h" -#undef CGAL_NEF3_MARKED_VERTEX_COLOR -#undef CGAL_NEF3_MARKED_EDGE_COLOR -#undef CGAL_NEF3_MARKED_FACET_COLOR -#undef CGAL_NEF3_UNMARKED_VERTEX_COLOR -#undef CGAL_NEF3_UNMARKED_EDGE_COLOR -#undef CGAL_NEF3_UNMARKED_FACET_COLOR +#include "colormap.h" +#include "rendersettings.h" +#include "OGL_helper.h" +#include "printutils.h" using CGAL::OGL::SNC_BOUNDARY; using CGAL::OGL::SNC_SKELETON; @@ -53,16 +50,18 @@ public: NUM_COLORS }; - Polyhedron() { + Polyhedron(const ColorScheme &cs) { + PRINTD("Polyhedron()"); + // Set default colors. setColor(CGAL_NEF3_MARKED_VERTEX_COLOR,0xb7,0xe8,0x5c); - setColor(CGAL_NEF3_MARKED_EDGE_COLOR,0xab,0xd8,0x56); - setColor(CGAL_NEF3_MARKED_FACET_COLOR,0x9d,0xcb,0x51); setColor(CGAL_NEF3_UNMARKED_VERTEX_COLOR,0xff,0xf6,0x7c); - setColor(CGAL_NEF3_UNMARKED_EDGE_COLOR,0xff,0xec,0x5e); - setColor(CGAL_NEF3_UNMARKED_FACET_COLOR,0xf9,0xd7,0x2c); + // Face and Edge colors are taken from default colorscheme + setColorScheme(cs); + PRINTD("Polyhedron() end"); } void draw(bool showedges) const { + PRINTD("draw()"); if(this->style == SNC_BOUNDARY) { glCallList(this->object_list_+2); if(showedges) { @@ -75,27 +74,51 @@ public: glCallList(this->object_list_+1); glCallList(this->object_list_); } + PRINTD("draw() end"); } + + // overrides function in OGL_helper.h CGAL::Color getVertexColor(Vertex_iterator v) const { + PRINTD("getVertexColor"); CGAL::Color c = v->mark() ? colors[CGAL_NEF3_UNMARKED_VERTEX_COLOR] : colors[CGAL_NEF3_MARKED_VERTEX_COLOR]; return c; } + // overrides function in OGL_helper.h CGAL::Color getEdgeColor(Edge_iterator e) const { + PRINTD("getEdgeColor"); CGAL::Color c = e->mark() ? colors[CGAL_NEF3_UNMARKED_EDGE_COLOR] : colors[CGAL_NEF3_MARKED_EDGE_COLOR]; return c; } - CGAL::Color getFacetColor(Halffacet_iterator f) const { + // overrides function in OGL_helper.h + CGAL::Color getFacetColor(Halffacet_iterator f, bool is_back_facing) const { + PRINTD("getFacetColor"); CGAL::Color c = f->mark() ? colors[CGAL_NEF3_UNMARKED_FACET_COLOR] : colors[CGAL_NEF3_MARKED_FACET_COLOR]; return c; } + void setColor(Polyhedron::RenderColor color_index, const Color4f &c) { + PRINTDB("setColor %i %f %f %f",color_index%c[0]%c[1]%c[2]); + this->colors[color_index] = CGAL::Color(c[0]*255,c[1]*255,c[2]*255); + } + void setColor(Polyhedron::RenderColor color_index, unsigned char r, unsigned char g, unsigned char b) { - assert(color_index < Polyhedron::NUM_COLORS); + PRINTDB("setColor %i %i %i %i",color_index%r%g%b); this->colors[color_index] = CGAL::Color(r,g,b); } + + // set this->colors based on the given colorscheme. vertex colors + // are not set here as colorscheme doesnt yet hold vertex colors. + void setColorScheme(const ColorScheme &cs) { + PRINTD("setColorScheme"); + setColor(CGAL_NEF3_MARKED_FACET_COLOR, ColorMap::getColor(cs, CGAL_FACE_BACK_COLOR)); + setColor(CGAL_NEF3_UNMARKED_FACET_COLOR, ColorMap::getColor(cs, CGAL_FACE_FRONT_COLOR)); + setColor(CGAL_NEF3_MARKED_EDGE_COLOR, ColorMap::getColor(cs, CGAL_EDGE_BACK_COLOR)); + setColor(CGAL_NEF3_UNMARKED_EDGE_COLOR, ColorMap::getColor(cs, CGAL_EDGE_FRONT_COLOR)); + } + private: CGAL::Color colors[NUM_COLORS]; diff --git a/src/Camera.cc b/src/Camera.cc index 7f4d3c12..29ecc013 100644 --- a/src/Camera.cc +++ b/src/Camera.cc @@ -5,6 +5,7 @@ Camera::Camera(enum CameraType camtype) : type(camtype), projection(Camera::PERSPECTIVE), fov(45), height(60), viewall(false) { + PRINTD("Camera()"); if (this->type == Camera::GIMBAL) { object_trans << 0,0,0; object_rot << 35,0,25; @@ -92,6 +93,10 @@ void Camera::viewAll(const BoundingBox &bbox, float scalefactor) } break; } + PRINTDB("modified center x y z %f %f %f",center.x() % center.y() % center.z()); + PRINTDB("modified eye x y z %f %f %f",eye.x() % eye.y() % eye.z()); + PRINTDB("modified obj trans x y z %f %f %f",object_trans.x() % object_trans.y() % object_trans.z()); + PRINTDB("modified obj rot x y z %f %f %f",object_rot.x() % object_rot.y() % object_rot.z()); } void Camera::zoom(int delta) diff --git a/src/GLView.cc b/src/GLView.cc index 60589a18..a75e41fc 100644 --- a/src/GLView.cc +++ b/src/GLView.cc @@ -1,8 +1,10 @@ #include "GLView.h" #include "stdio.h" +#include "colormap.h" #include "rendersettings.h" #include "mathc99.h" +#include "printutils.h" #include "renderer.h" #ifdef _WIN32 @@ -22,6 +24,7 @@ GLView::GLView() showaxes = false; showcrosshairs = false; renderer = NULL; + colorscheme = &ColorMap::inst()->defaultColorScheme(); cam = Camera(); far_far_away = RenderSettings::inst()->far_gl_clip_limit; #ifdef ENABLE_OPENCSG @@ -39,6 +42,32 @@ void GLView::setRenderer(Renderer* r) renderer = r; } +/* update the color schemes of the Renderer attached to this GLView +to match the colorscheme of this GLView.*/ +void GLView::updateColorScheme() +{ + if (this->renderer) this->renderer->setColorScheme(*this->colorscheme); +} + +/* change this GLView's colorscheme to the one given, and update the +Renderer attached to this GLView as well. */ +void GLView::setColorScheme(const ColorScheme &cs) +{ + this->colorscheme = &cs; + this->updateColorScheme(); +} + +void GLView::setColorScheme(const std::string &cs) +{ + const ColorScheme *colorscheme = ColorMap::inst()->findColorScheme(cs); + if (colorscheme) { + setColorScheme(*colorscheme); + } + else { + PRINTB("WARNING: GLView: unknown colorscheme %s", cs); + } +} + void GLView::resizeGL(int w, int h) { #ifdef ENABLE_OPENCSG @@ -125,7 +154,7 @@ void GLView::paintGL() setupCamera(); - Color4f bgcol = RenderSettings::inst()->color(RenderSettings::BACKGROUND_COLOR); + Color4f bgcol = ColorMap::getColor(*this->colorscheme, BACKGROUND_COLOR); glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -440,7 +469,7 @@ void GLView::showCrosshairs() // FIXME: Crosshairs and axes are lighted, this doesn't make sense and causes them // to change color based on view orientation. glLineWidth(3); - Color4f col = RenderSettings::inst()->color(RenderSettings::CROSSHAIR_COLOR); + Color4f col = ColorMap::getColor(*this->colorscheme, CROSSHAIR_COLOR); glColor3f(col[0], col[1], col[2]); glBegin(GL_LINES); for (double xf = -1; xf <= +1; xf += 2) diff --git a/src/GLView.h b/src/GLView.h index 83559aff..f3f98ae2 100644 --- a/src/GLView.h +++ b/src/GLView.h @@ -26,6 +26,7 @@ Some actions (showCrossHairs) only work properly on Gimbal Camera. #include "system-gl.h" #include #include "Camera.h" +#include "colormap.h" class GLView { @@ -45,11 +46,16 @@ public: void showAxes(); void showSmallaxes(); + void setColorScheme(const ColorScheme &cs); + void setColorScheme(const std::string &cs); + void updateColorScheme(); + virtual bool save(const char *filename) = 0; virtual std::string getRendererInfo() const = 0; virtual float getDPI() { return 1.0f; } Renderer *renderer; + const ColorScheme *colorscheme; Camera cam; double far_far_away; size_t width; diff --git a/src/MainWindow.h b/src/MainWindow.h index af347b1f..be4e338b 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -79,6 +79,7 @@ private slots: void updateUndockMode(bool undockMode); void setFileName(const QString &filename); void setFont(const QString &family, uint size); + void setColorScheme(const QString &cs); void showProgress(); void openCSGSettingsChanged(); diff --git a/src/OGL_helper.h b/src/OGL_helper.h index 1c7ba2c1..488ee133 100644 --- a/src/OGL_helper.h +++ b/src/OGL_helper.h @@ -17,6 +17,8 @@ // // Author(s) : Peter Hachenberger +// Modified for OpenSCAD + #pragma once #include @@ -25,16 +27,18 @@ #include "system-gl.h" #include -#define CGAL_NEF3_MARKED_VERTEX_COLOR 183,232,92 -#define CGAL_NEF3_MARKED_EDGE_COLOR 171,216,86 -#define CGAL_NEF3_MARKED_FACET_COLOR 157,203,81 -#define CGAL_NEF3_MARKED_BACK_FACET_COLOR 157,103,181 - -#define CGAL_NEF3_UNMARKED_VERTEX_COLOR 255,246,124 -#define CGAL_NEF3_UNMARKED_EDGE_COLOR 255,236,94 -#define CGAL_NEF3_UNMARKED_FACET_COLOR 249,215,44 -#define CGAL_NEF3_UNMARKED_BACK_FACET_COLOR 249,115,144 +// Overridden in CGAL_renderer +/* +#define CGAL_NEF3_OGL_MARKED_VERTEX_COLOR 183,232,92 +#define CGAL_NEF3_OGL_MARKED_EDGE_COLOR 171,216,86 +#define CGAL_NEF3_OGL_MARKED_FACET_COLOR 157,203,81 +#define CGAL_NEF3_OGL_MARKED_BACK_FACET_COLOR 157,103,181 +#define CGAL_NEF3_OGL_UNMARKED_VERTEX_COLOR 255,246,124 +#define CGAL_NEF3_OGL_UNMARKED_EDGE_COLOR 255,236,94 +#define CGAL_NEF3_OGL_UNMARKED_FACET_COLOR 249,215,44 +#define CGAL_NEF3_OGL_UNMARKED_BACK_FACET_COLOR 249,115,144 +*/ const bool cull_backfaces = false; const bool color_backfaces = false; @@ -362,18 +366,24 @@ namespace OGL { Bbox_3 bbox() const { return bbox_; } Bbox_3& bbox() { return bbox_; } + // Overridden in CGAL_renderer virtual CGAL::Color getVertexColor(Vertex_iterator v) const { - CGAL::Color cf(CGAL_NEF3_MARKED_VERTEX_COLOR), - ct(CGAL_NEF3_UNMARKED_VERTEX_COLOR); // more blue-ish - CGAL::Color c = v->mark() ? ct : cf; + PRINTD("getVertexColor()"); + (void)v; +// CGAL::Color cf(CGAL_NEF3_OGL_MARKED_VERTEX_COLOR), +// ct(CGAL_NEF3_OGL_UNMARKED_VERTEX_COLOR); // more blue-ish +// CGAL::Color c = v->mark() ? ct : cf; + CGAL::Color c(0,0,200); return c; } void draw(Vertex_iterator v) const { + PRINTD("draw( Vertex_iterator )"); // CGAL_NEF_TRACEN("drawing vertex "<<*v); CGAL::Color c = getVertexColor(v); glPointSize(10); + //glPointSize(1); glColor3ub(c.red(), c.green(), c.blue()); glBegin(GL_POINTS); glVertex3d(v->x(),v->y(),v->z()); @@ -384,19 +394,26 @@ namespace OGL { glEnd(); } + // Overridden in CGAL_renderer virtual CGAL::Color getEdgeColor(Edge_iterator e) const { - CGAL::Color cf(CGAL_NEF3_MARKED_EDGE_COLOR), - ct(CGAL_NEF3_UNMARKED_EDGE_COLOR); // more blue-ish - CGAL::Color c = e->mark() ? ct : cf; + PRINTD("getEdgeColor)"); + (void)e; +// CGAL::Color cf(CGAL_NEF3_OGL_MARKED_EDGE_COLOR), +// ct(CGAL_NEF3_OGL_UNMARKED_EDGE_COLOR); // more blue-ish +// CGAL::Color c = e->mark() ? ct : cf; + // Overridden in CGAL_renderer + CGAL::Color c(200,0,0); return c; } void draw(Edge_iterator e) const { + PRINTD("draw(Edge_iterator)"); // CGAL_NEF_TRACEN("drawing edge "<<*e); Double_point p = e->source(), q = e->target(); CGAL::Color c = getEdgeColor(e); glLineWidth(5); + //glLineWidth(1); glColor3ub(c.red(),c.green(),c.blue()); glBegin(GL_LINE_STRIP); glVertex3d(p.x(), p.y(), p.z()); @@ -404,18 +421,32 @@ namespace OGL { glEnd(); } + + // Overridden in CGAL_renderer virtual CGAL::Color getFacetColor(Halffacet_iterator f, bool is_back_facing) const { + PRINTD("getFacetColor"); +/* + (void)f; +// CGAL::Color cf(CGAL_NEF3_OGL_MARKED_FACET_COLOR), +// ct(CGAL_NEF3_OGL_UNMARKED_FACET_COLOR); // more blue-ish +// CGAL::Color c = (f->mark() ? ct : cf); +*/ + CGAL::Color c(0,200,0); + return c; +/* if (is_back_facing) return !f->mark() - ? CGAL::Color(CGAL_NEF3_MARKED_BACK_FACET_COLOR) - : CGAL::Color(CGAL_NEF3_UNMARKED_BACK_FACET_COLOR); + ? CGAL::Color(CGAL_NEF3_OGL_MARKED_BACK_FACET_COLOR) + : CGAL::Color(CGAL_NEF3_OGL_UNMARKED_BACK_FACET_COLOR); else return !f->mark() - ? CGAL::Color(CGAL_NEF3_MARKED_FACET_COLOR) - : CGAL::Color(CGAL_NEF3_UNMARKED_FACET_COLOR); + ? CGAL::Color(CGAL_NEF3_OGL_MARKED_FACET_COLOR) + : CGAL::Color(CGAL_NEF3_OGL_UNMARKED_FACET_COLOR); +*/ } void draw(Halffacet_iterator f, bool is_back_facing) const { + PRINTD("draw(Halffacet_iterator)"); // CGAL_NEF_TRACEN("drawing facet "<<(f->debug(),"")); GLUtesselator* tess_ = gluNewTess(); gluTessCallback(tess_, GLenum(GLU_TESS_VERTEX_DATA), @@ -458,6 +489,7 @@ namespace OGL { void construct_axes() const { + PRINTD("construct_axes"); glLineWidth(2.0); // red x-axis glColor3f(1.0,0.0,0.0); @@ -491,6 +523,7 @@ namespace OGL { void fill_display_lists() { + PRINTD("fill_display_lists"); glNewList(object_list_, GL_COMPILE); Vertex_iterator v; for(v=vertices_.begin();v!=vertices_.end();++v) @@ -528,6 +561,7 @@ namespace OGL { } void init() { + PRINTD("init()"); if (init_) return; init_ = true; switches[SNC_AXES] = false; @@ -535,11 +569,13 @@ namespace OGL { object_list_ = glGenLists(4); CGAL_assertion(object_list_); fill_display_lists(); + PRINTD("init() end"); } void draw() const { + PRINTD("draw()"); if (!is_initialized()) const_cast(*this).init(); double l = (std::max)( (std::max)( bbox().xmax() - bbox().xmin(), bbox().ymax() - bbox().ymin()), @@ -562,6 +598,7 @@ namespace OGL { glCallList(object_list_+1); // edges glCallList(object_list_); // vertices if (switches[SNC_AXES]) glCallList(object_list_+3); // axis + PRINTD("draw() end"); } void debug(std::ostream& os = std::cerr) const diff --git a/src/Preferences.cc b/src/Preferences.cc index 94df31b3..a0bf5b49 100644 --- a/src/Preferences.cc +++ b/src/Preferences.cc @@ -37,6 +37,8 @@ #ifdef ENABLE_CGAL #include "CGALCache.h" #endif +#include "colormap.h" +#include "rendersettings.h" Preferences *Preferences::instance = NULL; @@ -87,7 +89,6 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->fontSize->setEditText( QString("%1").arg( savedsize ) ); // Setup default settings - this->defaultmap["3dview/colorscheme"] = this->colorSchemeChooser->currentItem()->text(); this->defaultmap["advanced/opencsg_show_warning"] = true; this->defaultmap["advanced/enable_opencsg_opengl1x"] = true; this->defaultmap["advanced/polysetCacheSize"] = uint(GeometryCache::instance()->maxSize()); @@ -120,38 +121,7 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) this->actionTriggered(this->prefsAction3DView); // 3D View pane - this->colorschemes["Cornfield"][RenderSettings::BACKGROUND_COLOR] = Color4f(0xff, 0xff, 0xe5); - this->colorschemes["Cornfield"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colorschemes["Cornfield"][RenderSettings::CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][RenderSettings::CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Cornfield"][RenderSettings::CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); - - this->colorschemes["Metallic"][RenderSettings::BACKGROUND_COLOR] = Color4f(0xaa, 0xaa, 0xff); - this->colorschemes["Metallic"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = Color4f(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = Color4f(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_FRONT_COLOR] = Color4f(0xdd, 0xdd, 0xff); - this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_BACK_COLOR] = Color4f(0xdd, 0x22, 0xdd); - this->colorschemes["Metallic"][RenderSettings::CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][RenderSettings::CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Metallic"][RenderSettings::CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); - - this->colorschemes["Sunset"][RenderSettings::BACKGROUND_COLOR] = Color4f(0xaa, 0x44, 0x44); - this->colorschemes["Sunset"][RenderSettings::OPENCSG_FACE_FRONT_COLOR] = Color4f(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][RenderSettings::OPENCSG_FACE_BACK_COLOR] = Color4f(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_FRONT_COLOR] = Color4f(0xff, 0xaa, 0xaa); - this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_BACK_COLOR] = Color4f(0x88, 0x22, 0x33); - this->colorschemes["Sunset"][RenderSettings::CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][RenderSettings::CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colorschemes["Sunset"][RenderSettings::CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); + this->defaultmap["3dview/colorscheme"] = "Cornfield"; // Advanced pane QValidator *validator = new QIntValidator(this); @@ -163,8 +133,6 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent) setupFeaturesPage(); updateGUI(); - - RenderSettings::inst()->setColors(this->colorschemes[getValue("3dview/colorscheme").toString()]); } Preferences::~Preferences() @@ -275,10 +243,7 @@ void Preferences::on_colorSchemeChooser_itemSelectionChanged() QString scheme = this->colorSchemeChooser->currentItem()->text(); QSettings settings; settings.setValue("3dview/colorscheme", scheme); - - RenderSettings::inst()->setColors(this->colorschemes[scheme]); - - emit requestRedraw(); + emit colorSchemeChanged( scheme ); } void Preferences::on_fontChooser_activated(const QString &family) diff --git a/src/Preferences.h b/src/Preferences.h index 102ddf15..4aad048d 100644 --- a/src/Preferences.h +++ b/src/Preferences.h @@ -3,9 +3,7 @@ #include #include #include "ui_Preferences.h" -#include "rendersettings.h" -#include "linalg.h" -#include +#include "colormap.h" class Preferences : public QMainWindow, public Ui::Preferences { @@ -43,6 +41,7 @@ signals: void updateMdiMode(bool mdi) const; void updateUndockMode(bool mdi) const; void fontChanged(const QString &family, uint size) const; + void colorSchemeChanged(const QString &scheme) const; void openCSGSettingsChanged() const; void syntaxHighlightChanged(const QString &s); @@ -55,7 +54,6 @@ private: void addPrefPage(QActionGroup *group, QAction *action, QWidget *widget); QSettings::SettingsMap defaultmap; - QHash > colorschemes; QHash prefPages; static Preferences *instance; diff --git a/src/Preferences.ui b/src/Preferences.ui index 2eb1b129..80cff0cf 100644 --- a/src/Preferences.ui +++ b/src/Preferences.ui @@ -44,6 +44,12 @@ + + + 0 + 0 + + false @@ -65,6 +71,26 @@ Sunset + + + Starnight + + + + + BeforeDawn + + + + + Nature + + + + + DeepOcean + + @@ -89,6 +115,9 @@ Qt::Vertical + + QSizePolicy::Expanding + 20 @@ -170,7 +199,7 @@ 0 - + 75 diff --git a/src/ThrownTogetherRenderer.cc b/src/ThrownTogetherRenderer.cc index d95e7afe..4b114041 100644 --- a/src/ThrownTogetherRenderer.cc +++ b/src/ThrownTogetherRenderer.cc @@ -27,6 +27,7 @@ #include "ThrownTogetherRenderer.h" #include "polyset.h" #include "csgterm.h" +#include "printutils.h" #include "system-gl.h" @@ -43,6 +44,7 @@ ThrownTogetherRenderer::ThrownTogetherRenderer(CSGChain *root_chain, void ThrownTogetherRenderer::draw(bool /*showfaces*/, bool showedges) const { + PRINTD("Thrown draw"); if (this->root_chain) { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -62,6 +64,7 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, bool background, bool showedges, bool fberror) const { + PRINTD("Thrown renderCSGChain"); glDepthFunc(GL_LEQUAL); boost::unordered_map,int> geomVisitMark; BOOST_FOREACH(const CSGChainObject &obj, chain->objects) { diff --git a/src/color.cc b/src/color.cc index b91fed15..390a11d4 100644 --- a/src/color.cc +++ b/src/color.cc @@ -35,18 +35,18 @@ #include #include using namespace boost::assign; // bring 'operator+=()' into scope +#include "colormap.h" class ColorModule : public AbstractModule { public: - ColorModule() { } + ColorModule() : webcolors(ColorMap::inst()->webColors()) { } virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; private: - static boost::unordered_map colormap; + const boost::unordered_map &webcolors; }; -#include "colormap.h" AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { ColorNode *node = new ColorNode(inst); @@ -72,8 +72,8 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia std::string colorname = v.toString(); boost::algorithm::to_lower(colorname); Color4f color; - if (colormap.find(colorname) != colormap.end()) { - node->color = colormap[colorname]; + if (webcolors.find(colorname) != webcolors.end()) { + node->color = webcolors.at(colorname); } else { PRINTB_NOCACHE("WARNING: Color name \"%s\" unknown. Please see", colorname); PRINT_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); diff --git a/src/colormap.h b/src/colormap.h index 7c0652ca..9f305efd 100644 --- a/src/colormap.h +++ b/src/colormap.h @@ -1,149 +1,43 @@ -boost::unordered_map ColorModule::colormap = map_list_of - ("aliceblue", Color4f(240, 248, 255)) - ("antiquewhite", Color4f(250, 235, 215)) - ("aqua", Color4f(0, 255, 255)) - ("aquamarine", Color4f(127, 255, 212)) - ("azure", Color4f(240, 255, 255)) - ("beige", Color4f(245, 245, 220)) - ("bisque", Color4f(255, 228, 196)) - ("black", Color4f(0, 0, 0)) - ("blanchedalmond", Color4f(255, 235, 205)) - ("blue", Color4f(0, 0, 255)) - ("blueviolet", Color4f(138, 43, 226)) - ("brown", Color4f(165, 42, 42)) - ("burlywood", Color4f(222, 184, 135)) - ("cadetblue", Color4f(95, 158, 160)) - ("chartreuse", Color4f(127, 255, 0)) - ("chocolate", Color4f(210, 105, 30)) - ("coral", Color4f(255, 127, 80)) - ("cornflowerblue", Color4f(100, 149, 237)) - ("cornsilk", Color4f(255, 248, 220)) - ("crimson", Color4f(220, 20, 60)) - ("cyan", Color4f(0, 255, 255)) - ("darkblue", Color4f(0, 0, 139)) - ("darkcyan", Color4f(0, 139, 139)) - ("darkgoldenrod", Color4f(184, 134, 11)) - ("darkgray", Color4f(169, 169, 169)) - ("darkgreen", Color4f(0, 100, 0)) - ("darkgrey", Color4f(169, 169, 169)) - ("darkkhaki", Color4f(189, 183, 107)) - ("darkmagenta", Color4f(139, 0, 139)) - ("darkolivegreen", Color4f(85, 107, 47)) - ("darkorange", Color4f(255, 140, 0)) - ("darkorchid", Color4f(153, 50, 204)) - ("darkred", Color4f(139, 0, 0)) - ("darksalmon", Color4f(233, 150, 122)) - ("darkseagreen", Color4f(143, 188, 143)) - ("darkslateblue", Color4f(72, 61, 139)) - ("darkslategray", Color4f(47, 79, 79)) - ("darkslategrey", Color4f(47, 79, 79)) - ("darkturquoise", Color4f(0, 206, 209)) - ("darkviolet", Color4f(148, 0, 211)) - ("deeppink", Color4f(255, 20, 147)) - ("deepskyblue", Color4f(0, 191, 255)) - ("dimgray", Color4f(105, 105, 105)) - ("dimgrey", Color4f(105, 105, 105)) - ("dodgerblue", Color4f(30, 144, 255)) - ("firebrick", Color4f(178, 34, 34)) - ("floralwhite", Color4f(255, 250, 240)) - ("forestgreen", Color4f(34, 139, 34)) - ("fuchsia", Color4f(255, 0, 255)) - ("gainsboro", Color4f(220, 220, 220)) - ("ghostwhite", Color4f(248, 248, 255)) - ("gold", Color4f(255, 215, 0)) - ("goldenrod", Color4f(218, 165, 32)) - ("gray", Color4f(128, 128, 128)) - ("green", Color4f(0, 128, 0)) - ("greenyellow", Color4f(173, 255, 47)) - ("grey", Color4f(128, 128, 128)) - ("honeydew", Color4f(240, 255, 240)) - ("hotpink", Color4f(255, 105, 180)) - ("indianred", Color4f(205, 92, 92)) - ("indigo", Color4f(75, 0, 130)) - ("ivory", Color4f(255, 255, 240)) - ("khaki", Color4f(240, 230, 140)) - ("lavender", Color4f(230, 230, 250)) - ("lavenderblush", Color4f(255, 240, 245)) - ("lawngreen", Color4f(124, 252, 0)) - ("lemonchiffon", Color4f(255, 250, 205)) - ("lightblue", Color4f(173, 216, 230)) - ("lightcoral", Color4f(240, 128, 128)) - ("lightcyan", Color4f(224, 255, 255)) - ("lightgoldenrodyellow", Color4f(250, 250, 210)) - ("lightgray", Color4f(211, 211, 211)) - ("lightgreen", Color4f(144, 238, 144)) - ("lightgrey", Color4f(211, 211, 211)) - ("lightpink", Color4f(255, 182, 193)) - ("lightsalmon", Color4f(255, 160, 122)) - ("lightseagreen", Color4f(32, 178, 170)) - ("lightskyblue", Color4f(135, 206, 250)) - ("lightslategray", Color4f(119, 136, 153)) - ("lightslategrey", Color4f(119, 136, 153)) - ("lightsteelblue", Color4f(176, 196, 222)) - ("lightyellow", Color4f(255, 255, 224)) - ("lime", Color4f(0, 255, 0)) - ("limegreen", Color4f(50, 205, 50)) - ("linen", Color4f(250, 240, 230)) - ("magenta", Color4f(255, 0, 255)) - ("maroon", Color4f(128, 0, 0)) - ("mediumaquamarine", Color4f(102, 205, 170)) - ("mediumblue", Color4f(0, 0, 205)) - ("mediumorchid", Color4f(186, 85, 211)) - ("mediumpurple", Color4f(147, 112, 219)) - ("mediumseagreen", Color4f(60, 179, 113)) - ("mediumslateblue", Color4f(123, 104, 238)) - ("mediumspringgreen", Color4f(0, 250, 154)) - ("mediumturquoise", Color4f(72, 209, 204)) - ("mediumvioletred", Color4f(199, 21, 133)) - ("midnightblue", Color4f(25, 25, 112)) - ("mintcream", Color4f(245, 255, 250)) - ("mistyrose", Color4f(255, 228, 225)) - ("moccasin", Color4f(255, 228, 181)) - ("navajowhite", Color4f(255, 222, 173)) - ("navy", Color4f(0, 0, 128)) - ("oldlace", Color4f(253, 245, 230)) - ("olive", Color4f(128, 128, 0)) - ("olivedrab", Color4f(107, 142, 35)) - ("orange", Color4f(255, 165, 0)) - ("orangered", Color4f(255, 69, 0)) - ("orchid", Color4f(218, 112, 214)) - ("palegoldenrod", Color4f(238, 232, 170)) - ("palegreen", Color4f(152, 251, 152)) - ("paleturquoise", Color4f(175, 238, 238)) - ("palevioletred", Color4f(219, 112, 147)) - ("papayawhip", Color4f(255, 239, 213)) - ("peachpuff", Color4f(255, 218, 185)) - ("peru", Color4f(205, 133, 63)) - ("pink", Color4f(255, 192, 203)) - ("plum", Color4f(221, 160, 221)) - ("powderblue", Color4f(176, 224, 230)) - ("purple", Color4f(128, 0, 128)) - ("red", Color4f(255, 0, 0)) - ("rosybrown", Color4f(188, 143, 143)) - ("royalblue", Color4f(65, 105, 225)) - ("saddlebrown", Color4f(139, 69, 19)) - ("salmon", Color4f(250, 128, 114)) - ("sandybrown", Color4f(244, 164, 96)) - ("seagreen", Color4f(46, 139, 87)) - ("seashell", Color4f(255, 245, 238)) - ("sienna", Color4f(160, 82, 45)) - ("silver", Color4f(192, 192, 192)) - ("skyblue", Color4f(135, 206, 235)) - ("slateblue", Color4f(106, 90, 205)) - ("slategray", Color4f(112, 128, 144)) - ("slategrey", Color4f(112, 128, 144)) - ("snow", Color4f(255, 250, 250)) - ("springgreen", Color4f(0, 255, 127)) - ("steelblue", Color4f(70, 130, 180)) - ("tan", Color4f(210, 180, 140)) - ("teal", Color4f(0, 128, 128)) - ("thistle", Color4f(216, 191, 216)) - ("tomato", Color4f(255, 99, 71)) - ("transparent", Color4f(0, 0, 0, 0)) - ("turquoise", Color4f(64, 224, 208)) - ("violet", Color4f(238, 130, 238)) - ("wheat", Color4f(245, 222, 179)) - ("white", Color4f(255, 255, 255)) - ("whitesmoke", Color4f(245, 245, 245)) - ("yellow", Color4f(255, 255, 0)) - ("yellowgreen", Color4f(154, 205, 50)); +#pragma once + +#include +#include +#include +#include "linalg.h" +#include + +enum RenderColor { + BACKGROUND_COLOR, + OPENCSG_FACE_FRONT_COLOR, + OPENCSG_FACE_BACK_COLOR, + CGAL_FACE_FRONT_COLOR, + CGAL_FACE_2D_COLOR, + CGAL_FACE_BACK_COLOR, + CGAL_EDGE_FRONT_COLOR, + CGAL_EDGE_BACK_COLOR, + CGAL_EDGE_2D_COLOR, + CROSSHAIR_COLOR +}; + +typedef std::map ColorScheme; + +class ColorMap +{ +public: + static ColorMap *inst(bool erase = false); + + const ColorScheme &defaultColorScheme() const; + + const boost::unordered_map &webColors() const { return webcolors; } + const ColorScheme *findColorScheme(const std::string &name) const; + std::list colorSchemeNames() const; + + static Color4f getColor(const ColorScheme &cs, const RenderColor rc); + +private: + ColorMap(); + ~ColorMap() {} + + boost::unordered_map webcolors; + boost::unordered_map colorschemes; +}; diff --git a/src/export_png.cc b/src/export_png.cc index befdda4e..fad2a42f 100644 --- a/src/export_png.cc +++ b/src/export_png.cc @@ -38,6 +38,7 @@ void export_png(const Geometry *root_geom, Camera &cam, std::ostream &output) glview->setCamera(cam); glview->setRenderer(&cgalRenderer); + glview->setColorScheme(RenderSettings::inst()->colorscheme); glview->paintGL(); glview->save(output); } @@ -85,6 +86,7 @@ void export_png_preview_common(Tree &tree, Camera &cam, std::ostream &output, Pr OpenCSG::setContext(0); OpenCSG::setOption(OpenCSG::OffscreenSetting, OpenCSG::FrameBufferObject); #endif + csgInfo.glview->setColorScheme(RenderSettings::inst()->colorscheme); csgInfo.glview->paintGL(); csgInfo.glview->save(output); } diff --git a/src/linalg.h b/src/linalg.h index d43a28a5..69fe316f 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d) using Eigen::Vector2d; diff --git a/src/mainwin.cc b/src/mainwin.cc index d166f6a3..a2e9f4ad 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -28,6 +28,7 @@ #include "ModuleCache.h" #include "MainWindow.h" #include "parsersettings.h" +#include "rendersettings.h" #include "Preferences.h" #include "printutils.h" #include "node.h" @@ -409,8 +410,13 @@ MainWindow::MainWindow(const QString &filename) this, SLOT(openCSGSettingsChanged())); connect(Preferences::inst(), SIGNAL(syntaxHighlightChanged(const QString&)), editor, SLOT(setHighlightScheme(const QString&))); + connect(Preferences::inst(), SIGNAL(colorSchemeChanged(const QString&)), + this, SLOT(setColorScheme(const QString&))); Preferences::inst()->apply(); + QString cs = Preferences::inst()->getValue("3dview/colorscheme").toString(); + this->setColorScheme(cs); + connect(this->findTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectFindType(int))); connect(this->findInputField, SIGNAL(returnPressed()), this->nextButton, SLOT(animateClick())); find_panel->installEventFilter(this); @@ -1917,7 +1923,7 @@ void MainWindow::actionExportCSG() return; } - std::ofstream fstream(csg_filename.toUtf8()); + std::ofstream fstream(csg_filename.toLocal8Bit()); if (!fstream.is_open()) { PRINTB("Can't open file \"%s\" for export", csg_filename.toLocal8Bit().constData()); } @@ -1979,6 +1985,7 @@ void MainWindow::viewModePreview() viewModeActionsUncheck(); viewActionPreview->setChecked(true); this->qglview->setRenderer(this->opencsgRenderer ? (Renderer *)this->opencsgRenderer : (Renderer *)this->thrownTogetherRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } else { viewModeThrownTogether(); @@ -1995,6 +2002,7 @@ void MainWindow::viewModeSurface() viewActionSurfaces->setChecked(true); this->qglview->setShowFaces(true); this->qglview->setRenderer(this->cgalRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } @@ -2004,6 +2012,7 @@ void MainWindow::viewModeWireframe() viewActionWireframe->setChecked(true); this->qglview->setShowFaces(false); this->qglview->setRenderer(this->cgalRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } @@ -2014,6 +2023,7 @@ void MainWindow::viewModeThrownTogether() viewModeActionsUncheck(); viewActionThrownTogether->setChecked(true); this->qglview->setRenderer(this->thrownTogetherRenderer); + this->qglview->updateColorScheme(); this->qglview->updateGL(); } @@ -2352,6 +2362,13 @@ MainWindow::preferences() Preferences::inst()->raise(); } +void MainWindow::setColorScheme(const QString &scheme) +{ + RenderSettings::inst()->colorscheme = scheme.toStdString(); + this->qglview->setColorScheme(scheme.toStdString()); + this->qglview->updateGL(); +} + void MainWindow::setFont(const QString &family, uint size) { QFont font; diff --git a/src/openscad.cc b/src/openscad.cc index 9c0fc6b6..1b860030 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -114,6 +114,7 @@ static void help(const char *progname) "%2%[ --viewall ] \\\n" "%2%[ --imgsize=width,height ] [ --projection=(o)rtho|(p)ersp] \\\n" "%2%[ --render | --preview[=throwntogether] ] \\\n" + "%2%[ --colorscheme=[Cornfield|Sunset|Metallic|Starnight|BeforeDawn|Nature|DeepOcean] ] \\\n" "%2%[ --csglimit=num ]" #ifdef ENABLE_EXPERIMENTAL " [ --enable= ]" @@ -152,7 +153,7 @@ static void info() exit(0); } -Camera get_camera( po::variables_map vm ) +Camera get_camera(po::variables_map vm) { Camera camera; @@ -623,6 +624,7 @@ int main(int argc, char **argv) ("viewall", "adjust camera to fit object") ("imgsize", po::value(), "=width,height for exporting png") ("projection", po::value(), "(o)rtho or (p)erspective when exporting png") + ("colorscheme", po::value(), "colorscheme") ("debug", po::value(), "special debug info") ("o,o", po::value(), "out-file") ("s,s", po::value(), "stl-file") @@ -723,6 +725,19 @@ int main(int argc, char **argv) } #endif + if (vm.count("colorscheme")) { + std::string colorscheme = vm["colorscheme"].as(); + if (ColorMap::inst()->findColorScheme(colorscheme)) { + RenderSettings::inst()->colorscheme = colorscheme; + } else { + PRINT("Unknown color scheme. Valid schemes:"); + BOOST_FOREACH (const std::string &name, ColorMap::inst()->colorSchemeNames()) { + PRINT(name); + } + exit(1); + } + } + currentdir = boosty::stringy(fs::current_path()); Camera camera = get_camera(vm); diff --git a/src/polyset.cc b/src/polyset.cc index 0d811e4a..222bfcb2 100644 --- a/src/polyset.cc +++ b/src/polyset.cc @@ -239,6 +239,7 @@ static void gl_draw_triangle(GLint *shaderinfo, const Vector3d &p0, const Vector void PolySet::render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) const { + PRINTD("Polyset render"); bool mirrored = m.matrix().determinant() < 0; #ifdef ENABLE_OPENCSG if (shaderinfo) { diff --git a/src/renderer.cc b/src/renderer.cc index 2f64999b..3b7a2a8a 100644 --- a/src/renderer.cc +++ b/src/renderer.cc @@ -3,47 +3,46 @@ #include "Geometry.h" #include "polyset.h" #include "Polygon2d.h" +#include "colormap.h" +#include "printutils.h" bool Renderer::getColor(Renderer::ColorMode colormode, Color4f &col) const { - switch (colormode) { - case COLORMODE_NONE: - return false; - break; - case COLORMODE_MATERIAL: - col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); - break; - case COLORMODE_CUTOUT: - col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_BACK_COLOR); - break; - case COLORMODE_HIGHLIGHT: - col.setRgb(255, 81, 81, 128); - break; - case COLORMODE_BACKGROUND: - col.setRgb(180, 180, 180, 128); - break; - case COLORMODE_MATERIAL_EDGES: - col.setRgb(255, 236, 94); - break; - case COLORMODE_CUTOUT_EDGES: - col.setRgb(171, 216, 86); - break; - case COLORMODE_HIGHLIGHT_EDGES: - col.setRgb(255, 171, 86, 128); - break; - case COLORMODE_BACKGROUND_EDGES: - col.setRgb(150, 150, 150, 128); - break; - default: - return false; - break; + if (colormode==COLORMODE_NONE) return false; + if (colormap.count(colormode) > 0) { + col = colormap.at(colormode); + return true; } - return true; + return false; +} + +Renderer::Renderer() : colorscheme(NULL) +{ + PRINTD("Renderer() start"); + // Setup default colors + // The main colors, MATERIAL and CUTOUT, come from this object's + // colorscheme. Colorschemes don't currently hold information + // for Highlight/Background colors + // but it wouldn't be too hard to make them do so. + + // MATERIAL is set by this object's colorscheme + // CUTOUT is set by this object's colorscheme + colormap[COLORMODE_HIGHLIGHT] = Color4f(255, 81, 81, 128); + colormap[COLORMODE_BACKGROUND] = Color4f(180, 180, 180, 128); + // MATERIAL_EDGES is set by this object's colorscheme + // CUTOUT_EDGES is set by this object's colorscheme + colormap[COLORMODE_HIGHLIGHT_EDGES] = Color4f(255, 171, 86, 128); + colormap[COLORMODE_BACKGROUND_EDGES] = Color4f(150, 150, 150, 128); + + setColorScheme(ColorMap::inst()->defaultColorScheme()); + PRINTD("Renderer() end"); } void Renderer::setColor(const float color[4], GLint *shaderinfo) const { - Color4f col = RenderSettings::inst()->color(RenderSettings::OPENCSG_FACE_FRONT_COLOR); + PRINTD("setColor a"); + Color4f col; + getColor(COLORMODE_MATERIAL,col); float c[4] = {color[0], color[1], color[2], color[3]}; if (c[0] < 0) c[0] = col[0]; if (c[1] < 0) c[1] = col[1]; @@ -60,6 +59,7 @@ void Renderer::setColor(const float color[4], GLint *shaderinfo) const void Renderer::setColor(ColorMode colormode, const float color[4], GLint *shaderinfo) const { + PRINTD("setColor b"); Color4f basecol; if (getColor(colormode, basecol)) { if (colormode == COLORMODE_BACKGROUND) { @@ -80,10 +80,25 @@ void Renderer::setColor(ColorMode colormode, const float color[4], GLint *shader void Renderer::setColor(ColorMode colormode, GLint *shaderinfo) const { + PRINTD("setColor c"); float c[4] = {-1,-1,-1,-1}; setColor(colormode, c, shaderinfo); } +/* fill this->colormap with matching entries from the colorscheme. note +this does not change Highlight or Background colors as they are not +represented in the colorscheme (yet). Also edgecolors are currently the +same for CGAL & OpenCSG */ +void Renderer::setColorScheme(const ColorScheme &cs) { + PRINTD("setColorScheme"); + colormap[COLORMODE_MATERIAL] = ColorMap::getColor(cs, OPENCSG_FACE_FRONT_COLOR); + colormap[COLORMODE_CUTOUT] = ColorMap::getColor(cs, OPENCSG_FACE_BACK_COLOR); + colormap[COLORMODE_MATERIAL_EDGES] = ColorMap::getColor(cs, CGAL_EDGE_FRONT_COLOR); + colormap[COLORMODE_CUTOUT_EDGES] = ColorMap::getColor(cs, CGAL_EDGE_BACK_COLOR); + colormap[COLORMODE_EMPTY_SPACE] = ColorMap::getColor(cs, BACKGROUND_COLOR); + this->colorscheme = &cs; +} + void Renderer::render_surface(shared_ptr geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo) { shared_ptr ps; diff --git a/src/renderer.h b/src/renderer.h index 992ef347..d8498607 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -3,6 +3,7 @@ #include "system-gl.h" #include "linalg.h" #include "memory.h" +#include "colormap.h" #ifdef _MSC_VER // NULL #include @@ -11,6 +12,7 @@ class Renderer { public: + Renderer(); virtual ~Renderer() {} virtual void draw(bool showfaces, bool showedges) const = 0; virtual BoundingBox getBoundingBox() const = 0; @@ -35,14 +37,20 @@ public: COLORMODE_MATERIAL_EDGES, COLORMODE_CUTOUT_EDGES, COLORMODE_HIGHLIGHT_EDGES, - COLORMODE_BACKGROUND_EDGES + COLORMODE_BACKGROUND_EDGES, + COLORMODE_EMPTY_SPACE }; virtual bool getColor(ColorMode colormode, Color4f &col) const; virtual void setColor(const float color[4], GLint *shaderinfo = NULL) const; virtual void setColor(ColorMode colormode, GLint *shaderinfo = NULL) const; virtual void setColor(ColorMode colormode, const float color[4], GLint *shaderinfo = NULL) const; + virtual void setColorScheme(const ColorScheme &cs); static void render_surface(shared_ptr geom, csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL); static void render_edges(shared_ptr geom, csgmode_e csgmode); + +protected: + std::map colormap; + const ColorScheme *colorscheme; }; diff --git a/src/rendersettings.cc b/src/rendersettings.cc index ba68dc5d..b3eb86b2 100644 --- a/src/rendersettings.cc +++ b/src/rendersettings.cc @@ -1,4 +1,6 @@ #include "rendersettings.h" +#include "colormap.h" +#include "printutils.h" RenderSettings *RenderSettings::inst(bool erase) { @@ -16,24 +18,5 @@ RenderSettings::RenderSettings() far_gl_clip_limit = 100000.0; img_width = 512; img_height = 512; - this->colors[BACKGROUND_COLOR] = Color4f(0xff, 0xff, 0xe5); - this->colors[OPENCSG_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colors[OPENCSG_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colors[CGAL_FACE_FRONT_COLOR] = Color4f(0xf9, 0xd7, 0x2c); - this->colors[CGAL_FACE_BACK_COLOR] = Color4f(0x9d, 0xcb, 0x51); - this->colors[CGAL_FACE_2D_COLOR] = Color4f(0x00, 0xbf, 0x99); - this->colors[CGAL_EDGE_FRONT_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colors[CGAL_EDGE_BACK_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colors[CGAL_EDGE_2D_COLOR] = Color4f(0xff, 0x00, 0x00); - this->colors[CROSSHAIR_COLOR] = Color4f(0x80, 0x00, 0x00); -} - -Color4f RenderSettings::color(RenderColor idx) -{ - return this->colors[idx]; -} - -void RenderSettings::setColors(const std::map &colors) -{ - this->colors = colors; + colorscheme = "Cornfield"; } diff --git a/src/rendersettings.h b/src/rendersettings.h index e0be8174..09be9ded 100644 --- a/src/rendersettings.h +++ b/src/rendersettings.h @@ -2,33 +2,17 @@ #include #include "linalg.h" +#include "colormap.h" class RenderSettings { public: static RenderSettings *inst(bool erase = false); - enum RenderColor { - BACKGROUND_COLOR, - OPENCSG_FACE_FRONT_COLOR, - OPENCSG_FACE_BACK_COLOR, - CGAL_FACE_FRONT_COLOR, - CGAL_FACE_2D_COLOR, - CGAL_FACE_BACK_COLOR, - CGAL_EDGE_FRONT_COLOR, - CGAL_EDGE_BACK_COLOR, - CGAL_EDGE_2D_COLOR, - CROSSHAIR_COLOR - }; - - void setColors(const std::map &colors); - Color4f color(RenderColor idx); - unsigned int openCSGTermLimit, img_width, img_height; double far_gl_clip_limit; + std::string colorscheme; private: RenderSettings(); ~RenderSettings() {} - - std::map colors; }; diff --git a/src/typedefs.h b/src/typedefs.h index a7516ca6..56cd5e8a 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -2,13 +2,14 @@ #include #include +#include #include class Assignment : public std::pair > { public: - Assignment(std::string name) : pair(name, boost::shared_ptr()) {} - Assignment(std::string name, boost::shared_ptr expr) : pair(name, expr) {} + Assignment(std::string name) { first = name; second = boost::shared_ptr(); } + Assignment(std::string name, boost::shared_ptr expr) { first = name; second = expr; } }; typedef std::vector AssignmentList; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 24a4e3ac..0e48957a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -622,6 +622,7 @@ set(CORE_SOURCES ../src/parsersettings.cc ../src/mathc99.cc ../src/linalg.cc + ../src/colormap.cc ../src/Camera.cc ../src/handle_dep.cc ../src/value.cc @@ -905,6 +906,18 @@ function(add_cmdline_test TESTCMD_BASENAME) if (${DISABLED} EQUAL -1) + set(EXPERIMENTAL_OPTION "") + string(REGEX MATCH "csgtexttest" match_result1 ${TEST_FULLNAME}) + string(REGEX MATCH "cgalstlsanity" match_result2 ${TEST_FULLNAME}) + string(REGEX MATCH "dumptest" match_result3 ${TEST_FULLNAME}) + if( "${match_result1}" STREQUAL "" ) + if( "${match_result2}" STREQUAL "" ) + if( "${match_result3}" STREQUAL "" ) + set(EXPERIMENTAL_OPTION "--enable=text") + endif() + endif() + endif() + # 2D tests should be viewed from the top, not an angle. set(CAMERA_OPTION "") foreach(test2d IN LISTS TESTS_2D) @@ -933,7 +946,7 @@ function(add_cmdline_test TESTCMD_BASENAME) set(FILENAME_OPTION -f ${FILE_BASENAME}) endif() - add_test(NAME ${TEST_FULLNAME} ${CONFARG} ${CONFVAL} COMMAND ${PYTHON_EXECUTABLE} ${tests_SOURCE_DIR}/test_cmdline_tool.py --comparator=${COMPARATOR} -c ${IMAGE_COMPARE_EXECUTABLE} -s ${TESTCMD_SUFFIX} ${EXTRA_OPTIONS} ${TESTNAME_OPTION} ${FILENAME_OPTION} ${TESTCMD_EXE} ${TESTCMD_SCRIPT} "${SCADFILE}" ${CAMERA_OPTION} ${TESTCMD_ARGS}) + add_test(NAME ${TEST_FULLNAME} ${CONFARG} ${CONFVAL} COMMAND ${PYTHON_EXECUTABLE} ${tests_SOURCE_DIR}/test_cmdline_tool.py --comparator=${COMPARATOR} -c ${IMAGE_COMPARE_EXECUTABLE} -s ${TESTCMD_SUFFIX} ${EXTRA_OPTIONS} ${TESTNAME_OPTION} ${FILENAME_OPTION} ${TESTCMD_EXE} ${TESTCMD_SCRIPT} "${SCADFILE}" ${CAMERA_OPTION} ${EXPERIMENTAL_OPTION} ${TESTCMD_ARGS}) set_property(TEST ${TEST_FULLNAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") endif() endforeach() @@ -1028,6 +1041,8 @@ disable_tests( offpngtest_iteration stlpngtest_fractal offpngtest_fractal + stlpngtest_logo_and_text + offpngtest_logo_and_text ) # mark which tests are 2d (regex match inside add_cmdline_test) @@ -1085,7 +1100,23 @@ set_test_config(Heavy cgalpngtest_rotate_extrude-tests cgalpngtest_linear_extrude-scale-zero-tests csgpngtest_linear_extrude-scale-zero-tests cgalpngtest_sphere-tests - csgpngtest_resize-tests) + csgpngtest_resize-tests + csgpngtest_resize-tests + stlpngtest_fence + stlpngtest_surface + stlpngtest_demo_cut + stlpngtest_search + stlpngtest_rounded_box + stlpngtest_difference + stlpngtest_translation + offpngtest_fence + offpngtest_surface + offpngtest_demo_cut + offpngtest_search + offpngtest_rounded_box + offpngtest_difference + offpngtest_translation +) # Bugs @@ -1313,11 +1344,35 @@ add_cmdline_test(openscad-cameyeortho-viewall EXE ${OPENSCAD_BINPATH} SUFFIX png FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/features/camera-tests.scad) - +# Colorscheme tests +add_cmdline_test(openscad-colorscheme-cornfield EXE ${OPENSCAD_BINPATH} + ARGS --colorscheme=Cornfield -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../examples/Basics/difference_cube.scad) +add_cmdline_test(openscad-colorscheme-metallic EXE ${OPENSCAD_BINPATH} + ARGS --colorscheme=Metallic -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../examples/Basics/difference_cube.scad) +add_cmdline_test(openscad-colorscheme-metallic-render EXE ${OPENSCAD_BINPATH} + ARGS --colorscheme=Metallic --render -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../examples/Basics/difference_cube.scad) +add_cmdline_test(openscad-colorscheme-sunset EXE ${OPENSCAD_BINPATH} + ARGS --colorscheme=Sunset -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../examples/Basics/difference_cube.scad) +add_cmdline_test(openscad-colorscheme-starnight EXE ${OPENSCAD_BINPATH} + ARGS --colorscheme=Starnight -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../examples/Basics/difference_cube.scad) +add_cmdline_test(openscad-colorscheme-monotone EXE ${OPENSCAD_BINPATH} + ARGS --colorscheme=Monotone -o + SUFFIX png + FILES ${CMAKE_SOURCE_DIR}/../examples/Basics/difference_cube.scad) #message("Available test configurations: ${TEST_CONFIGS}") #foreach(CONF ${TEST_CONFIGS}) # message("${CONF}: ${${CONF}_TEST_CONFIG}") #endforeach() -message("CPPFLAGS: ${CMAKE_CXX_FLAGS}") +message(STATUS "CPPFLAGS: ${CMAKE_CXX_FLAGS}") diff --git a/tests/regression/throwntogethertest/logo_and_text-expected.png b/tests/regression/throwntogethertest/logo_and_text-expected.png index 279f2a9e3d4778fb0af78fa66b6d8628996cf47f..cb13d2a23cb3a02cd32ca1218cf5afb81090b78c 100644 GIT binary patch literal 22349 zcmeEu=T}qB8*Ktem7+)$1Vx%OL3$0M^j@W_^p5nJfT$>4s(=)wO9>qzKmesHy|+m3 zJs}WMZr*$UiTmOADLJ#&SuTA$avr_{A09q~0XT|^k3Gpupfbzee zaCWt7000EgdZua`0zB-X`XZ%cxougN=>tb}*rYqRW<}bI{(P*wly`N)Ui#nWuKHLuV`Q zK7jbheG3G*<9qK3X5n*oz#dA#6sHt75=Y7{x=!^7YP4DhX0}B|5k+mA6CQY z%_KXSELtGtK_UnmoHb5@!Hp)W-}ZN^Chx5b2&_cu7|%rT6^# z{l^w0PGaD)KHJ^l1OICN_cEGMw?HwE1*u8Ac*m-)yqg=FCA`!+ZbeBq-VS^RAme^a zK|-j2jRwO{5PCW7<=WH_6H8}Mxx>q! zOP~I0`^OKY46e7LCw>njvt}R|Guicn-LkB$qQ9Ba!)#g&3dGhN{rpTrLIDExwMin4 zVkG=#`+{fLirC4lGyp7y`#A~J!5=|x^XRMkK=+5Q3bKz0y9`k{ml3x6wMx4T;GcDMbVV^CTdJ4KZ?$&$hh+U{Wf{+?9-O%<<9O$IZrZ%s9!S= zrporowD;|E@O92@x)GXPiPhWpEACF06G@gkXzqR2tNXPkI&G84qS@B{+jQUP674v} zi-@lp@%;w<>!Lg;<3+WWvf>0YvQziF=H9I3F}}PVvi)ZO7=6X~#qrhfr675j29xIx zj=aXIm83n;8tMHm%736*e`Csn4_%6X_E)M2D>`$uG-r~k0HEvJ2Tq_u99gj_jN!`! z=J-MQbzRdnwJ)#4PjtresgP|bh9kQN2{fUU_rw1wEr&It9)OZ$X+AR5sXcC$Td}KSW{OsdGM6_to9tr+p=EadePi@cKgBl zD{f+MJ#Hikl$9r|3CA=+2o|`ruuOyW@F(xRf^_^9O`7L#>j^UZtK97=N{hSIHSVTU zv+! zuyFL^GAbv{f-X5dtqp#Gg;KlC7xLTQ zjf61a^N!oEWARs;Y-fkUS}#u68U_nzJtMtX;#GJCz4_DtNt&?OL+_2S?~Q7eWW*Q_ zS}pM}4RQ^J+2j9YieeWF0)OQZLan9{|H_5*iH{VEH*-tAj$V!MIULg;l}bj=VL}ZLpJr2n;;pZ| zMkB9E{{BUaU}r<^f8GBaEm(>mbIkEzFI?Wez$DSPJHK(e3AgGqh;;SW{T`VR*9~k2 zb5(HROzLhnR(rMk%%}jg$hTysJ~rr5M@+ZaTDM*EXQRfaGo`c)ui7khKI4;`@J;fH z>w3YJy$MH2`A(95YaD9vFm;W-U1Ip|M$qjVSwf~gxiI*mSx6K+Lq@hEF5_QT=ivuU zbDDR{(B;aK32b#qL8TrvuX2gjAFBC&cv*t&73D!5K(w&q$GDZq^>{*iOiahC_^@7)GJpJD8pp)D5XtCt36ea}R z?9g|*%sk=8J!i!* z%HiyWYR`klxjq`x8=gDUxc;@{MmbIR|ASplPz?feWWm*C*`l!7zO0%k3u%UZ%;PMG z-4*kQLW#LC09K>odpj;ocqxfggDOIJ0gzoVR{@(|~$gT4!IESpY_Lw!untNe9d zqW~JJ$0V+*MID&7$^zc{1&w83e zwE)qP+);o7(v9V>YxeDX9-?;IGF+%I!Rx_wx2n>>$1GxA5o;y|21a?)UJ3$hd1oh;U~8>^ke8hTWN2mbr}AY4}z0uOHoe?)BRIN$Adi zze>!?kahgD8^4Qt1jEUng1ZZU=Y`IMMq5`$RGNm%kdhv_!|l(#7HBS*N;plbAA<5R z#p7Ejc*H0T-#rgZ7aE@y+kPoW-+DEZs8fj(L$rN^_EdW0PQ*+%fg{AKQP7{5kV$@@ z`t~b5a!Jc!&P=JW;5U|C%lNVYlRAe73{;-zIy~@Q*S7~0!c z3g;^)Xu8D-Fg z?FfhURTJtVY4i1PJk9=of&v8wIL5e~F0mX3Z(cJ;e!GmyT`Yx|Ioox6tG>tEn9<8z0P zcE#Mx3?QLyu4pExr2O&OSsuiGw1}X|WiZ4e@-Z&W_ZEVgNJTC)+YW~-<+<6$X0#2{fW6jK&gs!UAse`j zz0WAP?;AC9u-7+u`;zqwgHlMOQg--!WbfRLQ1_-M#ym6pt|fU*D3Z+mx!8OI&_Egm7!Seg3Rr&uymDG3%E175S@rcnVK!k0PDsaTdB7%E=Ja*>O8~ zc*;Y#`|H!ft%`zF;R#d4DUR}%hEd%LuZK|g? zAClr9*;Rf(Hrel7Scq_{G?(MAE7IcY@q6P1igf~$P)Gh`wEkC3L`g~GAA3YCe&{>Y z9KXK>KyVp8Qk};Uj%K$g$euINJAv#9BWq^U>|st1f;DDrJ_V*eeW zhB?Yk>=47VX$bNM&_gZ)mk;p!XIVKx0d5ueJVdS2bNI;HakN2_7X=YS%VjVkGBtbF zeMhfBCl$evGvweDdiGh3`RO9YL*n1>PrXQ`{La0F0@;~Kiy1g}Zz=)O%>o*ei1(x_C#E;Unc&09d3WhN(!ovl`wbfo_66C<`XoV@;BM>pY+T zTOI8X;vnuK=Bc;=YUV9VAzFx4Fx_8eZc5%4xFUnfW9ZV^-%8bR0D}BM9)JacSdOMTmSZOGv>4Y#e9d_ytTM z>0fvRXUvj;%oEl_yr7VCm4uUVngkR{qI*{d9KT?9YTbw7>aZuVkfbL;9Zf5ayty2X z7~p|hp|1BcTfRX~dsaP7k>i&tf1JJ0Hp`fTcYVv-=>l^Dck{TKd_ra$(0F5WQ- zF0Vj;5WFA=wQIrHRezn-+Wwj3&G~Hl5nS?LHT05nTRzO%^4usM`o#mzCyxCCVY|RI z#qX+wsQ_wR!DMlElUV(U)Kn+;UkmCP!SdWc32TXV|9o1K3ofO7F)owP)U*PANMxpi zkd;WA0PGn0U=w>fOH~>`$f`X3EwqBs+;C2qI{ftvrenqS;?U+R zu?B{y8-WC!&dlfzfoMJWaN{nTk#pmmDt7cVBOfk-(`UnTWrO<#7^c5W;F5>9UeSi2 zQIIFSKOHdZGLMg=4)6~rkJZwWMqsbgiOj)IHNrbsE?KumLrLmt%!3BJw&xE1_7U3p zfrEgIEtUFM={6H=q))hGx0t@%0&s1ymkUE~i55F%4Nlse0DysImTQjWzD~gBoVD_2UjtTQf$#}IhInNnF%FB5-DMxEu#$Vb7 z*yPh3ZF}>sCgZ_oyk6d0zl%gbT36tdc(?Uk#&?M$ye9TjCIJ&I(z^I8vSp@mNDFT^RA z#|s@sMR*XhqD3j|c0%8v-@#n2VFpU?Kc`ZnGFUy+Al2q%RXRFX9T6F6CB#&EV+w zkL6<2JaA41xWt+C&rSDhdNtmv-JUZSq0>^TP-oada&)D2p4S0;qOCQ&$>PbHfU3Xy zqyBA<%2o_a>_ThXmExvy5ltC4ZT{3i-s65lZfxw#nwJuKO1k~}&USqsscLZHco2r? zcp<+y0Fs4Vje7|l8N8<%FW;Ma3q%aN(aIxuIBvnr1!2FeTIxb^RT0fqi~2j$KjvKWXY5TqkD=--)cP})3(9j&WGW~uOx5_bTim0q%?U4wtIcWIBp7u zZVtv9LwKRVXj-?k!<|5qZ81-Ev0}jjji-Xy|{g3 znXEMBx$$uQ9@9rq=!%u2SBtLJWeVdX?qY7 zwjoh<1SvK`BW4gmi2TT9lhQ5?Fg7?nm}QypGjrG@SL|w6W9P2;In4kDeq2`5c9>@H z1tDlo2Xe~@ZDsJQLvhB%0gK< zhJf#6R<|r0oa%&q4AA!7%znPCkFA@1NIQ4&WB~_ty2_u8tnO?YAP-i-+55Dg3F+JX z{K*#lMDj&QWqf`2H19jJJV$ZAOv+xN)ui{Ml0n`oj{5jb#~<8SkiB*ZX0yB_G|Bb%ptjlXa_$H! zio~%}m5r+v`{X8ohCu|%ul!djIR;c3^po`d8>YtL- z=TQ6A1{Y>ta%xR$NgLVVSL;P87v25s7oUt4Twg985f=7*+1jh%+950Skr~Y_qfq$1 z3qGK+i_KwXR@xgs@lSkV4=EYnaM6jdUSDP>$nL2*73bE`-UNLANYe*Llru%p%0t!Blq{AT~F*Y zS7QB7o9}>+#qjUx_2yT0?^!IJp$B)am1S=%;Rv67#;=bi8=D{MEd)(+DdG2^`GcWS zn>N)7rdGaY%0Vuz4D&N=B4@%SQmLhqxWqe}_hVy0EH^;NQPa`++3O{b#JcL+Q{-gg z`}Puux)CxV+SJk>48@tA@7`n(->92kZO$xF#oC+csU=H;oDxHX0HV zH#-t9YoKqEar+%}`&NJHbMG9o3$pRwDz_O65sDeb1Q{k ziMkw{#hEb5Hj>Hn16*o2+8ruAZx469;H|x>YfQrQU=j)~i|8=h$ZX1=mIQqP!}(Tx z;w)ZYL-Y%#+zaxr@%7i^r(CX4E8LU&wr||ElnW5JZu;~Li=W*`km`*(m=oHbY6%DF zoy>GPZGts-y0ML!8>myu%+7sZV0Bw`7m{;#acTeVoakx#%RD@;I{94x$96NX?vQ50 zU8(?mz?9|$0HSf%Q-k#Fam0X^6=D79#woGKd8SWigBeDSDS~8Y2m?raOBA}uo-3Zn zu7(~=9w%VJdn-1mF(>rkLcwFWPSRoXGQ=8EmV5-?0{`0CPmu(bBE-Y)T>OPZ1dsz? zx$AN3dXc@|D2~#0T^hg~G|5l>kgrgVLzpv_ZfqJ>QU&M$zy}ujw#_pEY;!>%Ln{bN zWee;bDgTm!#Odn!=iL_6=x8|Wmq4QM2XUg>=bCS#)*Ru=@L|$qPYG7R#vG%y&}G;5 zxmV?Xap3D;GXYejv;)aK=F5&4lv#19jLo#Uj~B0oShsWV#CUBF-5fHRxqkACR#~o{ zzTW+>b7t(}u3>H*N+e`$ynwQiq;rC&4EL8}GH-nCWm9nY)lOzf!ElGkRnp@??BRtG zdn?|cKv(m+-*Ek(&ldQC@vMmVD2w0~ z1Dq-5F|K}hl`A<3t_zn01p@{)p}(m$*x7LYS0jJ|3MZ4;fhqV$SuB>yP^a?-Q%dz zw@%llQ=b@PWZDa4KL=$AeKrHxS6`YsR$bK*G(OmUUPZQ`48%Kbu!Y?)zPD=_gx`3% z@;CMl=A=@k7JUS{b61{4MQhUMR-Ru7N{xRT&yg)v+%3NSVC9oWd>k2>yk?<t`*5OrSu{R=ef*4HxRW27pL*Dl{nYcw?@gGI+a!@fyd3)#|m(p zt`!*JOi}BrE+}ofKe^;&?e*TJ^560~zDc#2z(e6mVw3qz_-lMndcKDh_L-+V8d=1&P3oW`h6M6}U%%d%2gAT1v0Z+xg z&h8bC^ApNb&SvZnF^n_tI8BJl0e|G#Pd0SU}DpWazr%kClfcWYR8afK%ktEE@_ReuVx+f2efk?NN^xElvk!Ukj6xKG$VW zp-_ihbHT+&LPAJhad>r?eX-@A8_m&J9!hKbV6s=C=I|u*J;R46s(_vw$Yzz0(@&Ez zLLC8kzxj)Azy{cdblXZiu3nujjN&!X#Iw0TXcS5wEBX7aca%PKW{4{=jzS2cPgnNQ zLGtCKSc5k_W&hxleXJ9@DcpLM&ba;g+KhkhMAYBE_*p`TRTMn_2ySp|apD>}Kw%sB z#;c=D=Lqe!M1cl5s=;@s>#jbjvo9f)tgLLk_DR1x>1ud+o4ht$IGu3MIvuBW>iU$S zix}9ksk1KSzZqReCVwBWnPE-kE>TEk{x;`k@umBwtM2VfL7$a2M{5X_Bvl9z?YC+0 z0wNcLj0kJocFt!XGk0^u zjoR*9>q$_n?8Q!Sc<$n6YgEQ@T!Hw6AOifXN6$Xg>MBe=evdo;7?>iebSH|rIoZ!tci%!mpi*zo3vw5)L$2&uuj=z=78c%joEDiA=>}r2rhYqM{u75DlH@T& zt@CGDhrT#s>F!+9M{l=Wmr`_H&C`d`z3XX4;(Il_x-*KZ%eoaRU6kZ5?(7#NWHi*19&^GYVPwBO&!z{l;S-99I!hmTj|FNH6p0~1B1 z^Zn#6gkc}QF27DY;opRb+;vo6buw;|J$MI5+j_V z+!fuTa($Dveb-({xY!+KleisO46m3V6fk&+P3a)&-|=-1+J8=^IsDF`icMz0x{`34 zQx8TMyuk~=ZfCXJD9?oo`{8#|dKNIaU-`Z>d6wo`5CSH7Z)~&b1L1tQ(>n}JFgey4 zeLo|G5t>gM-8)k^c=|mAjSGaR$%ZmxVq{)hO+O6O<|Db1Q^!4V+Zp@AuX|p+R=S85 z)bNTginu#7H@!P_QrwW-m(?2NQf2mb-<-J<_I>C!W$@Roah52DewJDD2LFdLr~uwC zN9_X@ z5>O~{FDTrp+Y!*iY_GvoN_Ikcv$D(oT#*I`|%gv85&&NvU(ru7INj-vwn*apv}w~p)`4jEqSP8g-o+2FZ4bu zY|kMVkp4h2znCVEC$39LU@L;Zbr3#;?xjNmlr@;((;*`Fe$7@=IeJ5bCo1~eC`s+t z1H2*6=1^8gP^9C$EtD1clOfNEM-IpMf(;jqvR@2Hc#t7J2nsYB3A_*Y-I5|)w3gMp zkPCI*yuNo$lT_ejZ8a*Pm=j!ecu&tf-YEOCIEhn^gesc~`#n9m=C1LCTSdrCDfN)D z&1ca>qd;cYEtaUwjNajWwMl1Q)(qgcY$K}1sfkkifwX%RLDw(#-Vw?Q@>J?CzcE&%V2#vq9jNB} zz{3T?I@tmyPlW&cT6lBylT*d#!kYtC`*E4l6O&*pwQld1s1Aa@@aZ`%Lssg5);gxc z&iJSQc5L!|3Tp`Hu`N~4MTKK7TtfYGo%NF(r>3%Z!v|@phXV)d(P`SbN}a;)8i8MM zcZv^Wqj^LD?zV1Gi?Zo2hoV4&{g0w;)Ce0t;1@;n>FP5G32vp78>R_u*nFxK#!|l+ z;kK;2q|(8t=;{`fMRD8w(X~)jGVbZP)4gc`0>B|ri2Mc=RkfTFSAl#xK7#X@3|f+J z9BLut&WFxbN~T^7g){4`WAjA3N7IDUvDm0)Ez-v!G3_9Yq5)4 zx{QT2mAVu6AN#-ci+3>P%}qsZUk833mI!t$v(B8|aS91TIUC4za3no_NgZ7z41kCJ z5@N|>krCSdkiqQ;GAtG7F26ajF<%Hf7R!~b-RZ5ib3&-7TxmoSR^#sls$by@+@}e> z-3=>ucogn2_73h?_)yJ!GbVMkZmQHAj>=PUqkH*#jh}6xGFp+*S1$H&-=IuEJf&pw z069Cu1K|7-Iq||pj|OMbDA~mf*KNqwF;AGe^q?{nNUPiVu1IM}GD=#Y4UIP_5R@n^ zD!J|HLB`MfWyhuxjB$NJdTvNZW(wzc28ncZ(u5e>)D0PV(Mo$6TZp zn2#Fk-wSqilH5eI-XziQ<>toVV*(`}$=$my3Ov0hBw#Epe?)E=eRK`m@FM|oqG zs%Ap7hvILRhb7=Y${3Oif|g!J^d2Vldr_p3lU+5!=5_0frMrp3 zC%AMhd7dVK*^9?8e`GUV{N4Rp6*BU^HxT{iQJ2qJsM+m|+wWKf)V2TlOleE}^Z6}M zXr{KC>!QH3V&_9k(sRreduL3@-jfXl?oXS>HY{mKH`$%;@}^2-hSpX(XzEroQD}{` ze?|96AGxIPmDT>zaO2oLf>-&QSs4U#Rx(6V@R~z<)XbhITv-QUGnBXJv2zMeAMeL6 z;h2kA$BIFLOrr8f!fC`-Xi^E2pV3?im5n7r!dJ^6*Pn78ULuswzKc&Un5FANcxE6I zgI4bNrUEa^y4dxz7c>g7+RlsN0YH)POpk!-0B}ULPk*{4q;E~){3UlM? z<3C4*kH3);(fr$f28f;Z1)yh52arAvySNPhlAzit??&M4yKWAUdJtp;BpB8#+y6-7kle; zA(cSYgTBdTTo`$8_~4ih7kcHa#cJU%9_7SsqqZ5^0d7oSyPGMV;YDk$D6JRsxrx!sV@L-E zb@$Be?EYbVhpg|mY(2@zpYRSN7VHQ-q}Mo~bk3aWHRy3rD)-#r4$Q-69~u%Ky}|Q{ zWiCX#q^kION}G?i_OXC4DW^Zcj+K3rpSIlYnwQB?n=RN90-4$$HK0u$Rw5_g)jWDr zRz@Y4?eV>OS$U%RJUm4zJS@#Od@+W+VPI*c61MN zL$Yl}5{weC_)w0=lrRS_f4~I5;$=KJvJzD|RG=~eSY9;?i>+_cqh}0+z_ow=%dLGa z`7dPALyxoyS|;9~nx8z|8U)gZnZ{;F{e< zdA+aF{Lk7)o0uk!=17ZB6<0DGVM-P$OYj`}v5+HUmg6CtEHUgsq}9u}WYCuO?AyuhcjpR?4lo^lPz}`Iy-PX8BFYoU2wLK( zeNK9|U^tlSLKy*pKHFS+FJf-p^Vk>dV~geCoZ#UKu!CLNMxRkJ#~JB0PAh~vD$DE{`R)2|9#Y(c3`e~trq-X3 zm3RoaRQoKodJnx=)SVsujExO8+Nc?=-%lu#x=&8>_x29aJy>r{H3W>losjqNvLC0C zL2HxQy0jx^zCy4AcVwj(wmbxCN}I07pbpSkMs)sc!HI)G;S$^>igjOnlBTbWOQy2K zq(I_FyNdzZOl#(1R}lxJr>3>W3;*^KvScRLBPD+es^rw02lWc1n6GabOygQ~N_Fk% z`&~?8%Znpf{VgY0p78P-6iMy`;~WdL}x6dXPqs~gnB`Y-(w8Gc@|>U z)9HL@WC?Y=rJ6-jiI!3CzN-9!J017+CdF}R@EjeN8>n?ixqVwXKEc^kO`M%~XJ73z z_sz=JjyV5%B*}`EcJDC8qw-#uRa_C-^b-sdTN)#^&-M5VjZ&hN|4HN#J4J}VzvZ3l z*xuQufyol>_ETFHpVl{zf&Xq3bJu{=KkpeB*i_oNho{%}CA5Uxv$3rAY_n(LhH(X% z_q8-L2-K&q1D+2hg?H=zLH^({V2^!oMsF)P94IBKXLsp9Go*~1>bK-Dl$xne4b8C{ zRuX!n{j`k#;^rh8^*g>q$D^(*Fyz_&YLq6#^PWaRpI4-R~{DM={|2!A1S~JMznU znh5~vfI=e0s~-EP9-q(jk(UsJ-mmz6xA)IG?McZdmmf_pwJa6s46(%q!Uj3%g$c8= zg*L~kGz_N((B?WMn=&sp!VG24y&ekS+a>4Nh5RZ!hs;Iy`%pXYO*4{V+Pl#{d* zk*++)dzp<@#)HQXfs8!WvOsy6a(J89wXJ#Qbo1o0m6* z&du0rdgj?jF4Z5Z$=-$6e+f3Wp>3r|ImrG=>sZowGY7++&u*ZTal-lfttW8M9P;eW zD@5SbEB^RP3Gr8yjqyZ#UBw}9Q9+`g#3^2rzI{b~88QmI95cN4;f5%e^-}p3)kM!& z6@|Pf+^~3(vsI#^xhxLpAM5Fur~W}Iu(6UtTfsQ3joPN_=!9A&x!~y!HD+Q`*d@CW z^}A`a(q>mB$L`mi#3wUiWhVV>Omt9FCVeVls|ELNgOHFr&}0o#DYlQ6YsOUWY%NA0VT4vBZq0aYy*Y<@{2 zzg)LIEj=OSC)(Wn7)_ZKOHSnP-EhPx(fPh$Pa&E3gp`ToC<00U6fAA2eUV7oXpuxZ z;SCP6($2&TlNKG@`ZSRf)1KyLpN~*N68A5sMEz)6`SsuRKHpHbu{Juw_EEuxr!gE?R{U8pX@w%Uqz|I zzU%zoL6jVih%O#JnSLe0at})Z$CDPpyba@-zsrP_=z73@g=E*hMEsj5$(Ier>BT_Y z4Uswv^Hx0W&L%5$`8pKb;=}%NTWDaZ-?sZr&-nef^n(J|DO`rP96s)bG4v+UYQ~Cg z_v6!RASF|45B^ezY&vVEuPx(DU3hCX5kgOgG@7d+sFX1Wfwg(oz@4W6RNMjR-+xLfdqC`18btqlQNar6pnuF zRK`~`Noj{#E1pgAPe&E*B^!iYHegjm^~A7et>&{Il9?XuGYC5#aW<1A1_DdIfu5@w z+^u#_P1{9Flz#d3@pW)DnNd$W=@)z`V<}ecIRN;jTCT^84<@#rebZhlx{_pKqOjcq z=CfXL^7Bnps?Qd>Qs9J9#W&s}B0uZf-y5C-L0esdDnGnltxgm6=XZL9bpGtDm|I`i z%Vy1#Dj%5882@@v2@`1up zLo#un4q8Q+VH-)eECNEY*^+bvA1;oSylE^&~7u6wiQ zTy9YXTRcTj%jgsAT*W=_0!m@w_QTD}tk+?t$H1f}1?$a+ZlJKx*MZ*~&&tc}AXT^) zfAvBAM}9?wm6lSId-gWqKj>1$e3TitIzUw5Ncp^`+3v%`coknVr(_ej%1_PY=b}~< z4-9UdU6s1S%QLW#S9?CyzacOh`U{3-5k$VV#C%7oLi52 zk9|S$UnWp6lRw(_a(?gqaoT_K&_6WdoZ`t$iX%f*mnhMB-=NxX!(w6Q$Qkxk@u@`8 zk(dV5J{pCGb~@@o-9AkW*HQ~EtD3*--taji-293ks=ObqBb&#mRB}^bWudT38*)ld zK%0Ks&u`geQ<#WCen4YCX((l}GcW@58TAQRzy9t9{4lKJtL`FFRCia;OzG;auvKV# zDqSMDiY(0$wCnMo|8@NP$7lc<`~4`upC}_|8_tOArj+Ptqw2FHT~ch{@S{n4b~O~5 z1`e4>9WtsZj6>#&xcQ-kMc+TV)Qwa8HgLcf$M#l{n{{df4qtzlv_fc&v4Fm-7UNU} zXAvE2HTPF!;Mu_r67uPVZ9U!gR}idd?UxrbLXREzxZBNw!D}&JSC{@;lWBd1u`*2U zb%~O3kKG}93CU9}I)YbwOel8A{)}+Qy>hH=@-Qjr1Ii8h9n09IuRPhRdy7-~h3~AY zY1nRL(awVSVSI@4^ruo_Bkix8C$#eTGyE($nU>1Khw(H87^$l(mgB1fn)?7^+S96E zWsUy^G2g)XC=2Xvq5Y4ATS)j8yC|T#(csjjhFy^)EQt3b+uPvIT|@<$GI7MPlv8c> z=L$x{Ix`POqMz{Df8+<$^MFqbU7v4H%feA>(KsD-mE7a*jBtSzn)x+4-seW#n|L_vscx3-T=T$oi!`zBFTRUw*;Jw2r; zStC+A^tpVhf%DHE9pMnO76^!$l0R3Xqu{Te_pi0Nc|S_ZXI6vff}gwcf%AbrMfoUG}AusE4V^UYByOe2X6^aKY^^Rs$_f{-CA+$^L9c@4IY?* z^tmVP?>g@A0XXITH^m_u!`Y&w0^I`Qum5Yx&nRbSYvD27W^3D^z^kT=WmHfDymK09 zKQ#Q>7=qXhd?Z3ZTd^VNN-ulrKkAzGsDht3K592{sL(w0rZa5x!o$A0K_?}W-h+=6^nVGtXesyT&L(({vV4X<5 zm)5gmi(q7nyVNTd5(d|=0G_Dq&-tMSL?$!0T@$`?QzG{}?rGefnR(~Q$P|q5#wy#N zW!eCZDP1}m+$O8X4+^H+Dt%Uq#1bWn*3zF*>2_{dCBjPO)Tv64rxAkn!Lw+(fkcOb z?(YG%F?O0R7n`8>d#eBy${D$9UPoEP7xtq5C zp6@0>i;6nUrz~ad;N;Ck?ZIG2krv6a+yQB5c$EZNF3T8cyYfbfNxoxGN9?X|@A%qS_nRy4smCM~6`B~-P{-7pKmzp1bqeu&Ndqh*y-xC!< zYodZoFFbktxvv3n`BT7%Xa-{VOs&hs@Pd8?IT4&;NlCnp;hcwfnC0u?C&ay_28BQF zMPHlkHrC7Fw9MFt>k5j+|GEfi0mUlmz%nZi-5aoj+KU`iU_$du)C7$qA(>HAx;;me zN>apEI2g+VCArI`>9hMIaxIkJ?qYp2e{+bdB&*dTfhFUSFq?15ow2kMgReEod?KVp z+K-o|OeT-S+WRQj^?JO3qddG(-G5F$On1`v%pM$=Ns*&frl+nyO5s5U14h#@MiqF<5ANJ zfMq3m|n9T%3A6)MJN zlDk;5>ZOXT5q?hADjzpKX9^^**ItJiO_L+}@5UEm7_dBh{iL zB;UKg;kaqeNQ$$ATm^eWPU|4{eHyZTz!}oT$VBpVkPLk+!rtJmLNK_fISzR_*e&+M z^lcs;<#)dS7;G!?(CEcGGy=OS6+DL}eE-O@vfX)0ADD}XegpjzfeKFH)+IXYJ1~!6 ziTL3o0>EiJ5&%EhH4rl7(|x|{$tiNbb=jIJkSvyk=0X7gAjBHSDi{p%eM z@M~9$7I}(@Z%-mW++%+@P3)?d`bq0UQeXrO(uzK%X5l3b)vYwjGIgvK(*`$qu^8pupBLY#5Pu?4yVgx>q{} zmkd~*iW5F`|ALn=-sR+KmK+4>f78p5&{5OoXB!JRIT8x61BwJ@;%Y_)4Ik82{+oFTwg@aYlF=^jQ7iJPnRT13!Iq1$hc zS&tVN_Dyi6#7>JYL!P{%iN^6i#=@5g{w=r!v5>%c_{#|~Ae82V@jrZ9Bhx|qI44}wfkq@`cHHHR$%_0bl`v_^?HA& z9CWHl%~T`SQ^=*#uu{=nQj|auzVmUe^Isf)eZL>x z>w4bzexCb%p6|YM!LvgFg`&%=iL1l*7k*z7dlBxQGZ2fT6aEEzOBo98AlwW9;h_Lk z_Cx!=4l0V{@<#yk$p=}!1gntFQu(Vm0>#8@Lt2VeM2Cl&_v=xUbJO0(g~@{p^edvK zL=r$m7pzxvWR-5N;y6{gMq`+8suf(=eAm<|s$6RRi-$3=6jFJuQyxDaZdBgTP39mB2Rc`#y&!)DP#CM=sBHPJB>8Lm_?x&4 z?w;|g^*g;cxkV~-g_=9nk^1x&IDmBT&c4K-*u5?ndlVTgwqDs?wUU>k8XUSc3AMYL zk}zFyt@WmbQQW=9G_MQEe1g7ObgF#um}#8TW_=grIHNo?SiBS|pPkPU6GkfpKRW1w zKCJENQj-d8=RVa9M>i`LMg;Zc;m0)p%c3orBR(~!Fg+Chhfo$KG6Sb#P|W1STz>l( zH1+xtHh(B^gUsd1Y)ld<&vyA&-mW(P8xdWUc))d2mBiU{(&j!=*1Dz9%$(PGIDRJD zzVhK>`XYAXv<(lUc2$~1$-@u4B>Jh>E4*S@ns7S|=oF!<QX0bWVi5*`FFNwL&K9X-OFNq+RV2; z8Et(2*vI~+mKLobO8?8+dpP0XAi;jjxOJuAjhg{f4F_%&vC2}huMx&_qjDH+1VHW) z&24R+#r}Mx$Dd_{hhn4S;B5{}wpz6(vzguFTOxm@VVC6Yb$TUmAEbZlswOcQSeh?e zg)45J7XMfK$?l!O&NQO0|Wc?PBRnbc5p{jtkr>LyNmUu_Oes=vl z1GmK&J5u&|0KIdOeiap;wm-7G%odU2!Z>I;NWq(Odduge2p2`V)rx6>JumN~p~@1E z=l%Sv1chDzR&;_vD-mC?q-GS!5-cQeEd#v}(6NApB+3tsII`zv2C8x(QU$s89e#bdzefYPBXUa5^DM=jJW zaw881UpdU!h){uJuQbOWtV#qvMY9Za+-@+0*p212K)pDkhSQJ~!#s-wzHHHJ5ucz) z*aSs|c@gMP(i;f2XD4_|qIFt!XBxyv@|U+EPIJ<%ZuvRyx~@#!1{=26g1lI=x0)i7 z2_ITZNLJ4kZPB$Y{1>%zDX|gv01jJpxu;tnbV_poF^@n~k>sw{vBTV7RLzRR8fBq4 z>D|SS_)vxkOD&P}E$%wa*@ZQgXxNcNZX>ufo-_n`Gz9h=7$d`5W{E77J@_Z^w9neY zw?q8%&m@)=Z^nNcy~QUqtX{?0pyPeA2JV-}2?ib*`E) zCr_#j@?&7$5O!YDC;A0#dH=Y>2RX7y(I-!AF9d%qIu6VXaGVc?DY3c}WP-~?>(up= zMm*|?t#`(UoBA+kNt3Eb{m)xfi-PAcMK-!!AVV|3QT;Jm2WH2*sC~MtBg>rlvwIbH zdGaR^Q17KSB6-$lOQhfRdi3iax|#8Et0YM0@K&`t8>Lk;B4va@2v* z-jmf+`b=lA1bKjD`cGy;?u<_12iE{ri4JNXNU;&%s`Eq3y`7`smxQVLK06?^2XJfk zVZE-daUg$_a3<3(=!W*bNmxl@Wylqv^PKheyQ@54b8Z=y%{tgBPH?ux6%%W=krb%3I^?k v5+iFL)=Sk+wo-_Nbz4uys^32-OgqeYl0RRAIGt(dwa}xxe@>nfY}v;n-QR$Hu@F`dwYpTWnZ;CuiGLZ-MS2_)GNTWw4nywO}O=S zj&^o*5`$_Hr9_qXdFC3wHP4(%GtX$cv z$i(KbPf47}?W7R@zds%a6kn*dEYc3|O+#Awm{c{MV8XA+`};+^w8O-lsZvpFJTS2N zvQOPz-~R@RV0sWEsv7d&S6EZF8X&b{`H%1a{E!+u@!$8x&ZTsAfbsO6f75>>EO@~O zOUDBeQ@lYX4~XqVXw3XKat7hl{_pHEU@lZ<*wc7!{X8(84|C;^3tNp)D^1oa1f05+>N8EPqW~Bs*Kp8sSqe!Ref^$6#zI`ad zgfDi=z&Xfg>q5N^|3nwvpguU82RZZdE_>6{J{m$QC8(f5;Xxs(*t81FACD9R{tLa^ za7thz_RdNS!N9GiMEm7RDqdmzSu0A^Av|jucxg^`oJJHAxb-9{p_33tnE@FXCKK`u zOR`4B_b9Xe42-B|!(A$AGoC!6ofkVm zN8(9t0#aYU_|ubO?94Ukl-tBa$3Pp#4VtI_+8Uv)Ayz}68_eO`i8&}-Ub&1_K@B9f znHSqqdE^D^apsSkdpK^ngYqYeL)uR0Vm)IHF!nmF-%JX2uQuhy-i!TN0DxLiQM^2{ zG~nB_N6yO;$1EOUPm~{9e>R>GUf{*51E+`OtXLdbAKJDQK;HiS*{iV(9P;y8y@MGe zR{kVytErjp>%P5$8^}{#ou_4aksP}8;qrkfP+qU*9H5Z-dd(li^9b4+eT~YIupQvq zHI;lAWC{`{EC;4hq5Nu@SZT?Q^5Ab5@GfliL5&B&B{ztj=BKG`}YJeo!k2X zrB=e6;@V_Hxja9Sy7nkBT;5unMI2EYNWOBwI=l#B{^~YO@hPUag+*>{^I})J z=ilv$fJ;dFpW}$|ux4^J`eW6)tL}h$G3{XR?x4pXd7yqup=w-01lPp>jX1k1S)U{p z?f%jzcuOjg+`C&RTmP9cFtMC+xf4MzK6Y@S2A+N$_;Z2E|Evdz&LlK-Gr8hnuQnxrNAeXO1V-($Pc7yA3RW6l*kSZQy6r`zTNhk_%Ntj>D{=dZZdzpm` zDiVTQ1JtB9L6!k+7u?pu4Xl3;M^E;4)pXsgR42)QR z5v}>tB;t7LfE;9)uy0bud5ILVHXx;)u~j=MH|j)xIm+k_=|>G;4mgALyh?XJCOU!a zjO2k@03)J400NizUm7eSP9+VjS(b$_3SsJEg!V}T*a^Mm%m=Z}xC3Wv$$<^ZTZfq-_j!GGDYTeG9X+dh)%Wsvid zG$d(?Rw80G8#ZL?z^b83Dm~(`lOgK51>U5gX}DtNX`?9(Vu9YIPqD$<+eIy?jWx&g zlf{lrK3q1r6?3MB%pZlWESq7iAMM|mVe2t}@iKR8SObhE;UB0jTshLt(Sf%nT>g%& zm>FJXRSw)ts_VTb&8`ZnrDKLY;)H_z9oZmTITCMnz+dU3Rc{_b(|785Ax7D!Kn%A|^6L8%=n)F~M%K*9( zyczFu0Hsz zXWYnb*(Dge4cT3>v}$S<*5eG3w~_xNyIj$Q4F9YYYw zu%J+_+JkY!^vPa+bui4V-;*hIG+y^Z`k z!v4(#*+PQC?kwH@4i&u9^`q@9ndpwfw%g9)T9fX!9X?fGAYjXMsXRaLp^sh2`B4mn zY9WW1J*eI#J{@1GRYA}&u-Zl&?ucY2XFl}+?891hL>92HuC{NP<*-iVZ1qQj=))Ps zeO!C@LGA%9XR9^y!JdQFC-&YCJ-i5}PLFy!%ZTj>C;k9};a4i>ySwnzc9QHrDa_Uh zhY$jrjRJ)(rMgaDf+?xksd4_0|c^7;UF|UgWPh&R5VI2#C z+vx_{$+4csL%jM;vF9;()-Zze{gQ9p+-h1kb-zbGs4zHtA~;7LngW<=z6wmx zttOBHB%gHn{%dvt?VR)MAkAS#^x7cNP+oOfSn?9!h5X5loLO7`#j%SFC1I5xk(Y6> zvRk)oy65%XmoiLHL04|8;9bK?H!wVXugai69dTWg3YTRzo|ZDvZmk6F)Ak;*0OuQ#;-4q z9zf=L{iAD3xz_Ma_rt=EOF!Thpjsbne(7JjId}Asg8zY(tS`>!s)@S2dQ( zON%oeQ9WJr`3(2H0Dt*~+uN8O9Na_sDe=LvV8H3^pD~u5G12FRVYBaU&wv9rc!q*l zcu1a&XmN<*7F?u+sZS@q0mm49OeV@{5*x=8`St9zxnfRj-D@^qeWN~-V+Zv#cLQZh zSoC6k#-imhTKPO+jn#Gi178A7SD2Qr5GA);-q@;p_meDUIA}c?W|%Rx9C#PQ+I+U~ z!f7>Q=df2O=0?;YW*Uzc7wPfYo`3(C`P@q2ZXTobdt1#qj41D97+&GA52_^4`5UX( zdCJS`53NnDPXJZtEGA*H*@Y*9_@~ofp~og{YYL@OB$$3ne1I=oQ$W)MaO*brp5HLg zg=gxD5(m6JX7R9#D!WMCaTcF}N5-s?2KBFsWY?&^`tXQd%; zOuJPS&1eFfFivzmlk)}4r`c;|X2kIbj3bTkpX@W&?psj#J;u@WJ+~-gkBz-13t8nD zt$#i|37c9vUfw<5>-9V(jL;2gQf?UUZV9nUD7U2km-!y!pObS>Tq!NfX{Oe3cvB(i zv=|l{;o{CBWY@)=CTNXJO=kp@g!rGAQz6e(kXXD~Cpq6+n_yy|aP%+I3*}z&Jl)lU zE4Byorl9hjT$3DK*CJebu<5i{^nD*ND0z+UJ)YtT{g~{thj~7L;6KunGxr3PDLL8o zD1?vIDI?~Tf2SUovxxL45Nt{$7qshoqcp(oMVeWiXXq7EzHJZo*!1&7PgWF-uNqL%w`rO~riHRC& zN<8y$emGwaj^XIC`=Y~7vH@I{m15EpcW@ioE3Me+GS-mp@kH(A6dPJ}#IB0Z_}f{; z$li7TJ$t>c&$z!1e>$8q5#UM>{b;Uv`>UMY9P+EO#Tskmv2Sz|UgfD$vHD&-KPJb+ zCV@Oggzqe3&fy;bD#W5j+hI6I@d_ml-CX6x&fV8()oPxCJzVj1o}q6qy>)G$ zv0Z3;_~sNt;Nxs);(2Y-;op`eelex<-N~IX&hNGGa=5;a*Zd^xVR35(qHAigokFbf zL~)0!;=2C+)c$nRq0n&g$ci=Y>@2?C)Dn4een%{6Ghh=TJBBulVecR4^ztPQy1^4pA4x}N7(Cxb0-JFv;RLW#1!xNgr%-Eu9 zbah0-pE78@jn_Cr84>PH;J{#fS(98!A}Xn*-xq>z2Jl`!&soIWct|rngtn|V%lwTp z>(eADi!PMKIvV2?tbU@;2|BhlstbSP*V@~&_!J*JMVWVH=h2o>%|D7hO^ZFJhyR%Q zW^KK=*-nV{Inn$5*w&UBL1a(?=1fo+?Wbngf*N*|8A^!x;wzqf{qul&rCy$42YzrX%e55f}@-keC@Q9mhUZMdMEFk>vW!~Nu1A(dNebYRoY%9^gQ zcTbK8d<$Fjen$k(4K&c;+R2Cd)1KimN_A_yER!&;IN}~5OGB4szLQe7% z_^NWR&f~IQ6=h5gIZov(!~lojKoW}RnmfIPEsdEpHR1{B-qRL2A;l40Z%s`r_`~ix z-sw7Nb|RBfi3(wOnE90bBwvlnY@v5J8%}-XPj|>_@IV5Y9fdC+GRrMq;&#b0ixF|h zcSue6)K7&Cub;r1^Nz1QIoq2F1l_;v8FZD`-}je}JX_@IHdi@3#u7cFZT;eN;1?YD zdu+&=1z+4U&aC=z@{Mbr$ms!|SaV)dZ`4q=9}ZXc?F8z2uAjI=p7O+cerS2bq-HZ7 z?0(D#`ieE*#l5_FYnbAQf0Kv9@NS8U1IEOnIiVkroQs8|Aa+#5ARtN*lYD;Gx%ITx zJ}FTHbKKVN`AArDw;{Ea^Z-=^FOVpL+n2I9;bCx?li)v3>^27NHSBCpL7DBGwhvhe zWLcE5;yrq42;TOCJl5}RQH%&{sK7?bnbxF^giPE+C`IH=j`6U{wxQoD;n zO#AeCKTSZvTFPp=pbS$Um;T|GQqWi;+3z?2O1AAXqE{UIRY=plYH5C--jM+1>P zDPBWp&#!e=b4H>Wr^xrE?`bSf7_UPp@I!H8@}eBHi+wjZ>eQi@q2KfY%D0Z^E5fdf zXF(r*w6IRQ>)9I3nq8}2%yGqpRmI-%4z~HAv$-w2*v0J?F!)(Q9*HL{OJVw zd(KOTvkJ^<{appU3u}tIbP>$XbB_b>5Z?mw7bT#C@5BBFUuiuw?$O`S&u)JH!B8Q} zFt}xTgXPfzlp1J7vBIK>B{7#EiaW+_Y^p{JO3*1Tt1i^~gH$K~lT@X{qmVvmT(E>N zj#Q!Y%Dj6KL$b26(Juhp48iq&)FnIT1|OQpQWt<)jCb(yA@$RF(kfK$t6c1G)Y9P& zIqKhA*xZV5_&fh0H#CKA-&R>5+;(t7=3E6???vZ0-{nR4+vK3Ys25QcBTrdN0k2;Q zAXF#qanMfqL-#$4gX{31IC!cEG$=Jazj1YRZ+{;w5vwM`Q8ttl=kJZ^A=M00lC@)# ztR}#BfK<^mjzf=k25_K4BRogroXAs{to9)=_fWtrsUXCZ&$`Y`z@ylGha2 zypH_5n8;h|KrTUnG!3*7NT^AWJMv&*p!n;tI%ZFHvCq=DtGs)sNV!-Sg{1_RiXb-bJJv`4|VA8a)a)9%em$ zP3?@Dz*53ElD8#?9@K-MCJpa>W1-hr11Yg@SDOKTmhn=0!tbvUokMEjObbL4rm=nQ zZ78!fQw90}h<`^lg6i&I;2ZIv%<9DJcy-}MAzWIc4*ALW6;bOv@p%!PO*-)zf;iln zlu8?W56yaI{o4xTglN5(0Q5fZJ%r{qAHM9QtxH#Hb4?uBo4Op6oSzV_j!O@PE|*`J zEREmY6Z`&@stV~e#{J8K{D+V9wgo@Mxt2cZq&0IYCM~dj1q&Xb9|#mJBY21T7-CmI zd7K;i39&Ne@B?sn5nQov?%=7G!Q+MMwMQL7J{TUn&l=PfOQKsJDlubpqt$S_1b^eR|W=17oM2;k$X&X z171BFOp~f>{G+dw=(~_^b9gE&Y&P=zThz3bIxf*Ybe?+vQyc$&hE|{a&gdf9n-7Jh zucXJE{n&QUR3HFbljv@a1e z#gs8W0tmf?)i|LBby7YsU6 z4NdXn3Lfu2D%qKb5ybFagTXVen~4#-%AQ5a3rc#y9%Qz_}E`m(#W~=p^rlzo^z3Bi|zNL zh*_3NOn)Ou=PA+Wtw(t_@xealgkQ&jSO^pO^w-w#+~Pau*Gq_JlxidYMMtJ9Ev{&; z>7IiK{Oj+@-D(tvh&3##%H-Q;^{2LCE=f@ZZOmPOtJT zG1UB{F&+P)uT=Mbw3K-OKgvMy#`4jJ=YJ@8my%kA>=ADsep-2SRCksyWky69oI5$*?==1DSD2aJ`or~qDNkO1k2a{V%eso|!-r+{{ z?8(ceTe!&8oSG=a8+>irQ&Aq(8}bOa;Hn?W+O0c)A8VYrs!-Fei}DvO>Lj>%q1(3D zG7Et1rw3AX%qh0B|uVbxC<0E|Ske zd&20>*zJsb2mT|SiU>>M`QvR~-$H}UA5@6X{N_*d9^|45MKCqT$Qo~_+awKA0^dto zMGca7uV<=-)=lUIPQr_0lNYMy*jK)DWW8O3^QLXIds?T=5PJPAoqlErR`_mjc`*nNZxak=xK_Z5Rp_#KZ$(!Q3VN*> ztk)qd-0e)ZI_@#yGB~Eixc*AdA7qTr9~{mWl#EkEf&TQqNLrLnf(};wZ0iCVgt(LM zKZB|vdXhvoR7gYKpX~;Kh3I1KNLgDUz8d>Mx?{xE!?+mk<4+N7F0IdU%I5KP(&>RX#0}Xse?EP(BnP2ZKEACBnG%C7Q2+%k(f+y+KJlk5>Ze%3 zsd5~9*X1sgiNZkv9u-OsbN^W1-TBM^uu1DwE;CP;s@n)s88zLc$#6TTJDJ4$Oq;{% z=Wfl>?UMh#^@<3ADqnyWSYfH&uL#Ji6BpD_me4y(8mp4rxh;+!Om2HOz+EsoM}DAw zl`%D2*R5jiqz!XAQ;l@W3(*}+An*MW{|{vdL1uR2_$mN_Lu4le1@k zoFWb72NpBMYJOM;{aqi7Ie1H^?IeditpKfARySM8B=9MAD^Qt#Ntliisrf9hBOD!Q zT^b`Ue?sPP6c+G+kU4A|;wGysX)U8>;X_MrdAzU%A)#J4i%~#+vxgb33Hp*gA{G>X68xi|x|VFnoWADXLR=uea+#*Qno1$G!*Kv?1o82f8FLiuv0kW$8&a>qNQ_ z0SRO&e~jAG6x(nwsc3)aD4LU5(u4JkH4HVzd|_X!^* zd_1MnPQt?tf#ch+>2SQz>8K~Q$Ws1PqQ-B&?d)2(@jnOVTgCQ+H@@unD5c%6pfz&q z*P7d;W?o>n?q`l2T5;+!hjfvWuJG*9d0k1bi6UxJgObX`1B4&hy#e!g-&{;nbj4S~ z&asbvYH<-^>5S(KFl=tfXx?*y%#^%dQr9t zY8R5f976#+X~1`sgV*|1nFXDfin}^)_9Og#lp?(_K`a3*jDZV|?Vo3$8vdrl%BuXl z_iYrYJX5CGu3lnjPEYFxipAO9uAe^Z3@q9yq+O7o)V%;mw%gq?69*`9wsxq#01&=N zjndFAk>sGE2be`=2sV4yTJB<_TzslCF+yN{6mw6W%lAsm^7?~EqUiI)h4{?E0pKWp|Oq2l( z!`jbIc(F*sxfX-0g{K;r{g;}!;Mk51Sns0hX!(}^JTFTrdaH_6_${*Z7x6geFJqPj z*g=}BY>Ok6ZM8&SHa2UPw`c$AyZTw^hln~13!||m9}Oren}=5sXF}ddSK!!c&Y+@E zjw@}X<*E=E_?WUb9B>2Tm9Q7n$Zl=Hn4BhkMPrBg-ksSycV=}%s`5tVLttslv7y}r zm2?WLNR}0^Pe_S}acu=D1uq!2za~sq{$N z8V9*(O>xgb#WI3n>b~jATQ(p8qaKqM$umU#-CT~YO>G6n>AGe_y4a1Y*G|Qd5Xvdts zr0r3+ra69Oh-E+0q!N(CgoJD>9Ir9B9iA(G?jij`X^u*L@8R)6xRL9?<2_MA!P`#r zFxK~?E^c==Uqn@Q@+>%`m0XZQ%AA@S)4jN}o{mg0^NqVLxuMC7fVNx7*m(97PSL+5 z3-lEcG<|)Drip!dj0RNMC~W*uzP~;8Cq;tHAE=^imR#J2?#cYNSFE~(Nt5dt38(U^ zuHPtNMR>8&}!=mv$=dFv9@8NlAAC@HV!am{Jrx2NB0~%?4 z4;}eFJITMk;H5%A=r=ng60aEZe6Vo zp0%7I_oa54DP=2Cy2s>ehbAZONsE|>IW>ftoXsR)>EtnaI-oYwv61dM^Ddywp4=C# z{Bq=qn*ENI#?D9+C_4=bOp@R{oX{AL|q>v!qG&Z?!&qod1oU2#hk9 zmhyu)`Eqo&)=WE==fl+Oe{3M3c9O$AtGQ#}uKi|*sES-V@PKT7iuL9O zACiSV7eWqk4NCgq#NGzFx7MGYuBTYwuIYUGaP1b`vHV5oFh_OUFPR!R|IvF8%MmkzUco(VLeTUk@lL0(h+Nl+*BTUP+%t6vT`{n zKr0^r>0G_f)bOwg2fiN}b)m7tRVwX|du~8z35|eO%mU{yeo7t*W8sf|)S$K<#`6B* zKG_-f>N#?%hq4)?cJ4Egw-_Z^*1Aa@T9$0Ayt$Aljw#G6WZLbdk2IJa|M8qVrKh)2 z%`mP|Za|sUfjm`lakkU8k|BwBzjMtHA%Yv%hjn>6q&HkmtDnZFuW94n<^nfAojdNQ z31eURb5=Q?raZyFwD3Q<={L+7L+)qGk>gcGS;3w#Ej(TwVmxNdjaGjNF9r>|-*zHD ze|O^a>4lckv5O&v>0BrpY|egu)}H@JTmbj<=KA#`&8~kjwcn*QxS!ZN&^XwK6NLiYlcpJ0oKvuBMaaT|dHgU+LLPpCQY&A3n3~ zFhk1RE0n#ckb9J(QkHM+HFjxh%j=n9lhoqt$2qnFTs}Mzzm7c)OF>GyKI`~g+WV|% z#0!>PO(-wU7qCuUbfv(Wqa^i}->xm!m~TFzu=lDl$v)C&Prp4n6^r^8)s^zq$|OS- zRS#;ZtN;}@0KmM*3&*M+Xr0Mz$QbSRcDyr?-W9mhDQ!S<7Zc;-p z+qR1Anheuvx{!V8u#`TR<(2gKofjbC%0ngzt=2w^&yK7COu7!`IBQ9fO z5AWdjJL#FHFm`PI5y^*DwdGWYjMr&0QgTZ=g7c9?)se`}+h<7ST{s_WE^6n29B6sw zDi&|ayC8FJNM9WOw>p&Dr%A!agYHV*Q{ak;H3uafnMNGnF&Tu^4K}u4e}{nL05`!8 zJ4Jjb2BS+=*_<2vRb)xGAX#A$Le!58kM#U8zz$G5n+*?d)ZYyTeRu7=i!nvKGH(RPjbiN=L7-2%T^2GbNWd+~euiWSK!X(Y_NQxtl; zEK_D3`5Ti>%#352mJl^A#eB)~;&zJteb5dKCNvztCmr@}?Hz!Hl<|hgxmMp-S27=n zJF1juFqd#x(5Xr->?6)DQu-~N`eux0qLp@)!azUlq5d4NqViJo;2EG_j_G}wG)Y{X zBQi~4o^n)^_0?tw{>IS*8$Xb?wQh?^jL4*G9GnUPnXzEHahV~kcSKFUg=R05ARf1X zMbUH_feX76`K-qr)E1Z6^mq)7*~YfJ|9*89Ta$xIbU`*MW`nw|C?X|L^?L17%oT#;(rRHo+_;gCl-(x{ z^u;Fb9bft{duVi_^&Tt$v$|$Lf81Q-u(Kdc@hgoRjG|U~o9<1sh7*L@bV>Uh-8Our z%esHhV~NI%owWBqM=va1C-LP{8i0OpL^Zmar~pd(kULVe`dMb;9(F&f!w99TY|?Xg zO@b4n-qe??Zm7Eo`1=`f^Ile9?a(|rhTqi-8h>3EyqKPY3OZ(__NC!z00w=^3~8(% z%C>XxqOQ4lHyyvpS@_6JX0JfV$#r+?pVq{_gQVQ$eEUxH7GL7@oz5lEhoX`ffUcwq z57M17#at(;{JrJb<$%CD$D|ezkah%iKjwrL*u5qF^=%? z`<`IbEKa5Z6Oj@C17SrKPe|rK2E9XAA&&t+mcRjFKwLLHdm#6pHPH?t5j!CPH}qKs z1t~S|=dHT09XiFV!f#V~3mz7-W)RutxllP@M}Qhk2c10E5c2h(ROC$n}k`(A+{@(3J%K;S1stW%Aeu>gdE2Ho-S1IJ< za!QQMK!4)F6G{ik+PgBz-vgk4sF}zQ=r^SZ%Nd|L_^{-%_wx;`cR}gl?VE|y)K{R!S8v?QO1|z-SK3?K zI0@V188uc(Se)G}wo2cs_SDoy z;-uSCg~u5w40pI=oz1Ze`8UQ&{0?pY1Z~~PJN-U0w8y{Pd`~)IPj2q4QR!J{NIc3D z2>E5-_Tfv)g(ub1XNt+Ca8I^Ur%zf6rHJrriXus1ioQ$*&@0j>Sv|STv81$pt+H1`trr^PPRigyTGpDT+L!ezmo$fuuoi^r(xQD8+IJN-*) zxHNC>3YR55ASFo!=o(?{`cm`m&P7kjmsBM#Bd_*xfp?U8%F!S4@<1#K_o82{pkt6z z8FE$rzBIO5(-)q|)PqVNZGDWfeIS_6P4$6o`s=TWqgfUZrM~Pi0w>g(*6ieo*kw6t zxsm!AVUFchXZs@T>;!3KyL_*GD#z3&)`DJ< z9}#q&rol7kYQ_$?ck@rB`gDZad@=Fmmjv!-z`e^jMv;4~+;4ak;Ufk;bQz$i7qSZ1 z-aL$1m`%TG&2}v^FHyN9V!g(W$L`bq6?W|xOCgsE(skj}hxAqA_imv3my&m0GspqV z89L=re8CUXny=WJJjW#7;t$wf!lAhWOBYr;qsSC>#EL)d@HN%O-S&5j$hW0!x+A~E zIN4v!KlDTmd4Qy2yAWlUek+v5vkX#G8clx~Y3vwEDDhND*b=Xk51u~5Wl znuQDGQ%zgT^aXH-pqN8MiM#>8inTwVJL+G7MH1|PttEfd342m^eJUQ68DwbP{Y$9S zDWbELXJbCnpycJ>Ve{yF!$qQq3F}pELyw}W*Qq7ki1~$R=C#b!`+kYNI7~3r3TM1K zaU?!8n2oXNnv*p}%hieqG~o;nN2#RdVO{Y_4O(ZKqmb1Hz4zHS{ z&DN%Td&>l_aa-D2K3aK_8Dq{OLc_B>!BX|Evd52EWG81~6SQtK1)H*vcrR-?UT!PX z&n@U#tRz{=?vTSIe7}&@>#6YXu0>uGijm8L4Y$W%sfyq~{D8h7$S+B%Tb{4jgvjaFL)Lhvc%}V70HN2i{F%Ou;8<9Waq2-d6gyamq%}ZO$F%w`Ew~) zmtRqxiEhVrnotIiqUMIqrGkE-70>qV;O4~yX0(;ssEK6D1~tFv8^#c5+uLf>V`Y<0hO}U#m6O_8$tKZ1gK;$W`jKfYt z{|EV6NLF+6VwQlDYivB*FfYe|@dVzNu9s>BR%o+RTyCcRNPB01kBbfA)1f_HGYiz) zC{7IenvRd4gD~eA%nS5=j9KjtFDly4vxX?Y@4V-nxh@75m1D}2x*iopBil6V z_15zb*QcPH(CEvhO=l&19J1%$*;W>f%ZkOy%yVnaMfAV6hHQ@hJo1(SosPqAV1Ap0 z%R_dG-adTK@u9j=@q;p0tK3jG!2%mmS!KU%5>-yz9;`H zF+JgV)Y~ch>hOg=I+eIQiBT-~jvN1{nETAt4*&rH6xq4VA1-gFR2($dsjrs(w^U*nFeA4v%jUq#+8;ZRh2Mtv@dxhC#2 zs%IlA02LePMDdl#UD9EMc+C8*^=HQZ_`WDMf&7IE6oPVie`4}%4&S(bL5aT8Eh_Wz zOo^bFD^+L{MpoJk4Vc0LUj7=8bj^x*TQ>>2i;CH`F?qw=nGb#c9IF3ch1?ih0^^AKx=?ci{Fa)9qzW4N9ru2e`wTj~vbUX>JNzyEND>r;`?2>;6%ig=+g}qO zZM;{j)+fNn$_rB}q*ZMrH_~+VJDZh6W^?aL{=uyF%(RC`4E{O6EPk*P?hweg3a-em zi|j>&;=3$_Av`~qEA!meG{2-T=2P8&RE3L5xg12w5!#>iy^$#|YtVRnceU5ZdFN>G z>NA2tz0F=tIurJr4gLE*gnY;1=l`ft?mrDyxr4@Ijb+9E_*l}~Hyq3ca}Wn8U-puF zh(qCLd+kT(4X&)vJl_XEsp&vRHQ_kBM*D6=NJC8|Um|CbODre3?ToXzHbboY?z4~C zjGFN`qYj=YM&@DoG;L`9{R-MAnRF^!SUNdQzYI;WH+gxBgCqPS`*>O_QiIw?&Ryk6 zT1hzq5Yt9FSAA#AwtNAav?yTh6QlG=%zk>A?!gU$Glzx|R`-8xzqK;|JD}6VzRO+s zo!XcV!AqAJj2EBdZdYGp_A*eq)R59Yf4W)SR$ZxCcP}U5{3R7OVVT%f)lGtV1GI$eP*tcjlTH-zCG7~pW33rW zP`bqYB93PL=BFG{Hai!0FyTPHOy?2nhQhfu(YJ8zJh6umym;H*{$JpT7-cWBbxuKG z`(i?#&sqrse()fbtbHlMLCxf^oGz{#iQJzpBHd;V>0|SfbaD|Fz~k+)|L<-ibGsI< z>pJBm>%?5%5Oo@4|6-?^-*kxJ!`33*qPm5n8^v*XFW%Gs4o4=-7Ppm5nLIF3Un|kS zpsef4^7OBHkL^EGI=zgI-)nC(0a&u%9c~)nJX2iO$qyz#E|lH@u6VH{kHk^HBg?_UxguZqA|qy} zJK{!g1~=2<{VWA*4vqJ=x0D!bBG+91SpT|V(324>U$4oZBtbsA%kBRMy@~R(kC5$aL%v^f+2(}rXg>qfKOY` z5f?4X(#!jmF3NkR!fr15Dag!OWtl`fgNB)Xj{YZ!>&n=sL{YU4IhAAp?8oqDY^0;sD`tQ``gR2RbYDa zQ}8|euN&QK%gO$HpScRd)ISXtvw6yifXx{vjW3uvEUvR9%kZS}jp)BpzQhG(DV=_S zel`Ck)a${>3TT9=(w+_XzqSBmR=L=acv)KhMg;9x6{Z5E3H5b_QRqnnJ z`Jwa%8h&07YIV>!Z>_-R(+OP~yAbn1!`t{x`ZwYH9Xw>tWcry8Ajg>Uugu8@ul`t^6(1gcCI?-bDWDeGFAJA`VXpqEp?dq zYsa??e|{ZR{pexLw38QLidKk@|Ko8q*UMv%IclE2=iEZgv<)-w_EwxkYk-{f&byX{ z_=c4&nDdqSQB!GBR69!oDzE5pPCLH8e>8rHqMP0QQ{j7wrkqQ(AZxa>LDq5>&_)0{Go}G8^qbi!ihZ_V^&n-*vBa$Pbq?GU5-mw}b(FLEaR z9*dH!Tyi@4OAUU0>)%W5jPaB!@@4Q#A6pKnhR_i$b@(*y++H7^k8x?uP5YeAe=)ZD z8?WUVZl(uhkB!N2R0f-m9ZJbuZoDPMbPT>WwO(O!2mkzTI_{WXx469AU+W==5W+Z8+nO`?!KV54T$6GA zXnWHI@DPpj)ST8?6*(+K4sJ3?3oa7-cv}9&=W{b1Y7LQooyH}yl&?z~rRa{DxY-zX zN0K3NH)79N%(lNY?Axz!psTQ%YxJ^kmV1HIGPHju70T;*KJ+&ucB3AY&sm6;ojiT! zg8S)YVRfgcKDvK*Gs{7*=t0>o;n!thTt4kAIPaU>O0$Ya9eKlB9qU(u-ClGeN>5#; z2+GN+vG)pCyO;!vgD8Tb%ACQA>PIJ+z9MSRAGJ7aZ?Z`lOF`986{crjx{^D?+r(Gd zLlh>z@LnH%lm}k%8WWn_0!SiN2%=1VSh!;Wy)1w`Gqn(cb@{u$B5KRPE zCz7g0c5lx~Tb8i@Ym>cyi9s>O)h@F}UZ(iUeCgvkpwK<#avLryt;DP372s0ZG1Cvo zSNwN}eOs+(Etm3nuOP_Uo;CheqbtV1R&x;hNL3m|6?L$_4*9F&GVQLrF7iVrYpv`c z+a{>}s4_+MH07q~{HDHv%G&)yRd5B)KyjODb;!RfcF)zy#A3~ zZ;-f&wWf-a=6$`#Z`v%e+~VP^ViGjHY3~UbimX+^Q^MqM7LO&NnI=!QeTg$Kt2bMl z9w%e<=5ps7?{eQ}tBj~gHkC~FO_)D=DC0cVq;{|0XqF zJ}oF@%zzq3ZnhLQ6r(3LPDdmL4Ohq7T=wKz4G*4`WBr2$vilu0eYPE!5k^HqD_0B? zuqg0i%RE62e^s``kb`QjB0CcZl7;(BI3yx#okoS>MX{cDYp$4PAO-)FVe`3d$YB-o&rz}ozlOoBlZix9=G+q06LT&&@^bG zU^%6vu9EkAgGH=};y}>($<^fg$$bhj$H^ZtTQy*(2Z~v*=jlFvB#02{x->eO1i>vd zHuj5-zVyjU8V4nVwI3GLmo^=0GuPo|VL4~kdl{9uN6(MHrr<@Nl^MDAMw&e_*P-_Y zsW9o`9GX|7?S>2hZlqot)A|uv(~kcfwF_Ie2L2+f-?y}7t5KBLiUt)V+*M0XUZ_2I zRvEQA_a6Scv5z#kYz-{dljOQmtnz-k^k;U$y{R=-Rl@Nk$$z z4gD#Hp-A2a3sseVdL03qTsa24SOxm$n(a(7dn8zA4_7OJVyn%f(j#1E?-5*T_f^9< zgAOO0)A9zGtQN_ad)$PY zx%lpR^_&n^6}xif1+TasPRfk=x3FKuMQIEc<+2*4yVI2-W>OQAERQ539f%bb{;L4!qy> z+kqe5n7b zwm$^3>l5~aslXg6ARxT0B>0Dr*D`A0|1SVF0|mYTd%{Wf}-T(mnE<+zeRqX z^ozbo^7bNQA{O5bF0CpyZHyI=&b5x4e(&pitImBqow&J>Ro{(kuF&Za1uZz|$nxr>2l!bkX1;ko@<8u}?N&t71gQU#@R_ zdBP%%3-wzp?(%RVI>8>y6Z(TF*!hyFkRU}5L*${MzXohdqvE|(nZ}=!HW2M6V*KGK zH(*dU*frtk)8R{Y{-z~Hr;Ytq4m~{>#lo&`ODJw_K(f1pQ0Z)}PHAO&V^Nq)?iC^z zKSIGn5c#nJDowv$rJ8F<(ItMm(J@ zm3#KJB*_^x;0Df}xR~UXV5{-DYT)?AeeL*mJ6(ezBT85&qBrmU-R`Y2M78BVuro$v z>u087XBxexGI43BcaWB)RmQrKkM2}EE;;|$4BKBK+q^v{eQU8w#J^ zsYyJ!;<^jPkugU+etcVxgs&R}eFL1m+J7CgZ#IC(-p<3BrTrWN?C-hRz0kNb)909| z>NIY@~6A=(pgsG+Rt z{f@?q@cFD3w2-4iEy&_aI%&hh6pKs-L;H-8oK8Im#o-Du_sr6E(dMKZ+h1oH&^YXQ zES7gN^5Z~b*!OoG&9fzLR5n9@vom*y`ri5_py)VQ~$7pQ5pb*Ie zATS`w#ZQ@y*P33iX&pph~GeN-GDCB-fU-u=Ww%?6{U zdRK7Cz~Ww=;_^pwMm>3>q}PdHw$O-pDSYer5kv}KG@2%1_hI&~dE2$IVd>cw9C?{!R=%fqD5Y&0Pol z`P@sHVpR_;xI|~i3)0qi@B?_s>nm?AAj(NNoZvqmbF$wevNg@uxD+Z{XijqHyJ@oJ zluY(@dAVCV9;ab8Pw#lxxw`;f&HdwcoBP!qC+78iV73>y4kw>%?-!oGyI;ATKc&Bbb(bY|hgu$R!N-RkwuJ zPb0rSkpcJws6M0`6m;fZK>Lr4Aa3NAmY%HjFc$<*mtG zOa+JZrDW>1rN#U==Fuq4+L+SbqLE&KoKK(bCxi%zZmkoyS#dht%lHV+g3ayc5-mhC zaR>@?FoJCYpHT3To|V05)|(IxuLVf!h-;@WvH?6BJiszNQ?~`k^|W&sYy@&)@L7wW z8_C?K53!s;+AtTv5_n6Prh1T@kQUNUqIk6z;yBBrgqMEGG2L?Euss+&7r51)ji_tU zwsyNa7V_5rvj=$Ic^8S~>q90eN_fdXjd!o!DlbY_b)x5rcCd|`hP2e_7E3v)2v+sb zvY;xiM6cTcrqE+LJ~vUL&!rHVZ~3~a#Fl7ox>Q^N1)Un@!%gr%7mqq_&(XCsx;cnb zS~W_|UztA$@qJTwrP|lu0w4|Bjuk%h)eEPEp~7%GFDcly@FMDvtJVOo%Bn}@{-m%K z?PN=qSCl|%uyfnXyJ`_ZX4NswI#C)?i^;~2_&f5*Ve4PHKV~}8JVMb;xNDmer~}Cq zVML@16W03=7mX0*Y?o|9m5FQekIRt34^tQUIevZ$;Q+aEfbfmXEuYDxxiOJm@qcls zD4ppzK2Hjq(Y}I?@^f%pv2Js&IbNj&rq&wwBKfQ07KpjgWhnN3rsgS2Jx}7Ced0xc zxEhcQPMd{3A;~^OT_XFhcL&`L1LULBINMzw{;>M3TMuvdM?{DFz2H!}^jI5Sn*E9O znWvn@5x_jIdxCBUPEG0qSFNAWLVSRnyup~c%aT}ucNhzJ)>;4k%FBz=xlt|jFi z(^{7KzYOJ~Eqe&NN%n{zGqqd@hXb?EKb4|Q1eqSm8%-R-CA?t+g8YO4f`;Z^ zK&9Is3iCmZVJS$Qdneds# zS%3(~KKc)T09aL(xXsqYFSG6%AHm5%dI>v|Ii2?u!fjr=Mif>#*y;?(!F}RiG*E5! z@GqV6GSGYT#Al6u!`{s&wK&NP(t9gW*xGz@RzPz9Ad^wQMdr$?g4t!_~J`B zxt8zluMo_(Xb2H=IS5*5;a&A)4r>SN*#36t;5W9Xcqz*0c6az>CHhwJ+`Pt1&NpcSPP-ifAhN~_Ym-IR0y~rbD`eH z1)t`Uzf;tnaO1IPX!`3z;Knq-`RYt-ASd|R><*v?VlO@Ni{mlzAA)=Kpc@A>6nQxl z0%(T_R#aw+6nJMNc%`fmHivDitH+=1!)E_>12XyVM8?XQ>52YaG0*o{C#;R5@QH+hBtCJ zE~`K%8c;jC!%U@$~28srk~ z4(f!Zq`7{vV?|BTpPL0$!;5`n7S#h}KJRLN`k|iNkJY4+=+?#{p2>BK4m)h;+(UBr z@^Dg%t1b4HeP^V6aP{eRQWyLg*Y*K=6ix&O|Pk5lAuYL zSG$}t1IqpA@OItbFpZ|~Yx|t)K#KX)DriFUd@?jIXGUJ|4?668D#5$6zZm&=Fsc}` zBZN23&%>;iiPm>O8t7-(orfF*rfP?$g9Kt!mS1(+cv61qGC4iXmalpu7qA~uw~X^0 zL0(tE1G?arrc)S37AiZH?@tK14TnFPUZ_7NZr)vhF^tG7zQn&0%C$b-%dQrYM^-`> z_hH$=gqGjE>d%)y1jn_So}t=@;S!!()GFdUVckEuJ#W8DT~>GZUinn?ILf+#V#GtK zo&UY!yqGXKBj>DWrx)A1Vv?gi;4NNmhEd)OtCG+j6Ev}$KbNBh&4x!;{GrnQ%1S9u?+;dE_D zvZ_PRWWTTp6J^89l1D0P9UVHEULu(SMP3@Qvmw~E0d>kBcdP(T=w$7>sbKK4Ez@!P;US*u zPA^uVeUs=_#rfwQ*bxW(`s6_70ICUnzE^bs*| zvs8)P2X<*4kDenZ>!8*5!udY=6#qj9%KBu-c~Y=Kr0m!qXbtZKZm#Vb z5YYb~n|`2xO@VTSYiR@GLXWN$!Vn;mCSpaDP9yYj#C8t-9Ds@?m5qK{6PdCQ_4hpO zL#!LGcL+WD@24Pc8!ijHcLh;yZdk9uqDRy+T;o>?AuU^ppp z=h&RHB;zI#`A<#8kC-EzJ6@Esgkq|8*Me~Bc}k!00XkBV>?vA!@9OBC;3Maj7~ko* zr$Ykx%jN<+?@J#W*ZUoKE}F+TOQFDazaORaRi=O9%_ykiR_w^vWM5uesr#tTcImLq zsoPQBM^}`k&RM@=Q1~~4ACZ4_nKKmv4LFUn+{CoJdL;SVL)GfUwN0jzhYZ8yk1+Wo zL=HgzW<=r;27e$wbU9DZFB!F&F%=UzL6|>1vIMqaVgU;8+Kn^q)ppOFb}#7OR>_I` z3-Ze)JU5kzDz%Ye2)s1N3C2ug&vYMv#P*S+thk7Kd&lTxu0yiPovQJlgAcI`DBq1D zvr>L3u1Z)j6Tv(nIfl32AZ#5s)X_(n*_Um#$#zL}C2=wrRhGlo!Sf`U(1_-MmDU(x zrqhoNwF+2I!r}W~EUzIpzo0MQ>LnmZ0c zid`e7mx4y=1f_zc3L7-X-(Fi2#jeUWEwg{zq-BPMxhq$~h?O2cDc5c^dc+acsBfDf zJjoe;Rfcw*S!y1CdWahQN_qh25qm)F!#D)>>il3#z(TbKcI^04jyxJvA^aBWH~7}! zZkNve3G&6V=MrZUzQoP`Ofrqi9bq}?nJ)*R5}RomVdr8xIUTQ^_2h-x<`K*aR`~X` zH;Irc_-d(fCQvE$ZKZa_b^^rWwGbgRgu39D4s#8T4?2wl9ap2$gf z&-y-RLhk2&6Gd4ooT6u~-h;P}X z!)4LpHxeuIvrF@jh7hWNswt*TT|;G1dF}kMXlf;=vp55QtvqFl_Go>OxQBeM^sZ^a zo4TJd-^{FgX70*yKX#o-gu>Y-MTph++}d|kiF2gS>+dh;XAPu+9l1j=u(pGxFjKOpx;YOtr@KQ9rCJarHL zJ!lZoLOdMjYXRbBp%^5*0Z0A=gBCLAHf$0Vf*Aq*2axSRPG^zWl}RJ-f~&GYnUjkp zCdS$sfjC?kpBGo`F2ARw`H$R( zCIduwKMI`sk>)T5mWL)LN9Xp5X#3WhENoA_s)Ard5_A*=lMF+7gZoXxp@s4$UzN*s zo&742eJ`*Ru(4Td9x=%qfKLA1Fq4GwMtm039w1im@-#LJ#&+3NYVWwsS+Y`2oqiig z=~Vq@#rxVjO2io<%F4*T8_^wxqeclRARYPQ;wRt(oK;A*Vc_>jz-aIz)y{*45X|Yd zS|1=^iqi>a2GmY)wcBxzB*0XcOTY6tfBAdozo;qzi-NWV8V3Rj0JeX*!SaVPe#qn9 zhN%Ujg()4D$reN{yJY0`{cFiC?9LwrKrD|CpkDR?W7*8m?~BU3+!$F2;UdMt5hu74mCB;PmM^%3nD^KhZ4abRWs!-{B<)T)ocp zCp7Ry^zlwA?EQ_C39JTly*kr?XVLtyxF-c;B*w=UvyDth3+q2+2Jih(#kQ?Y)6t!` z|A<~`5E$&$@29JEyKnisT4mh=(MscrfcGJ`lKYlJ3{B@Iny$WXRJ9zN<(Zd8HFZtg zexzY#^$^f!+L+m?s=C2p1Jkfn+8fX0q3OcBHY!(X>`y3GNs+U%I&f)-*@pF+q-~9b zq6l)y=)0c&gTML&IF!;3g57_mp@2TpU_6GK9$3W!eQfbg3-Xw?0VdWTT3*#chi`9r z^Xc$+9R<9bC6P;O8R|lvE`3|<*mcTICY=cQ@^I*384vZhZ3(N{(=T#Dz<=5|2M?<4 zVI7!8SoYQnTj3f|OldpOV8*tb(j8V4w;?7+{B$>Gj6gzhw>l0Mx`fc?$Wz8=d*HJ4|wF>?}g3cZu