From 5733ef76ca857f8bda1057c4126f56a6ac56e5d9 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 22 Dec 2015 14:25:46 -1000 Subject: [PATCH] Curve glyph tweaks, faster ModalWindow animation --- specter/include/Specter/FileBrowser.hpp | 45 ++++- specter/include/Specter/ModalWindow.hpp | 9 +- specter/include/Specter/ScrollView.hpp | 5 + specter/include/Specter/Table.hpp | 54 +++++- specter/include/Specter/View.hpp | 10 +- specter/lib/FileBrowser.cpp | 11 +- specter/lib/ModalWindow.cpp | 166 +++++++++++++++++- specter/lib/Table.cpp | 76 +++++++- .../resources/fonts/SpecterCurveGlyphs.ttf.gz | Bin 1141 -> 1175 bytes 9 files changed, 359 insertions(+), 17 deletions(-) diff --git a/specter/include/Specter/FileBrowser.hpp b/specter/include/Specter/FileBrowser.hpp index c565c1210..e91c83377 100644 --- a/specter/include/Specter/FileBrowser.hpp +++ b/specter/include/Specter/FileBrowser.hpp @@ -97,7 +97,50 @@ class FileBrowser : public ModalWindow } } m_fileFieldBind; - ViewChild> m_fileScroll; + struct TableDataBind : ITableDataBinding + { + std::string m_nameCol = "Name"; + std::vector m_names = {"One", "Two", "Three"}; + + std::string m_typeCol = "Type"; + std::vector m_types = {"t1", "t2", "t3"}; + + std::string m_sizeCol = "Size"; + std::vector m_sizes = {"s1", "s2", "s3"}; + + size_t columnCount() const {return 3;} + size_t rowCount() const {return 3;} + + const std::string* header(size_t cIdx) const + { + switch (cIdx) + { + case 0: + return &m_nameCol; + case 1: + return &m_typeCol; + case 2: + return &m_sizeCol; + default: break; + } + return nullptr; + } + + const std::string* cell(size_t cIdx, size_t rIdx) const + { + switch (cIdx) + { + case 0: + return &m_names.at(rIdx); + case 1: + return &m_types.at(rIdx); + case 2: + return &m_sizes.at(rIdx); + default: break; + } + return nullptr; + } + } m_fileListingBind; ViewChild> m_fileListing; public: diff --git a/specter/include/Specter/ModalWindow.hpp b/specter/include/Specter/ModalWindow.hpp index cddb582f0..8bb6f7dbc 100644 --- a/specter/include/Specter/ModalWindow.hpp +++ b/specter/include/Specter/ModalWindow.hpp @@ -8,8 +8,8 @@ namespace Specter { class ModalWindow : public View { - unsigned m_frame = 0; - unsigned m_contentStartFrame = 0; + int m_frame = 0; + int m_contentStartFrame = 0; float m_lineTime = 0.0; enum class Phase @@ -29,6 +29,7 @@ class ModalWindow : public View Zeus::CColor m_windowBgClear; Zeus::CColor m_line1; Zeus::CColor m_line2; + Zeus::CColor m_line2Clear; ViewBlock m_viewBlock; boo::IGraphicsBufferD* m_viewBlockBuf; @@ -39,7 +40,9 @@ class ModalWindow : public View } m_verts; void setLineVerts(int width, int height, float pf, float t); + void setLineVertsOut(int width, int height, float pf, float t); void setLineColors(float t); + void setLineColorsOut(float t); void setFillVerts(int width, int height, float pf); void setFillColors(float t); @@ -59,6 +62,8 @@ public: ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint); void think(); bool skipBuildInAnimation(); + void close(); + bool closed() const {return m_phase >= Phase::BuildOut;} void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); void draw(boo::IGraphicsCommandQueue* gfxQ); diff --git a/specter/include/Specter/ScrollView.hpp b/specter/include/Specter/ScrollView.hpp index 79c317ed5..6cbf7a897 100644 --- a/specter/include/Specter/ScrollView.hpp +++ b/specter/include/Specter/ScrollView.hpp @@ -20,6 +20,11 @@ private: public: ScrollView(ViewResources& res, View& parentView); + void setContentView(View* v) + { + m_contentView = v; + updateSize(); + } }; } diff --git a/specter/include/Specter/Table.hpp b/specter/include/Specter/Table.hpp index b82232108..0e4deb4f9 100644 --- a/specter/include/Specter/Table.hpp +++ b/specter/include/Specter/Table.hpp @@ -2,14 +2,66 @@ #define SPECTER_TABLE_HPP #include "View.hpp" +#include "ScrollView.hpp" namespace Specter { +#define SPECTER_TABLE_MAX_ROWS 128 + +enum class SortDirection +{ + None, + Ascending, + Descending +}; + +struct ITableDataBinding +{ + virtual size_t columnCount() const=0; + virtual size_t rowCount() const=0; + virtual const std::string* header(size_t cIdx) const {return nullptr;} + virtual const std::string* cell(size_t cIdx, size_t rIdx) const {return nullptr;} +}; + +struct ITableStateBinding +{ + virtual float columnSplit(size_t cIdx) {return -1.0;} + virtual void setColumnSplit(size_t cIdx, float split) {} + virtual SortDirection sort(size_t cIdx) {return SortDirection::None;} + virtual void setSort(size_t cIdx, SortDirection dir) {} +}; class Table : public View { + ITableDataBinding* m_data; + ITableStateBinding* m_state; + + SolidShaderVert m_verts[SPECTER_TABLE_MAX_ROWS * 6]; + boo::IGraphicsBufferD* m_vertsBuf = nullptr; + boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ + boo::IShaderDataBinding* m_shaderBinding = nullptr; + + ViewChild> m_scroll; + + struct RowsView : public View + { + Table& m_t; + RowsView(Table& t, ViewResources& res) : View(res, t), m_t(t) {} + void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); + void draw(boo::IGraphicsCommandQueue* gfxQ); + } m_rowsView; + public: - Table(ViewResources& res, View& parentView); + Table(ViewResources& res, View& parentView, ITableDataBinding* data, ITableStateBinding* state=nullptr); + + void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); + void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); + void mouseEnter(const boo::SWindowCoord&); + void mouseLeave(const boo::SWindowCoord&); + void scroll(const boo::SWindowCoord&, const boo::SScrollDelta&); + + void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); + void draw(boo::IGraphicsCommandQueue* gfxQ); }; } diff --git a/specter/include/Specter/View.hpp b/specter/include/Specter/View.hpp index 5a1c202cd..9f0e33e2a 100644 --- a/specter/include/Specter/View.hpp +++ b/specter/include/Specter/View.hpp @@ -212,7 +212,7 @@ public: template struct ViewChild { - ViewPtrType m_view; + ViewPtrType m_view = ViewPtrType(); bool m_mouseIn = false; int m_mouseDown = 0; @@ -280,6 +280,14 @@ struct ViewChild m_mouseIn = false; } } + + void scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll) + { + if (!m_view) + return; + if (m_mouseIn) + m_view->scroll(coord, scroll); + } }; } diff --git a/specter/lib/FileBrowser.cpp b/specter/lib/FileBrowser.cpp index d762ea512..82fa0ce4a 100644 --- a/specter/lib/FileBrowser.cpp +++ b/specter/lib/FileBrowser.cpp @@ -61,7 +61,7 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string m_pathButtons.emplace_back(*this, res, idx++, c); m_fileField.m_view.reset(new TextField(res, *this, &m_fileFieldBind)); - m_fileListing.m_view.reset(new Table(res, *this)); + m_fileListing.m_view.reset(new Table(res, *this, &m_fileListingBind)); m_fileListing.m_view->setBackground(Zeus::CColor::skBlue); m_split.m_view.reset(new SplitView(res, *this, SplitView::Axis::Vertical, @@ -91,6 +91,7 @@ void FileBrowser::okActivated() void FileBrowser::cancelActivated() { + close(); } void FileBrowser::pathButtonActivated(size_t idx) @@ -99,7 +100,7 @@ void FileBrowser::pathButtonActivated(size_t idx) void FileBrowser::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) { - if (skipBuildInAnimation()) + if (skipBuildInAnimation() || closed()) return; m_split.mouseDown(coord, button, mod); for (PathButton& b : m_pathButtons) @@ -112,6 +113,8 @@ void FileBrowser::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton bu void FileBrowser::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) { + if (closed()) + return; m_split.mouseUp(coord, button, mod); for (PathButton& b : m_pathButtons) b.m_button.mouseUp(coord, button, mod); @@ -123,6 +126,8 @@ void FileBrowser::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton butt void FileBrowser::mouseMove(const boo::SWindowCoord& coord) { + if (closed()) + return; m_split.mouseMove(coord); for (PathButton& b : m_pathButtons) b.m_button.mouseMove(coord); @@ -138,6 +143,8 @@ void FileBrowser::mouseEnter(const boo::SWindowCoord& coord) void FileBrowser::mouseLeave(const boo::SWindowCoord& coord) { + if (closed()) + return; m_split.mouseLeave(coord); for (PathButton& b : m_pathButtons) b.m_button.mouseLeave(coord); diff --git a/specter/lib/ModalWindow.cpp b/specter/lib/ModalWindow.cpp index f516d6cec..f2f58fe74 100644 --- a/specter/lib/ModalWindow.cpp +++ b/specter/lib/ModalWindow.cpp @@ -6,11 +6,11 @@ namespace Specter { #define WIRE_START 0 -#define WIRE_FRAMES 60 -#define SOLID_START 40 -#define SOLID_FRAMES 40 -#define CONTENT_START 80 -#define CONTENT_FRAMES 40 +#define WIRE_FRAMES 40 +#define SOLID_START 30 +#define SOLID_FRAMES 20 +#define CONTENT_START 40 +#define CONTENT_FRAMES 10 #define LINE_WIDTH 2 #define CONTENT_MARGIN 10 @@ -65,6 +65,55 @@ void ModalWindow::setLineVerts(int width, int height, float pf, float t) m_verts.lineVerts[21].m_pos = Zeus::CVector3f::lerp({lineLeft, lineBottom, 0}, {lineRight, lineBottom, 0}, t2); } +void ModalWindow::setLineVertsOut(int width, int height, float pf, float t) +{ + std::pair margin = m_cornersOutline[0]->queryGlyphDimensions(0); + float t1 = Zeus::Math::clamp(0.f, t * 2.f - 1.f, 1.f); + float t2 = Zeus::Math::clamp(0.f, t * 2.f, 1.f); + + float lineLeft = 0; + float lineRight = pf*LINE_WIDTH; + float lineTop = height-margin.second; + float lineBottom = margin.second; + m_verts.lineVerts[0].m_pos = Zeus::CVector3f::lerp({lineLeft, lineBottom, 0}, {lineLeft, lineTop, 0}, t1); + m_verts.lineVerts[1].m_pos.assign(lineLeft, lineBottom, 0); + m_verts.lineVerts[2].m_pos = Zeus::CVector3f::lerp({lineRight, lineBottom, 0}, {lineRight, lineTop, 0}, t1); + m_verts.lineVerts[3].m_pos.assign(lineRight, lineBottom, 0); + m_verts.lineVerts[4].m_pos = m_verts.lineVerts[3].m_pos; + + lineLeft = margin.first; + lineRight = width-margin.first; + lineTop = height; + lineBottom = height-pf*LINE_WIDTH; + m_verts.lineVerts[5].m_pos = Zeus::CVector3f::lerp({lineRight, lineTop, 0}, {lineLeft, lineTop, 0}, t1); + m_verts.lineVerts[6].m_pos = m_verts.lineVerts[5].m_pos; + m_verts.lineVerts[7].m_pos = Zeus::CVector3f::lerp({lineRight, lineBottom, 0}, {lineLeft, lineBottom, 0}, t1); + m_verts.lineVerts[8].m_pos.assign(lineRight, lineTop, 0); + m_verts.lineVerts[9].m_pos.assign(lineRight, lineBottom, 0); + m_verts.lineVerts[10].m_pos = m_verts.lineVerts[9].m_pos; + + lineLeft = width-pf*LINE_WIDTH; + lineRight = width; + lineTop = height-margin.second; + lineBottom = margin.second; + m_verts.lineVerts[11].m_pos = Zeus::CVector3f::lerp({lineLeft, lineBottom, 0}, {lineLeft, lineTop, 0}, t2); + m_verts.lineVerts[12].m_pos = m_verts.lineVerts[11].m_pos; + m_verts.lineVerts[13].m_pos.assign(lineLeft, lineBottom, 0); + m_verts.lineVerts[14].m_pos = Zeus::CVector3f::lerp({lineRight, lineBottom, 0}, {lineRight, lineTop, 0}, t2); + m_verts.lineVerts[15].m_pos.assign(lineRight, lineBottom, 0); + m_verts.lineVerts[16].m_pos = m_verts.lineVerts[15].m_pos; + + lineLeft = margin.first; + lineRight = width-margin.first; + lineTop = pf*LINE_WIDTH; + lineBottom = 0; + m_verts.lineVerts[17].m_pos = Zeus::CVector3f::lerp({lineRight, lineTop, 0}, {lineLeft, lineTop, 0}, t2); + m_verts.lineVerts[18].m_pos = m_verts.lineVerts[17].m_pos; + m_verts.lineVerts[19].m_pos = Zeus::CVector3f::lerp({lineRight, lineBottom, 0}, {lineLeft, lineBottom, 0}, t2); + m_verts.lineVerts[20].m_pos.assign(lineRight, lineTop, 0); + m_verts.lineVerts[21].m_pos.assign(lineRight, lineBottom, 0); +} + void ModalWindow::setLineColors(float t) { float t1 = Zeus::Math::clamp(0.f, t * 2.f, 1.f); @@ -85,6 +134,7 @@ void ModalWindow::setLineColors(float t) else if (t < 1.0) { m_cornersOutline[1]->colorGlyphs(c2); + m_cornersOutline[2]->colorGlyphs(Zeus::CColor::skClear); m_cornersOutline[3]->colorGlyphs(c2); } else @@ -121,6 +171,63 @@ void ModalWindow::setLineColors(float t) m_verts.lineVerts[21].m_color = m_verts.lineVerts[20].m_color; } +void ModalWindow::setLineColorsOut(float t) +{ + float t1 = Zeus::Math::clamp(0.f, t * 2.f, 1.f); + float t2 = Zeus::Math::clamp(0.f, t * 2.f - 1.f, 1.f); + float t3 = Zeus::Math::clamp(0.f, t * 2.f - 2.f, 1.f); + + Zeus::CColor c1 = Zeus::CColor::lerp(m_line2Clear, m_line2, t1); + Zeus::CColor c2 = Zeus::CColor::lerp(m_line2Clear, m_line2, t2); + Zeus::CColor c3 = Zeus::CColor::lerp(m_line2Clear, m_line2, t3); + + m_cornersOutline[2]->colorGlyphs(c1); + if (t < 0.5) + { + m_cornersOutline[1]->colorGlyphs(Zeus::CColor::skClear); + m_cornersOutline[0]->colorGlyphs(Zeus::CColor::skClear); + m_cornersOutline[3]->colorGlyphs(Zeus::CColor::skClear); + } + else if (t < 1.0) + { + m_cornersOutline[1]->colorGlyphs(c2); + m_cornersOutline[0]->colorGlyphs(Zeus::CColor::skClear); + m_cornersOutline[3]->colorGlyphs(c2); + } + else + { + m_cornersOutline[1]->colorGlyphs(c2); + m_cornersOutline[0]->colorGlyphs(c3); + m_cornersOutline[3]->colorGlyphs(c2); + } + + m_verts.lineVerts[0].m_color = c3; + m_verts.lineVerts[1].m_color = c2; + m_verts.lineVerts[2].m_color = m_verts.lineVerts[0].m_color; + m_verts.lineVerts[3].m_color = m_verts.lineVerts[1].m_color; + m_verts.lineVerts[4].m_color = m_verts.lineVerts[3].m_color; + + m_verts.lineVerts[5].m_color = c3; + m_verts.lineVerts[6].m_color = m_verts.lineVerts[5].m_color; + m_verts.lineVerts[7].m_color = m_verts.lineVerts[6].m_color; + m_verts.lineVerts[8].m_color = c2; + m_verts.lineVerts[9].m_color = m_verts.lineVerts[8].m_color; + m_verts.lineVerts[10].m_color = m_verts.lineVerts[9].m_color; + + m_verts.lineVerts[11].m_color = c2; + m_verts.lineVerts[12].m_color = m_verts.lineVerts[11].m_color; + m_verts.lineVerts[13].m_color = c1; + m_verts.lineVerts[14].m_color = m_verts.lineVerts[12].m_color; + m_verts.lineVerts[15].m_color = m_verts.lineVerts[13].m_color; + m_verts.lineVerts[16].m_color = m_verts.lineVerts[15].m_color; + + m_verts.lineVerts[17].m_color = c2; + m_verts.lineVerts[18].m_color = m_verts.lineVerts[17].m_color; + m_verts.lineVerts[19].m_color = m_verts.lineVerts[18].m_color; + m_verts.lineVerts[20].m_color = c1; + m_verts.lineVerts[21].m_color = m_verts.lineVerts[20].m_color; +} + void ModalWindow::setFillVerts(int width, int height, float pf) { std::pair margin = m_cornersFilled[0]->queryGlyphDimensions(0); @@ -174,9 +281,11 @@ ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleCo m_windowBg(res.themeData().splashBackground()), m_windowBgClear(m_windowBg), m_line1(res.themeData().splash1()), - m_line2(res.themeData().splash2()) + m_line2(res.themeData().splash2()), + m_line2Clear(m_line2) { m_windowBgClear[3] = 0.0; + m_line2Clear[3] = 0.0; m_viewBlockBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1); m_vertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), 38); @@ -236,7 +345,9 @@ void ModalWindow::think() Specter::ViewResources& res = rootView().viewRes(); float pf = res.pixelFactor(); - if (m_phase == Phase::BuildIn) + switch (m_phase) + { + case Phase::BuildIn: { bool loadVerts = false; if (m_frame > WIRE_START) @@ -268,14 +379,44 @@ void ModalWindow::think() if (loadVerts) m_vertsBuf->load(&m_verts, sizeof(m_verts)); ++m_frame; + break; } - else if (m_phase == Phase::ResWait) + case Phase::ResWait: { if (res.fontCacheReady()) { updateContentOpacity(1.0); m_phase = Phase::Showing; } + break; + } + case Phase::BuildOut: + { + { + float wt = (WIRE_FRAMES - m_frame) / float(WIRE_FRAMES); + wt = Zeus::Math::clamp(0.f, wt, 1.f); + m_lineTime = CubicEase(wt); + setLineVertsOut(m_width, m_height, pf, m_lineTime); + setLineColorsOut(wt); + if (wt == 0.f) + m_phase = Phase::Done; + } + { + float ft = (SOLID_FRAMES - m_frame) / float(SOLID_FRAMES); + ft = Zeus::Math::clamp(0.f, ft, 1.f); + setFillColors(ft); + } + if (res.fontCacheReady()) + { + float tt = (CONTENT_FRAMES - m_frame) / float(CONTENT_FRAMES); + tt = Zeus::Math::clamp(0.f, tt, 1.f); + updateContentOpacity(tt); + } + m_vertsBuf->load(&m_verts, sizeof(m_verts)); + ++m_frame; + break; + } + default: break; } } @@ -296,6 +437,12 @@ bool ModalWindow::skipBuildInAnimation() return true; } +void ModalWindow::close() +{ + m_phase = Phase::BuildOut; + m_frame = 0; +} + void ModalWindow::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) { float pf = rootView().viewRes().pixelFactor(); @@ -335,6 +482,9 @@ void ModalWindow::resized(const boo::SWindowRect& root, const boo::SWindowRect& void ModalWindow::draw(boo::IGraphicsCommandQueue* gfxQ) { + if (m_phase == Phase::Done) + return; + gfxQ->setShaderDataBinding(m_vertsShaderBinding); gfxQ->setDrawPrimitive(boo::Primitive::TriStrips); gfxQ->draw(0, 22); diff --git a/specter/lib/Table.cpp b/specter/lib/Table.cpp index a98e8fad3..d72fe9e37 100644 --- a/specter/lib/Table.cpp +++ b/specter/lib/Table.cpp @@ -1,12 +1,84 @@ #include "Specter/Table.hpp" +#include "Specter/ViewResources.hpp" namespace Specter { -Table::Table(ViewResources& res, View& parentView) -: View(res, parentView) +Table::Table(ViewResources& res, View& parentView, ITableDataBinding* data, ITableStateBinding* state) +: View(res, parentView), m_data(data), m_state(state), m_rowsView(*this, res) { + m_vertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), + SPECTER_TABLE_MAX_ROWS * 6); + + if (!res.m_viewRes.m_texVtxFmt) + { + boo::VertexElementDescriptor vdescs[] = + { + {m_vertsBuf, nullptr, boo::VertexSemantic::Position4}, + {m_vertsBuf, nullptr, boo::VertexSemantic::Color} + }; + m_vtxFmt = res.m_factory->newVertexFormat(2, vdescs); + boo::IGraphicsBuffer* bufs[] = {m_viewVertBlockBuf}; + m_shaderBinding = res.m_factory->newShaderDataBinding(res.m_viewRes.m_solidShader, + m_vtxFmt, m_vertsBuf, nullptr, + nullptr, 1, bufs, 0, nullptr); + } + else + { + boo::IGraphicsBuffer* bufs[] = {m_viewVertBlockBuf}; + m_shaderBinding = res.m_factory->newShaderDataBinding(res.m_viewRes.m_solidShader, + res.m_viewRes.m_texVtxFmt, + m_vertsBuf, nullptr, + nullptr, 1, bufs, 0, nullptr); + } commitResources(res); + + m_scroll.m_view.reset(new ScrollView(res, *this)); + m_scroll.m_view->setContentView(&m_rowsView); +} + +void Table::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) +{ + m_scroll.mouseDown(coord, button, mod); +} + +void Table::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) +{ + m_scroll.mouseUp(coord, button, mod); +} + +void Table::mouseEnter(const boo::SWindowCoord& coord) +{ + m_scroll.mouseEnter(coord); +} + +void Table::mouseLeave(const boo::SWindowCoord& coord) +{ + m_scroll.mouseLeave(coord); +} + +void Table::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll) +{ + m_scroll.scroll(coord, scroll); +} + +void Table::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) +{ + boo::SWindowRect rowsRect = sub; + m_scroll.m_view->resized(root, rowsRect); +} + +void Table::RowsView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) +{ +} + +void Table::draw(boo::IGraphicsCommandQueue* gfxQ) +{ + m_scroll.m_view->draw(gfxQ); +} + +void Table::RowsView::draw(boo::IGraphicsCommandQueue* gfxQ) +{ } } diff --git a/specter/resources/fonts/SpecterCurveGlyphs.ttf.gz b/specter/resources/fonts/SpecterCurveGlyphs.ttf.gz index e9d3a7c913c0fd016427bc3f9ecf587c6eb74b5d..147031a2ac3324bbdfe503774569566eafaa4691 100644 GIT binary patch literal 1175 zcmV;I1ZevoiwFp7t$9`e15 z4M}x37K2h{wrsVj$!0Ryq!OaoCT@yo4aSuAA=P!W*-f*_ZugQV`XYHz`l9%vMQHFr z6a_Vlm?&DX8nF+8M7+|6P_2n15s|7zcXs^F?8&V~6#C>@X8!BeBo~%5K=z zL^PLi97G~-7_v8!ni%|P{9Fy}2jIVwj7JA*qTYGffTy-36u$SXreNC+IhY*DkJmL( z7vx#UJ5%XcRPpe)fp36(^GI|&Lp^ji>~}&oMx!I~BjcyjL>lt($xJ$zfAz29M~QTS zf1E^bc;@ykKK;n`9luevm&BT%>R;YWy{hgALYyiK7*{h}b*f@_BW+%qtEs`xRWRH| z3Ee#uay4qzFnbn$q_t>>!fvfl(RJxQ|O+-dk1hH&ZR#*gXKY*bt|DQ z6&S?$ir6PW#c6?7D!pnc^4THrU67b7t0d0ML!vh>vNqMd=q>t~7U*{pyMpuJ16m(~>J*x0)YMVbRa&*C zJ=*z>qQb$_v?A1+P#1L%D|Hl^(6{&l{#^lopo<&jAbX*l)cXE>y-USStL(1sU?XG)XoAg{ad{WrQ( zO4(o7XPNWu;u5c8`{;^3T&yWzMf+6g9r&NN&)6@EtK#~(HySwtr~@G8&b9NHszV}NA}Oli(hVyAHWR*5N-F`ca=Z4U(y|R2QzuU(p{N`>*$U3 zxxb0Rl4n#)dC7B3!Sj;Gp_3zdm1On0sshL_a(FPr(D73@y zrp|O`B0H2w=8fI4dc!iCTN{jH(bzCdy@(ZuUmou)Vi)ip1K&+4Ct>7olV3ID`Xn&#?_@kX<3cUR-@?i8X7$00&>`8(U5@xLA@*-f*_uKSZVidG5|v@d-SY+o9E z5JgLCBG@PxL=b&Yv{mq>^g#+K7(p;<(aenJ&dxSJMHKqvUGALU@0@eb{aFxzTk#SU z^d=H}cOTyM!OK7}#@Y>!_q9e-#qu$L4zS#x8cF7_F5dPv%a?dQoEj?`h-)E0+01q| zoh;^Emm?wA%yKZD9UnZ`f3LJZ#Qs+^R&t=OWBre8V?4HGSW$nzW|D2|SZ>UWl#blq zia5(NEN{%_Qc2;6GmMw^#%OZH$~_o54`?PJj^=a4(mQ`W`xKzR z&-Uja*T!?r#9h0eUfcZ}YJ-sD;_)lLE~mPyyRu#gR0c^M)0vu66}uN{`OrK~9sepA z;i05%J0hM&tr?~h><6t~<0y1@g(^Q_P&7z@rd*FE>X(J?9o#j*`{6G95g2p^G2>N& z6%`mDF)#bX(066wK&8?fhT_i+K0eM8d9n(*X91?f#c(!P=5ZH(aa~ufUAL5v0!ztB z_-kE0R`=$AG?agLsSfX)quKvGif8a5PT&ka#bqo&c7^vt3~2i}sE)`Kp|yKjSMhUn zDxh8Jo>m02m{O!#lj>-is4dMYPj4bEOa^LHTT^MnSJd|vPgc-`j}eh+M)SJ6Owd|pF6P569(^|SJ3 zs@xkyJ(T1Ljd;xG1?Qdcc?C~m(&tq)QnSx%Fv#-x0P9a;CvwPR90d#^jSN?4@V!r= ziD?uj+R(wL*}x$rSv$<$*qO_Z7lzWAlCe3}WJJxjj%MRfGBwPmK8|IvUx~j3j$P*O z81p^Ia*jO5FS1kKN|mg_&T?VQ>dB7hGpyUgPHA?}a+(5*R=S)`GLzthqa04s0()3q zSrIPGKo`&dlCa|0i56Z=l)u$wA(0y`C31zdWke&U(Pi9N7%Mwl+M+FTkvFlr{T`sm zEtab>IKQkQ;!(L0?6(TVq1>opnr#u&G>x0cRegQk*gw2eb>3MN_?QGKq>@Gf()qop z(aWPliCDt)X`4@@K5h4D%%>eb-Qv^E$|>i62~oXm7h9kI%%iXNZS?5zD{uS_Fa8Oa HPzV43z=bt<