From 569a5395181907be1b388701bb8c2e168d4656ea Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 16 Dec 2014 21:45:56 -0800 Subject: [PATCH] new dot symbolizer for fast rendering of points - refs #1651 mapbox/mapnik-vector-tile#62 #2612 --- CHANGELOG.md | 1 + deps/agg/include/agg_ellipse.h | 1 + include/mapnik/agg_renderer.hpp | 3 + include/mapnik/symbolizer_base.hpp | 4 +- include/mapnik/symbolizer_utils.hpp | 6 + src/agg/process_dot_symbolizer.cpp | 105 ++++++++++++++++++ src/build.py | 1 + src/load_map.cpp | 25 +++++ .../dots-500-100-1.0-grid-reference.json | 33 ++++++ .../dots-500-100-2.0-grid-reference.json | 33 ++++++ .../images/dots-500-100-1.0-agg-reference.png | Bin 0 -> 2802 bytes .../dots-500-100-1.0-cairo-reference.png | Bin 0 -> 101 bytes .../images/dots-500-100-2.0-agg-reference.png | Bin 0 -> 2802 bytes .../dots-500-100-2.0-cairo-reference.png | Bin 0 -> 101 bytes tests/visual_tests/styles/dots.xml | 26 +++++ 15 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/agg/process_dot_symbolizer.cpp create mode 100644 tests/visual_tests/grids/dots-500-100-1.0-grid-reference.json create mode 100644 tests/visual_tests/grids/dots-500-100-2.0-grid-reference.json create mode 100644 tests/visual_tests/images/dots-500-100-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/dots-500-100-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/dots-500-100-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/dots-500-100-2.0-cairo-reference.png create mode 100644 tests/visual_tests/styles/dots.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0a81c0b..49849e340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ For a complete change history, see the git log. ## 3.0.0 +- Added new and experimental `dot` symbolizer for fast rendering of points - Improved support for International Text (now uses harfbuzz library for text shaping) - Uses latest c++11 features for better performance (especially map loading) - Expressions everywhere: all symbolizer properties can now be data driven expressions (with the exception of `face-name` and `fontset-name` on the `TextSymbolizer`). diff --git a/deps/agg/include/agg_ellipse.h b/deps/agg/include/agg_ellipse.h index b046b1cd0..671fd571c 100644 --- a/deps/agg/include/agg_ellipse.h +++ b/deps/agg/include/agg_ellipse.h @@ -48,6 +48,7 @@ namespace agg void approximation_scale(double scale); void rewind(unsigned path_id); unsigned vertex(double* x, double* y); + unsigned num_steps() { return m_num; } private: void calc_num_steps(); diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 0b9ccdb8f..58b18b35d 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -122,6 +122,9 @@ public: void process(debug_symbolizer const& sym, feature_impl & feature, proj_transform const& prj_trans); + void process(dot_symbolizer const& sym, + mapnik::feature_impl & feature, + proj_transform const& prj_trans); inline bool process(rule::symbolizers const&, mapnik::feature_impl&, diff --git a/include/mapnik/symbolizer_base.hpp b/include/mapnik/symbolizer_base.hpp index 31ce302a0..39a78a2b2 100644 --- a/include/mapnik/symbolizer_base.hpp +++ b/include/mapnik/symbolizer_base.hpp @@ -149,6 +149,7 @@ struct MAPNIK_DECL raster_symbolizer : public symbolizer_base {}; struct MAPNIK_DECL building_symbolizer : public symbolizer_base {}; struct MAPNIK_DECL group_symbolizer : public symbolizer_base {}; struct MAPNIK_DECL debug_symbolizer : public symbolizer_base {}; +struct MAPNIK_DECL dot_symbolizer : public symbolizer_base {}; // symbolizer using symbolizer = util::variant; + debug_symbolizer, + dot_symbolizer>; } diff --git a/include/mapnik/symbolizer_utils.hpp b/include/mapnik/symbolizer_utils.hpp index 7a73423f1..65e3f42bc 100644 --- a/include/mapnik/symbolizer_utils.hpp +++ b/include/mapnik/symbolizer_utils.hpp @@ -120,6 +120,12 @@ struct symbolizer_traits static char const* name() { return "DebugSymbolizer";} }; +template<> +struct symbolizer_traits +{ + static char const* name() { return "DotSymbolizer";} +}; + // symbolizer name impl namespace detail { diff --git a/src/agg/process_dot_symbolizer.cpp b/src/agg/process_dot_symbolizer.cpp new file mode 100644 index 000000000..4b53d3ced --- /dev/null +++ b/src/agg/process_dot_symbolizer.cpp @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// mapnik +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// agg +#include "agg_ellipse.h" +#include "agg_rendering_buffer.h" +#include "agg_pixfmt_rgba.h" +#include "agg_scanline_u.h" +#include "agg_renderer_scanline.h" +#include "agg_color_rgba.h" +#include "agg_renderer_base.h" + +namespace mapnik { + +template +void agg_renderer::process(dot_symbolizer const& sym, + mapnik::feature_impl & feature, + proj_transform const& prj_trans) +{ + double width = 0.0; + double height = 0.0; + bool has_width = has_key(sym,keys::width); + bool has_height = has_key(sym,keys::height); + if (has_width && has_height) + { + width = get(sym, keys::width, feature, common_.vars_, 0.0); + height = get(sym, keys::height, feature, common_.vars_, 0.0); + } + else if (has_width) + { + width = height = get(sym, keys::width, feature, common_.vars_, 0.0); + } + else if (has_height) + { + width = height = get(sym, keys::height, feature, common_.vars_, 0.0); + } + double rx = width/2.0; + double ry = height/2.0; + double opacity = get(sym, keys::opacity, feature, common_.vars_, 1.0); + color const& fill = get(sym, keys::fill, feature, common_.vars_, mapnik::color(128,128,128)); + ras_ptr->reset(); + agg::rendering_buffer buf(current_buffer_->raw_data(),current_buffer_->width(),current_buffer_->height(),current_buffer_->width() * 4); + using blender_type = agg::comp_op_adaptor_rgba_pre; + using pixfmt_comp_type = agg::pixfmt_custom_blend_rgba; + using renderer_base = agg::renderer_base; + using renderer_type = agg::renderer_scanline_aa_solid; + pixfmt_comp_type pixf(buf); + pixf.comp_op(static_cast(get(sym, keys::comp_op, feature, common_.vars_, src_over))); + renderer_base renb(pixf); + renderer_type ren(renb); + agg::scanline_u8 sl; + ren.color(agg::rgba8_pre(fill.red(), fill.green(), fill.blue(), int(fill.alpha() * opacity))); + agg::ellipse el(0,0,rx,ry); + unsigned num_steps = el.num_steps(); + for (geometry_type const& geom : feature.paths()) { + double x,y,z = 0; + unsigned cmd = 1; + geom.rewind(0); + while ((cmd = geom.vertex(&x, &y)) != mapnik::SEG_END) { + if (cmd == SEG_CLOSE) continue; + prj_trans.backward(x,y,z); + common_.t_.forward(&x,&y); + el.init(x,y,rx,ry,num_steps); + ras_ptr->add_path(el); + agg::render_scanlines(*ras_ptr, sl, ren); + } + } +} + +template void agg_renderer::process(dot_symbolizer const&, + mapnik::feature_impl &, + proj_transform const&); + +} diff --git a/src/build.py b/src/build.py index 6ccbdb11f..0f32bce0b 100644 --- a/src/build.py +++ b/src/build.py @@ -301,6 +301,7 @@ for cpp in enabled_imaging_libraries: source += Split( """ agg/agg_renderer.cpp + agg/process_dot_symbolizer.cpp agg/process_building_symbolizer.cpp agg/process_line_symbolizer.cpp agg/process_line_pattern_symbolizer.cpp diff --git a/src/load_map.cpp b/src/load_map.cpp index 631135a45..338d8edbc 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -119,6 +119,7 @@ private: void parse_markers_symbolizer(rule & rule, xml_node const& node); void parse_group_symbolizer(rule &rule, xml_node const& node); void parse_debug_symbolizer(rule & rule, xml_node const& node); + void parse_dot_symbolizer(rule & rule, xml_node const& node); void parse_group_rule(group_symbolizer_properties &prop, xml_node const& node); void parse_simple_layout(group_symbolizer_properties &prop, xml_node const& node); void parse_pair_layout(group_symbolizer_properties &prop, xml_node const& node); @@ -853,6 +854,11 @@ void map_parser::parse_symbolizers(rule & rule, xml_node const & node) parse_debug_symbolizer(rule, sym_node); sym_node.set_processed(true); break; + case name2int("DotSymbolizer"): + parse_dot_symbolizer(rule, sym_node); + sym_node.set_processed(true); + break; + default: break; } @@ -908,6 +914,25 @@ void map_parser::parse_point_symbolizer(rule & rule, xml_node const & node) } } +void map_parser::parse_dot_symbolizer(rule & rule, xml_node const & node) +{ + try + { + dot_symbolizer sym; + set_symbolizer_property(sym, keys::fill, node); + set_symbolizer_property(sym, keys::opacity, node); + set_symbolizer_property(sym, keys::width, node); + set_symbolizer_property(sym, keys::height, node); + set_symbolizer_property(sym, keys::comp_op, node); + rule.append(std::move(sym)); + } + catch (config_error const& ex) + { + ex.append_context(node); + throw; + } +} + void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node) { try diff --git a/tests/visual_tests/grids/dots-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/dots-500-100-1.0-grid-reference.json new file mode 100644 index 000000000..dcf3d346b --- /dev/null +++ b/tests/visual_tests/grids/dots-500-100-1.0-grid-reference.json @@ -0,0 +1,33 @@ +{ + "keys": [ + "" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/grids/dots-500-100-2.0-grid-reference.json b/tests/visual_tests/grids/dots-500-100-2.0-grid-reference.json new file mode 100644 index 000000000..dcf3d346b --- /dev/null +++ b/tests/visual_tests/grids/dots-500-100-2.0-grid-reference.json @@ -0,0 +1,33 @@ +{ + "keys": [ + "" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/images/dots-500-100-1.0-agg-reference.png b/tests/visual_tests/images/dots-500-100-1.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..863e0d08e6019b13eea7d562cc0c15a9e604c994 GIT binary patch literal 2802 zcmV003kN0{{R3M;S6t0003~P)t-s>=_Z| zBpBi<9NjP=*E%QGJ1CwYf1DwM(L*hjB#g{TF^wjc$Wb=OQa6GsrF|@_#Z@_ZEwRB| zKE7W-zF8#fZzf4xG2 zriEF!MvJ11U7e9(N=yB;OO(8PTd+==QBnPpqiw2HsaaY5q+7J1Ub}(4et^G#zL9Za zV*Qv(wnlgX2Xdh`BzdVzoZfPnt~fPkCL zoBoA`y|jPy|IEzJ&i~lw-_z6n{?pUj z+yDRE+~ec_|K#ND_U-@c>+J0R|NsAm6L!M@00~-2L_t(|ob8?cU(!w-z!i(qOwArF z{aP|JH3Kaw&8L*@qR59RF)DOcm_rfv|Nr#e00-=Dx7%&N-Mx7KPz(6HaJ{|fyU%)b z_5mP6{^7!&f?%)E>R6uW>GTgkfSNuh{_GJ-o5@jYW4VF{>Ex7=MYUF7%K^uhT5=2R zKq%=2XKg0fYT9#b7N{+|krpG8^b5QYBB?}()C?HJ_SoW(l$8@F4kOYOZ&dl zw!^M;FPjwpcoFv}xL93JgPh;{Y(ET3_dw^Q>pafRh#{S%ik3H(wb$YB`clthqfl=vR z2fvO}sq)(TiX^S9zTLihr8bpX%;!KNnJoIAhTR1k;#H<$Q+m$JnHCerMH8cpuf<~9 z4X@M}P4X%LY1QX~O9}8dXk%0QK1g{BKsU_t`i4==*PCB#Y?gET384B}mNif_+|9|c zDZQBWT4f?PLKww-eRWN4daZdQL@Aj_r2TBb;1V{adj~bzmd(1|{-SN9UCJ5Nvy2Ev zA;u_O9O-aTmltN%S6A&mD~0v)_}k~L<>jr<-;OU!=|mz%=3JD5E{=3)j%D2#rorMa z4C5c-4SYCGfy6faK?GvV%;*A1w;foJH{{sDCgkmx{&P8QBl?-rLdm@}=>?@(C2{`gI#3 zmh-{B8iVyKqjtYudGllN-^?P+N!I}IF*l=^DjQrOUR%;{Ua7uWzv}Dl9jv_hn&vB2 z-bQTfN!K&JHX1to&OEoIU%yg)-MR|(_VnGYy!o8s8}op>J<#k)_el=9xc$hI-Ukut z=?RTg-+V;zg+=~SObI6)4PJVvHf*Pou9tY*I3t|&eRMYvTfLiuwtLjoJBJ7+-DuL0 z+gR`&IqOZTjvRjoCSA{@ktH2JWV7Dv4%tM)Wpbtl)#OZ`w{Dzp(u+CthBcRI*cvp` zuqt_?YGzrr%xZ9p-c{^7zhuXX$eo>n_e6 z4=|o{=v*Y}1!I_YKOAX7~A9VKZS)y0%L< z4g01lb?nBiDV+2-=4TsIikbhWeAa4_zicyAn3GP)_nOqw~f@h z594EibBe}tuTkm)m#*AibF9zDZt@6w(sj*yQi3MwPlktZD{VnY&!xF-&rKMVPQeeH zPWv0kKi;@*Ns5b1AhVlh2AztgaJKgqAyKoIE%klV344(sYZ7hLib(+I2~h;(Ra z_3@4HFj~6LrB#i~o$k#rDLudMxeB${?^FE_&ARVR6h-cxvnib-gZg*7+$Ro$wjj4l z@d}gD)ui{eYI{*P7$1fgSrG-Dd}AbsPt8Wcq;wiiVd+ERFw~1bMa2@%&uE-a#90U< zy>{54k^Arz&3kk#;bDhez9-@=#F1V(*3``8lx@WR!eK_O+&$LBDTO)6dL(kEu@gV% zm{Y>dy1epM9OoiQH;*bq^oRTF&XEmz6_;jxO-k@T5oaNibW~D;v7s^R`UDqSz)Igt z@IMb%M=0s~88XYz_${-FUvwE|mz~m&$Y%Nxj(5btFe^Pz^-%J%f{h($07*qoM6N<$ Ef(2*14gdfE literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/dots-500-100-1.0-cairo-reference.png b/tests/visual_tests/images/dots-500-100-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..e11b74acfd17e8f70ff00fadbe9226cd0ea7a72c GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0y~yVEh7Pr!X=DNiWU-J0Qgz;1lBd|NsBxN2E^xxw4)v vjv*1PZx1r^0(plPeD}}gXBPo785j;sXOsk*ugTe~DWM4fp?evG literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/dots-500-100-2.0-agg-reference.png b/tests/visual_tests/images/dots-500-100-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..863e0d08e6019b13eea7d562cc0c15a9e604c994 GIT binary patch literal 2802 zcmV003kN0{{R3M;S6t0003~P)t-s>=_Z| zBpBi<9NjP=*E%QGJ1CwYf1DwM(L*hjB#g{TF^wjc$Wb=OQa6GsrF|@_#Z@_ZEwRB| zKE7W-zF8#fZzf4xG2 zriEF!MvJ11U7e9(N=yB;OO(8PTd+==QBnPpqiw2HsaaY5q+7J1Ub}(4et^G#zL9Za zV*Qv(wnlgX2Xdh`BzdVzoZfPnt~fPkCL zoBoA`y|jPy|IEzJ&i~lw-_z6n{?pUj z+yDRE+~ec_|K#ND_U-@c>+J0R|NsAm6L!M@00~-2L_t(|ob8?cU(!w-z!i(qOwArF z{aP|JH3Kaw&8L*@qR59RF)DOcm_rfv|Nr#e00-=Dx7%&N-Mx7KPz(6HaJ{|fyU%)b z_5mP6{^7!&f?%)E>R6uW>GTgkfSNuh{_GJ-o5@jYW4VF{>Ex7=MYUF7%K^uhT5=2R zKq%=2XKg0fYT9#b7N{+|krpG8^b5QYBB?}()C?HJ_SoW(l$8@F4kOYOZ&dl zw!^M;FPjwpcoFv}xL93JgPh;{Y(ET3_dw^Q>pafRh#{S%ik3H(wb$YB`clthqfl=vR z2fvO}sq)(TiX^S9zTLihr8bpX%;!KNnJoIAhTR1k;#H<$Q+m$JnHCerMH8cpuf<~9 z4X@M}P4X%LY1QX~O9}8dXk%0QK1g{BKsU_t`i4==*PCB#Y?gET384B}mNif_+|9|c zDZQBWT4f?PLKww-eRWN4daZdQL@Aj_r2TBb;1V{adj~bzmd(1|{-SN9UCJ5Nvy2Ev zA;u_O9O-aTmltN%S6A&mD~0v)_}k~L<>jr<-;OU!=|mz%=3JD5E{=3)j%D2#rorMa z4C5c-4SYCGfy6faK?GvV%;*A1w;foJH{{sDCgkmx{&P8QBl?-rLdm@}=>?@(C2{`gI#3 zmh-{B8iVyKqjtYudGllN-^?P+N!I}IF*l=^DjQrOUR%;{Ua7uWzv}Dl9jv_hn&vB2 z-bQTfN!K&JHX1to&OEoIU%yg)-MR|(_VnGYy!o8s8}op>J<#k)_el=9xc$hI-Ukut z=?RTg-+V;zg+=~SObI6)4PJVvHf*Pou9tY*I3t|&eRMYvTfLiuwtLjoJBJ7+-DuL0 z+gR`&IqOZTjvRjoCSA{@ktH2JWV7Dv4%tM)Wpbtl)#OZ`w{Dzp(u+CthBcRI*cvp` zuqt_?YGzrr%xZ9p-c{^7zhuXX$eo>n_e6 z4=|o{=v*Y}1!I_YKOAX7~A9VKZS)y0%L< z4g01lb?nBiDV+2-=4TsIikbhWeAa4_zicyAn3GP)_nOqw~f@h z594EibBe}tuTkm)m#*AibF9zDZt@6w(sj*yQi3MwPlktZD{VnY&!xF-&rKMVPQeeH zPWv0kKi;@*Ns5b1AhVlh2AztgaJKgqAyKoIE%klV344(sYZ7hLib(+I2~h;(Ra z_3@4HFj~6LrB#i~o$k#rDLudMxeB${?^FE_&ARVR6h-cxvnib-gZg*7+$Ro$wjj4l z@d}gD)ui{eYI{*P7$1fgSrG-Dd}AbsPt8Wcq;wiiVd+ERFw~1bMa2@%&uE-a#90U< zy>{54k^Arz&3kk#;bDhez9-@=#F1V(*3``8lx@WR!eK_O+&$LBDTO)6dL(kEu@gV% zm{Y>dy1epM9OoiQH;*bq^oRTF&XEmz6_;jxO-k@T5oaNibW~D;v7s^R`UDqSz)Igt z@IMb%M=0s~88XYz_${-FUvwE|mz~m&$Y%Nxj(5btFe^Pz^-%J%f{h($07*qoM6N<$ Ef(2*14gdfE literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/dots-500-100-2.0-cairo-reference.png b/tests/visual_tests/images/dots-500-100-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..e11b74acfd17e8f70ff00fadbe9226cd0ea7a72c GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0y~yVEh7Pr!X=DNiWU-J0Qgz;1lBd|NsBxN2E^xxw4)v vjv*1PZx1r^0(plPeD}}gXBPo785j;sXOsk*ugTe~DWM4fp?evG literal 0 HcmV?d00001 diff --git a/tests/visual_tests/styles/dots.xml b/tests/visual_tests/styles/dots.xml new file mode 100644 index 000000000..36dc356ad --- /dev/null +++ b/tests/visual_tests/styles/dots.xml @@ -0,0 +1,26 @@ + + + + + + dots + + csv + 0,0,5,5 + +fill, opacity,height, width, wkt +green,.5, 40, 40, "POINT(2.5 2.5)" +red, .5, 20, 20, "MULTIPOINT((2 2.5),(2.5 2.5),(3 2.5))" +blue, .5, 45, 45, "POLYGON((1 1,4 1,4 4,1 4,1 1))" +red, .5, 3, 3, "LINESTRING(3 2.5,3.0472522909029145 2.5549083791557554,3.088039946704745 2.6192015984770367,3.120968717931644 2.692088134329871,3.1447426958020195 2.7725928396160553,3.1581869214177796 2.859569153953152,3.1602684919277424 2.9517139787160285,3.150115859191815 3.0475850341520374,3.1270360384124487 3.1456204818095705,3.090529469857131 3.2441605641461093,3.0403023058681398 3.3414709848078967,2.9762759274968564 3.435767728064507,2.898593529924341 3.525242994563949,2.8076236529182754 3.6080919132297717,2.7039605714802892 3.6825396759861526,2.588421502084629 3.7468687332550683,2.4620406210083243 3.799445683953957,2.3260599327010416 3.838747494110833,2.181917067429678 3.863386683229473,2.0312301280479197 3.872135127146751,1.8757797451792864 3.8639461402385225,1.7174885378702207 3.8379745183057543,1.5583982123914466 3.7935942461113443,1.4006445648882897 3.7304136000915884,1.246430683579882 3.648287406936956,1.0979986727928661 3.547326252181924,0.9576002439358948 3.4279024692786355,0.8274665372684367 3.290652778432585,0.7097775527295491 3.136477485296219,0.606631577958298 2.966536191967265,0.5200150067991092 2.7822400161197343,0.45177294193977735 2.5852403579882455,0.4035809708310185 2.3774142988020817,0.3769184946959401 2.160846757592015,0.37304397632518604 1.9378095755409703,0.3929724535957084 1.7107377376983552,0.4374556424314622 1.4822029804218395,0.5069649254805411 1.2548850688650404,0.6016774914054002 1.0315410617374736,0.7214658547096571 0.8149729099992633,0.8658909478409702 0.6079937617301794,1.0341989363401651 0.4133933667857521,1.2253218645141815 0.23390299172467044,1.437882193988065 0.07216026761394456,1.6702012510582676 -0.06932559950169326,1.920311551565356 -0.18820782357901678,2.185972924581849 -0.2823348101737011,2.4646923091307618 -0.34978128405768727,2.753747051974397 -0.38887736562393815,3.0502114897965984 -0.3982352072417812,3.350986556389679 -0.37677282398941525,3.6528321152745926 -0.3237347810995832,3.9524016810311693 -0.2387094327324748,4.246279158964359 -0.12164244300528804,4.531017203016431 0.027153639820840958,4.803176766446595 0.2069939418962261,5.0593673990838255 0.416820095021341,5.2962878292111855 0.6552034322979141,5.510766357600486 0.9203525899932274,5.6998005860669245 1.2101255063356857,5.860596003276281 1.5220457563037595,5.990602956471176 1.8533231098340628,6.087551549283583 2.200878149857013,6.149484022799466 2.5613707367678797,6.1747841994053125 2.9312320579468265,6.162203596480088 3.306699955329308,6.110883849442411 3.683857181350839,6.02037512070598 4.058672194373903,5.890650212364317 4.427042069440575,5.722114145495159 4.78483706933339,5.515609017373219 5.127946394875156,5.272413999093365 5.4523246125098,4.994240389582243 5.754038241781528,4.683221697131584 6.029311975608545,4.34189877581244 6.2745740024088335,3.9732001007988598 6.486499901292641,3.5804173231036964 6.662054589735392,3.16717629986471 6.798531817364951,2.737403850475655 6.893590719648262,2.2952905409040607 6.945288971186986,1.8452498478612391 6.952112109805218,1.3918741005021504 6.912998639345142,0.9398876394743532 6.827360560726954,0.4940976708866134 6.695099026966764,0.05934332565157785 6.516614868014918,-0.3595565377529124 6.292813784961579,-0.7578562271360596 6.0251060697957435,-1.1309362311473898 5.715400765898583,-1.474355768902115 5.3660942451696325,-1.783904285743506 4.980053239466529,-2.055651309423385 4.560592426208784,-2.285994090762115 4.1114467298642285,-2.4717024691612375 3.636738561911254,-2.6099604260809084 3.1409402810613676,-2.698403818583074 2.6288322123574606,-2.7351538200309866 2.105456617575501,-2.718845635708872 1.5760680595181986,-2.648652107097507 1.0460806487014485,-2.524301869365668 0.5210127020395867,-2.3460917818082168 0.006429378924498685)" + + + + + \ No newline at end of file