[
  {
    "path": ".gitignore",
    "content": "# This .gitignore file should be placed at the root of your Unity project directory\n#\n# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore\n#\n/[Ll]ibrary/\n/[Tt]emp/\n/[Oo]bj/\n/[Bb]uild/\n/[Bb]uilds/\n/[Ll]ogs/\n/[Mm]emoryCaptures/\n\n# Never ignore Asset meta data\n!/[Aa]ssets/**/*.meta\n\n# Uncomment this line if you wish to ignore the asset store tools plugin\n# /[Aa]ssets/AssetStoreTools*\n\n# Autogenerated Jetbrains Rider plugin\n[Aa]ssets/Plugins/Editor/JetBrains*\n\n# Visual Studio cache directory\n.vs/\n\n# Gradle cache directory\n.gradle/\n\n# Autogenerated VS/MD/Consulo solution and project files\nExportedObj/\n.consulo/\n*.csproj\n*.unityproj\n*.sln\n*.suo\n*.tmp\n*.user\n*.userprefs\n*.pidb\n*.booproj\n*.svd\n*.pdb\n*.mdb\n*.opendb\n*.VC.db\n\n# Unity3D generated meta files\n*.pidb.meta\n*.pdb.meta\n*.mdb.meta\n\n# Unity3D generated file on crash reports\nsysinfo.txt\n\n# Builds\n*.apk\n*.unitypackage\n\n# Crashlytics generated file\ncrashlytics-build.properties\n\n# Sublime\n*.sublime*"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2023 TSANG, Hao Fung (tyt2y7@gmail.com)\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "> This project was started many, many years ago. The C++ implementation is a pre-OpenGL 3 relic. The C# implementation is more \"modern\" and uses fragment shader, which is better.\n\n<body>\n\t<div class='vaser_wrap'>\n\t\t<div class='article_name'>\n\t\t\t<h1>VASE<span style='line-height:40px;'> renderer</span></h1>\n\t\t</div>\n<h2>About</h2>\n<p>VASE renderer version 0.42 (VASEr 0.42) is a tessellating library for rendering high quality 2D vector graphics. It is an attempt to address unconventional features in 2d graphics. It is intended for OpenGL 1.1+, but much of the code is API independent C++.</p>\n\n<h2>Unconventional features</h2>\n<h3>Per vertex coloring and weighting</h3>\n<div style='position:relative; overflow:hidden; height:220px; float:left;'><img style='position:relative; top:-40px;' src='docs/sample_images/polyline_3.png' /></div>\n<br><br>VASEr revolutionarily lets you control the color and thickness at each vertex for a polyline. This feature is unseen on any commonly available graphics library.\n\t<div style='clear:both'></div>\n\n<h3>Linear gradient along curve</h3>\n<div style='position:relative; overflow:hidden; height:200px; float:right;'><img style='position:relative; top:-60px;' src='docs/sample_images/bezier_2.png' /></div>\n<br>A similar feature is also available to curves. Because there are so many vertices on a curve that it is impractical to specify data on each point, VASEr lets you define a linear gradient of color and thickness along the length of a curve, giving you two more degrees of freedom.\n\t<div style='clear:both'></div>\n\n<h3>Feathering for brush like effects</h3>\n<img src='docs/sample_images/bezier_3.png' style='float:left;' />\n<img src='docs/sample_images/bezier_4.png' style='float:left;' />\n<img src='docs/sample_images/bezier_5.png' style='float:left;' />\n<br>VASEr must use an outsetting polygon anyway, so it is free (as in computational cost) to scale it up, and it is called feathering in VASEr.\n\t<div style='clear:both'></div>\n\n<h3>Premium quality anti aliasing</h3>\n<div style='background:#333; margin: 10px; border:5px solid #AAA; float:left; height: 150px; overflow:hidden;'>\n<img src='docs/sample_images/fade_intro_1.png' />\n<img src='docs/sample_images/fade_intro_2.png' />\n<img src='docs/sample_images/fade_intro_3.png' />\n<img src='docs/sample_images/fade_intro_4.png' />\n</div>\n<br><br>From left to right: raw polygon without anti aliasing, <br>\nanti aliased with outset fade polygon, <br>\nexaggerated outsetting polygon with wireframe, <br>\nsame as left without wireframe.\n\t<div style='clear:both'></div>\n<p>Outset-fade polygon is the high quality, fast, portable, consistent and hassle free technique for anti aliasing. The difficulties are to calibrate the outsetting distance to achieve believable result and to perform tedious tessellation. Luckily VASEr did this for you.</p>\n<p>Below are line rendering comparison between VASEr and Cairo and AGG:</p>\n<div>\n\t<div id='ab_va'>VASEr<br><img src='docs/sample_images/ab_vaser_line_thickness1.png' /></div>\n\t<div id='ab_ca' style='display:none;'>Cairo<br><img src='docs/sample_images/ab_cairo_line_thickness.png' /></div>\n</div>\n<div>\n\t<div id='a_agg' style='display:none;'>AGG<br><img src='docs/sample_images/agg_line_thickness.png' /></div>\n</div>\n<p>There is small difference, but it is more a matter of taste than correctness. In terms of clarity VASEr is like between Cairo and AGG, and VASEr is the most crisp among the three. VASEr even do pixel alignment to 1px completely horizontal or vertical lines.</p>\n<p>The flaw is, because the outsetting distance is resolution dependent, while the triangulation is unaffected under rotational transformation, fidelity will be lost under scale and sheer.</p>\n\n<h2>Articles</h2>\n\n+ <a href='http://artgrammer.blogspot.com/2011/07/drawing-polylines-by-tessellation.html' target='_blank'>Drawing polylines by tessellation.</a><br>\n+ <a href='http://artgrammer.blogspot.hk/2011/05/drawing-nearly-perfect-2d-line-segments.html'>Drawing nearly perfect 2D line segments in OpenGL</a>\n\n<h2>Related Work</h2>\n<ul>\n<li>If you only need uniform color & width and only wanted a clean mesh, you can try out <a href=\"https://github.com/CrushedPixel/Polyline2D\">Polyline2D</a>.</li>\n</ul>\n\n<h2>Acknowledgement</h2>\n<p>Bezier curve subdivision code is extracted from Anti-Grain Geometry V2.4 by Maxim Shemanarev (McSeem).</p>\n<p>The open-source graphics library AGG inspired me a lot during my teenage.</p>\n\n</body>\n"
  },
  {
    "path": "cpp/samples/Makefile",
    "content": "CPP = polyline.cpp segment.cpp samples.cpp\nBIN = $(CPP:.cpp=)\nVASER = ../vaser/*\n\nall: $(BIN)\n\npolyline: polyline.cpp png_readwrite.cpp test1_base.cpp $(VASER)\n\tfltk-config --use-gl --use-images --compile polyline.cpp\n\nsegment: segment.cpp test1_base.cpp $(VASER)\n\tfltk-config --use-gl --compile segment.cpp\n\nsamples: samples.cpp test1_base.cpp $(VASER)\n\tfltk-config --use-gl --compile samples.cpp\n\nwin32: $(CPP) $(VASER)\n\t$(foreach var,$(BIN),g++ -I/usr/local/include -mwindows -DWIN32 -DUSE_OPENGL32 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -o $(var) $(var).cpp -mwindows /usr/local/lib/libfltk_gl.a -static -lpng -lz -lglu32 -lopengl32 /usr/local/lib/libfltk.a -lole32 -luuid -lcomctl32 -static-libgcc -static-libstdc++ & )\n"
  },
  {
    "path": "cpp/samples/png_readwrite.cpp",
    "content": "#include <stdio.h>\n#include <png.h>\n\nint\t// 0 on failure, 1 on success\nsave_png(const char *filename,\t// filename\n\tunsigned char* buffer,\t\t// buffer of an image\n\tint w,\t\t// width of image\n\tint h,\t\t// height\n\tint b)\t\t// color depth/ bytes per pixel\n{\n\tint\ty;\t\t\t\t\t\t// Current row\n\tconst unsigned char\t*ptr;\t// Pointer to image data\n\tFILE\t\t\t\t*fp;\t// File pointer\n\tpng_structp\t\t\tpp;\t\t// PNG data\n\tpng_infop\t\t\tinfo;\t// PNG image info\n\n\tif ( !filename)\n\t{\n\t\tprintf(\"invalid filename.\\n\");\n\t\treturn 0;\n\t}\n\tif ( !buffer)\n\t{\n\t\tprintf(\"null pointer buffer.\\n\");\n\t\treturn 0;\n\t}\n\t\n\t// Create the output file...\n\tif ((fp = fopen(filename, \"wb\")) == NULL)\n\t{\n\t\tprintf(\"Unable to create PNG image.\\n\");\n\t\treturn (0);\n\t}\n\n\t// Create the PNG image structures...\n\tpp = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);\n\tif (!pp)\n\t{\n\t\tfclose(fp);\n\t\tprintf(\"Unable to create PNG data.\\n\");\n\t\treturn (0);\n\t}\n\n\tinfo = png_create_info_struct(pp);\n\tif (!info)\n\t{\n\t\tfclose(fp);\n\t\tpng_destroy_write_struct(&pp, 0);\n\t\tprintf(\"Unable to create PNG image information.\\n\");\n\t\treturn (0);\n\t}\n\n\tif (setjmp(png_jmpbuf(pp)))\n\t{\n\t\tfclose(fp);\n\t\tpng_destroy_write_struct(&pp, &info);\n\t\tprintf(\"Unable to write PNG image.\\n\");\n\t\treturn (0);\n\t}\n\n\tpng_init_io(pp, fp);\n\n\tpng_set_compression_level(pp, Z_BEST_COMPRESSION);\n\tpng_set_IHDR(pp, info, w, h, 8,\n\t       b==3? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,\n\t       PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,\n\t       PNG_FILTER_TYPE_DEFAULT);\n\tpng_set_sRGB(pp, info, PNG_sRGB_INTENT_PERCEPTUAL);\n\tpng_set_sRGB_gAMA_and_cHRM(pp, info, PNG_INFO_sRGB);\n\t\n\tpng_write_info(pp, info);\n\t\n\tptr = buffer;\n\t\n\tfor (y = 0; y < h; y ++)\n\t{\n\t\tpng_write_row(pp, (png_byte *)ptr);\n\t\tptr += w*b;\n\t}\n\n\tpng_write_end(pp, info);\n\tpng_destroy_write_struct(&pp, 0);\n\n\tfclose(fp);\n\treturn (1);\n}\n\n\nunsigned char*\t// 0 on failure, address of memory buffer on success\n\t\t\t\t// must *delete[]* the buffer manually after use\nread_png( const char* filename, // filename\n\tint* width,\t\t\t// store image width into pointer\n\tint* height,\t\t// store image height into pointer\n\tint* channels)\t\t// number of channels\n{\n\tint\t\ti;\t\t\t// Looping var\n\tFILE\t\t*fp;\t// File pointer\n\tpng_structp\tpp;\t\t// PNG read pointer\n\tpng_infop\tinfo;\t// PNG info pointers\n\tpng_bytep\t*rows;\t// PNG row pointers\n\n\t// Open the PNG file...\n\tif ((fp = fopen(filename, \"rb\")) == NULL)\n\t{\n\t\tprintf( \"Unable to open file %s.\\n\", filename);\n\t\treturn 0;\n\t}\n\n\t// Setup the PNG data structures...\n\tpp   = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);\n\tinfo = png_create_info_struct(pp);\n\n\tif (setjmp(pp->jmpbuf))\n\t{\n\t\tprintf(\"Unable to read. png file %s contains errors.\\n\", filename);\n\t\treturn 0;\n\t}\n\n\t// Initialize the PNG read \"engine\"...\n\tpng_init_io(pp, fp);\n\n\t// Get the image dimensions and convert to grayscale or RGB...\n\tpng_read_info(pp, info);\n\n\tif (info->color_type == PNG_COLOR_TYPE_PALETTE)\n\tpng_set_expand(pp);\n\n\tif (info->color_type & PNG_COLOR_MASK_COLOR)\n\t*channels = 3;\n\telse\n\t*channels = 1;\n\n\tif ((info->color_type & PNG_COLOR_MASK_ALPHA) || info->num_trans)\n\t(*channels) ++;\n\n\tif ( *channels != 3 && *channels != 4)\n\t{\n\t\tprintf(\"png image other than 3 or 4 channels is not supported.\\n\");\n\t\treturn 0;\n\t}\n\n\t*width = info->width;\n\t*height = info->height;\n\n\tif (info->bit_depth < 8)\n\t{\n\tpng_set_packing(pp);\n\tpng_set_expand(pp);\n\t}\n\telse if (info->bit_depth == 16)\n\tpng_set_strip_16(pp);\n\n\t// Handle transparency...\n\tif (png_get_valid(pp, info, PNG_INFO_tRNS))\n\tpng_set_tRNS_to_alpha(pp);\n\n\tunsigned char* array = new unsigned char[(*width) * (*height) * (*channels)];\n\n\t// Allocate pointers...\n\trows = new png_bytep[(*height)];\n\n\tfor (i = 0; i < (*height); i ++)\n\trows[i] = (png_bytep)(array + i * (*width) * (*channels));\n\n\t// Read the image, handling interlacing as needed...\n\tfor (i = png_set_interlace_handling(pp); i > 0; i --)\n\tpng_read_rows(pp, rows, NULL, (*height));\n\n\t#ifdef WIN32\n\t// Some Windows graphics drivers don't honor transparency when RGB == white\n\tif (*channels == 4) {\n\t// Convert RGB to 0 when alpha == 0...\n\tunsigned char* ptr = (unsigned char*) array;\n\tfor (i = (*width) * (*height); i > 0; i --, ptr += 4)\n\tif (!ptr[3]) ptr[0] = ptr[1] = ptr[2] = 0;\n\t}\n\t#endif //WIN32\n\n\t// Free memory\n\tdelete[] rows;\n\n\tpng_read_end(pp, info);\n\tpng_destroy_read_struct(&pp, &info, NULL);\n\n\tfclose(fp);\n\n\treturn array;\n}\n"
  },
  {
    "path": "cpp/samples/polyline.cpp",
    "content": "/*config.h is generated by fltk in your system\n * this file is used with fltk 1.3 with gl enabled.\n * compile by: fltk-config --use-gl --use-images --compile polyline.cpp\n * or something like: g++ -lX11 -lGL -lpng 'polyline.cpp' -o 'polyline'\n*/\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"config.h\" //config.h must always be placed before any Fl header\n#include <FL/gl.h>\n#include <FL/Fl_Box.H>\n#include <FL/Fl_Tabs.H>\n#include <FL/Fl_Window.H>\n#include <FL/Fl_Value_Slider.H>\n#include <FL/Fl_Radio_Light_Button.H>\n\nnamespace VASEr\n{\n\tstruct Vec2 { double x,y;};\n\tstruct Color { float r,g,b,a;};\n}\n#define VASER_DEBUG\n#include \"../vaser/vaser.cpp\"\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n#include \"png_readwrite.cpp\"\n\nusing namespace VASEr;\nvoid test_draw();\n#include \"test1_base.cpp\"\n\nconst int buf_size=20;\nVec2 AP[buf_size];\nint size_of_AP=0;\nColor AC[buf_size];\ndouble AW[buf_size];\n\nFl_Window* main_wnd;\nGl_Window* gl_wnd;\nFl_Slider *weight, *feathering;\nFl_Button *poly, *bezi;\nFl_Button *feather, *no_feather_at_cap, *no_feather_at_core;\nFl_Button *jt_miter, *jt_bevel, *jt_round;\nFl_Button *jc_butt, *jc_round, *jc_square, *jc_rect;\nFl_Button *jc_both, *jc_first, *jc_last, *jc_none;\nFl_Button *colored, *alphaed, *weighted;\nFl_Button *skeleton, *triangulate;\nFl_Slider *bz_ap, *bz_ag, *bz_cu;\n\nvoid line_update()\n{\n\tColor cc[3];\n\tColor grey = {.4,.4,.4, 1};\n\t\n\t{ Color col={1 , 0, 0, 1}; cc[0]=col;}\n\t{ Color col={.8,.8, 0, 1}; cc[1]=col;}\n\t{ Color col={ 0, 0, 1, 1}; cc[2]=col;}\n\t\n\tfor ( int i=0; i<size_of_AP; i++)\n\t{\n\t\tif ( colored->value())\n\t\t\tAC[i] = cc[i%3];\n\t\telse\n\t\t\tAC[i] = grey;\n\t\t\n\t\tif ( alphaed->value())\n\t\t\tAC[i].a = 0.5f;\n\t\telse\n\t\t\tAC[i].a = 1.0f;\n\t\t\t\n\t\tif ( weighted->value())\n\t\t{\n\t\t\tAW[i] = weight->value() * (0.05 + double(i*2)/size_of_AP);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAW[i] = weight->value();\n\t\t}\n\t}\n}\nvoid line_init( int N)\n{\n\tswitch (N)\n\t{\n\tcase 2:\n\t\tAP[0].x=180; AP[0].y=270;\n\t\tAP[1].x=220; AP[1].y=30;\n\t\tsize_of_AP = 2;\n\tbreak;\n\t\n\tcase 3:\n\t\tAP[0].x=200; AP[0].y=100;\n\t\tAP[1].x=100; AP[1].y=200;\n\t\tAP[2].x=300; AP[2].y=200;\n\t\tsize_of_AP = 3;\n\tbreak;\n\t\n\tcase 4:\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\t\tsize_of_AP = 4;\n\tbreak;\n\t\n\tcase 5:\n\t\tAP[0].x=60; AP[0].y=250;\n\t\tAP[1].x=120; AP[1].y=50;\n\t\tAP[2].x=180; AP[2].y=250;\n\t\tAP[3].x=240; AP[3].y=50;\n\t\tAP[4].x=300; AP[4].y=250;\n\t\tsize_of_AP = 5;\n\tbreak;\n\t\n\tcase 6:\n\t\tAP[0].x=280; AP[0].y=110;\n\t\tAP[1].x=200; AP[1].y=50;\n\t\tAP[2].x=100; AP[2].y=150;\n\t\tAP[3].x=300; AP[3].y=150;\n\t\tAP[4].x=200; AP[4].y=250;\n\t\tAP[5].x=120; AP[5].y=190;\n\t\tsize_of_AP = 6;\n\tbreak;\n\n\tcase 7:\n\t\tAP[0].x=280; AP[0].y=110;\n\t\tAP[1].x=200; AP[1].y=50;\n\t\tAP[2].x=100; AP[2].y=50;\n\t\tAP[3].x=200; AP[3].y=150;\n\t\tAP[4].x=300; AP[4].y=250;\n\t\tAP[5].x=200; AP[5].y=250;\n\t\tAP[6].x=120; AP[6].y=190;\n\t\tsize_of_AP = 7;\n\tbreak;\n\t}\n\tline_update();\n\tgl_wnd->set_drag_target( AP, size_of_AP); \n}\nchar get_joint_type()\n{\n\tif ( jt_miter->value())\n\t\treturn PLJ_miter;\n\telse if ( jt_bevel->value())\n\t\treturn PLJ_bevel;\n\telse if ( jt_round->value())\n\t\treturn PLJ_round;\n\telse\n\t\treturn 0;\n}\nchar get_cap_type()\n{\n\tchar cap;\n\tif ( jc_butt->value())\n\t\tcap=PLC_butt;\n\telse if ( jc_round->value())\n\t\tcap=PLC_round;\n\telse if ( jc_square->value())\n\t\tcap=PLC_square;\n\telse if ( jc_rect->value())\n\t\tcap=PLC_rect;\n\n\tif ( jc_both->value())\n\t\tcap+=PLC_both;\n\telse if ( jc_first->value())\n\t\tcap+=PLC_first;\n\telse if ( jc_last->value())\n\t\tcap+=PLC_last;\n\telse if ( jc_none->value())\n\t\tcap+=PLC_none;\n\treturn cap;\n}\nvoid np_cb(Fl_Widget* W, void*)\n{\n\tint n=3;\n\tsscanf( W->label(), \"%d\", &n);\n\tline_init(n);\n\tif( n<4)\n\t{\n\t\tpoly->value(1);\n\t\tbezi->value(0);\n\t}\n\tline_update();\n\tgl_wnd->redraw();\n}\nvoid line_bezier(Fl_Widget* W, void*)\n{\n\tgl_wnd->redraw();\n}\nvoid drag_cb(Fl_Widget* W, void*)\n{\n\tline_update();\n\tgl_wnd->redraw();\n}\nvoid exportimg_cb(Fl_Widget* W, void*)\n{\n\tImage img = renderer::get_image();\n\tif( img.buf)\n\t{\n\t\tsave_png(\"capture.png\",img.buf,img.width,img.height,4);\n\t\tfree(img.buf);\n\t}\n}\n\nvoid make_form()\n{\n\tFl_Tabs* tabs = new Fl_Tabs(400,0,200,300);\n\n\t{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, \"config\");\n\t\tFl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,\"general configurations\");\n\t\t//weight\n\t\tweight = new Fl_Value_Slider(400,50,200,20,\"weight\");\n\t\tweight->type(FL_HOR_SLIDER);\n\t\tweight->bounds(0.02,30.0);\n\t\tweight->callback(drag_cb);\n\t\tweight->value(8.0);\n\t\t\n\t\t{ Fl_Group* o = new Fl_Group(400,90,200,20);\n\t\t\tpoly = new Fl_Radio_Light_Button(400,90,100,20,\"polyline\");\n\t\t\tbezi = new Fl_Radio_Light_Button(500,90,100,20,\"polybezier\");\n\t\t\tpoly->callback(line_bezier);\n\t\t\tbezi->callback(line_bezier);\n\t\t\tpoly->value(1);\n\t\t\to->end();\n\t\t}\n\n\t\t//number of points\n\t\t(new Fl_Button(400,110,80,20,\"2 points\"))->callback(np_cb);\n\t\t(new Fl_Button(480,110,20,20,\"3\"))->callback(np_cb);\n\t\t(new Fl_Button(500,110,20,20,\"4\"))->callback(np_cb);\n\t\t(new Fl_Button(520,110,20,20,\"5\"))->callback(np_cb);\n\t\t(new Fl_Button(540,110,20,20,\"7\"))->callback(np_cb);\n\n\t\t//test options\n\t\tcolored = new Fl_Light_Button(400,130,60,20,\"colored\");\n\t\tcolored->callback(drag_cb);\n\t\tcolored->value(1);\n\t\talphaed = new Fl_Light_Button(460,130,70,20,\"alpha-ed\");\n\t\talphaed->callback(drag_cb);\n\t\talphaed->value(1);\n\t\tweighted = new Fl_Light_Button(530,130,70,20,\"weighted\");\n\t\tweighted->callback(drag_cb);\n\t\tskeleton = new Fl_Light_Button(400,150,80,20,\"skeleton\");\n\t\tskeleton->value(0);\n\t\tskeleton->callback(drag_cb);\n\t\ttriangulate = new Fl_Light_Button(480,150,120,20,\"triangulation\");\n\t\ttriangulate->value(0);\n\t\ttriangulate->callback(drag_cb);\n\t\t//export button\n\t\tFl_Button *exportimg;\n\t\texportimg = new Fl_Button(400,170,200,20,\"export image\");\n\t\texportimg->callback(exportimg_cb);\n\ttab->end();}\n\n\t{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, \"polyline\");\n\t\tFl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,\"polyline_opt\");\n\n\t\t//feathering\n\t\tfeather = new Fl_Light_Button(400,50,100,20,\"feather\");\n\t\tfeather->value(1);\n\t\tfeather->callback(drag_cb);\n\t\tfeathering = new Fl_Value_Slider(400,70,200,20,\"feathering\");\n\t\tfeathering->type(FL_HOR_SLIDER);\n\t\tfeathering->bounds(1.0,10.0);\n\t\tfeathering->callback(drag_cb);\n\t\tfeathering->value(1.0);\n\t\tno_feather_at_cap  = new Fl_Light_Button(430,110,170,15,\"no_feather_at_cap\");\n\t\tno_feather_at_core = new Fl_Light_Button(430,125,170,15,\"no_feather_at_core\");\n\t\tno_feather_at_cap ->value(0);\n\t\tno_feather_at_core->value(0);\n\t\tno_feather_at_cap ->callback(drag_cb);\n\t\tno_feather_at_core->callback(drag_cb);\n\n\t\t//joint type\n\t\t{ Fl_Group* o = new Fl_Group(400,145,200,30);\n\t\t\tnew Fl_Box(400,145,80,15,\"joint type\");\n\t\t\tjt_miter = new Fl_Radio_Light_Button(420,160,60,15,\"miter\");\n\t\t\tjt_bevel = new Fl_Radio_Light_Button(480,160,60,15,\"bevel\");\n\t\t\tjt_round = new Fl_Radio_Light_Button(540,160,60,15,\"round\");\n\t\t\to->end();\n\t\t\tjt_miter->value(1);\n\t\t\tjt_miter->callback(drag_cb);\n\t\t\tjt_bevel->callback(drag_cb);\n\t\t\tjt_round->callback(drag_cb);\n\t\t}\n\t\t//cap type\n\t\t{ Fl_Group* o = new Fl_Group(400,180,200,45);\n\t\t\tnew Fl_Box(400,180,80,15,\"cap type\");\n\t\t\tjc_butt   = new Fl_Radio_Light_Button(440,195,80,15,\"butt\");\n\t\t\tjc_round  = new Fl_Radio_Light_Button(520,195,80,15,\"round\");\n\t\t\tjc_square = new Fl_Radio_Light_Button(440,210,80,15,\"square\");\n\t\t\tjc_rect   = new Fl_Radio_Light_Button(520,210,80,15,\"rect\");\n\t\t\to->end();\n\t\t\tjc_butt   ->value(1);\n\t\t\tjc_butt   ->callback(drag_cb);\n\t\t\tjc_round  ->callback(drag_cb);\n\t\t\tjc_square ->callback(drag_cb);\n\t\t\tjc_rect   ->callback(drag_cb);\n\t\t}\n\t\t{ Fl_Group* o = new Fl_Group(400,230,200,45);\n\t\t\tnew Fl_Box(400,230,80,15,\"cap parts\");\n\t\t\tjc_both   = new Fl_Radio_Light_Button(440,245,80,15,\"both\");\n\t\t\tjc_first  = new Fl_Radio_Light_Button(520,245,80,15,\"first\");\n\t\t\tjc_last   = new Fl_Radio_Light_Button(440,260,80,15,\"last\");\n\t\t\tjc_none   = new Fl_Radio_Light_Button(520,260,80,15,\"none\");\n\t\t\tjc_both->value(1);\n\t\t\tjc_both ->callback(drag_cb);\n\t\t\tjc_first->callback(drag_cb);\n\t\t\tjc_last ->callback(drag_cb);\n\t\t\tjc_none ->callback(drag_cb);\n\t\t}\n\ttab->end();}\n\n\t/*{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, \"BZ\");\n\t\tFl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,\"polybezier_opt\");\n\t\tbz_ap = new Fl_Value_Slider(400,70,200,15,\"approximation_scale\");\n\t\tbz_ag = new Fl_Value_Slider(400,105,200,15,\"angle_tolerance\");\n\t\tbz_cu = new Fl_Value_Slider(400,140,200,15,\"cusp_limit\");\n\t\tbz_ap->type(FL_HOR_SLIDER);\n\t\tbz_ag->type(FL_HOR_SLIDER);\n\t\tbz_cu->type(FL_HOR_SLIDER);\n\t\tbz_ap->bounds(0.1,5.0); bz_ap->value(BZ_default_approximation_scale); bz_ap->callback(drag_cb);\n\t\tbz_ag->bounds(0.01,0.52); bz_ag->value(BZ_default_angle_tolerance); bz_ag->callback(drag_cb);\n\t\tbz_cu->bounds(0.1,5.0); bz_cu->value(BZ_default_cusp_limit); bz_cu->callback(drag_cb);\n\ttab->end();}*/\n\n\t/*{ Fl_Group* tab = new Fl_Group(400, 20, 200, 300, \"TS\");\n\t\tFl_Box* o = new Fl_Box(FL_NO_BOX,400,25,200,20,\"tessellator\");\n\ttab->end();}*/\n\n\ttabs->end();\n}\nvoid test_draw()\n{\t//main rendering\n\n\trenderer::before();\n\n\tpolyline_opt opt={0};\n\ttessellator_opt tess={0};\n\topt.feather    = feather->value();\n\topt.feathering = feathering->value();\n\topt.no_feather_at_cap = no_feather_at_cap->value();\n\topt.no_feather_at_core = no_feather_at_core->value();\n\topt.joint = get_joint_type();\n\topt.cap   = get_cap_type();\n\topt.tess = &tess;\n\ttess.triangulation = triangulate->value();\n\n\tColor cc={1,1,1,1};\n\tif ( bezi->value())\n\t{\n\t\tpolybezier_opt bz_opt={0};\n\t\tbz_opt.poly = &opt;\n\t\t//bz_opt.approximation_scale = bz_ap->value();\n\t\t//bz_opt.angle_tolerance = bz_ag->value();\n\t\t//bz_opt.cusp_limit = bz_cu->value();\n\t\tgradient grad = {0};\n\t\tgradient_stop stop[10] = {0};\n\t\tgrad.stops = stop;\n\t\tgrad.length = 10;\n\t\tstop[0].t = 0.00; stop[0].type = GS_rgba; stop[0].color = AC[0];\n\t\tstop[1].t = 0.33; stop[1].type = GS_rgba; stop[1].color = AC[1];\n\t\tstop[2].t = 0.66; stop[2].type = GS_rgba; stop[2].color = AC[2];\n\t\tstop[3].t = 1.00; stop[3].type = GS_rgba; stop[3].color = AC[3];\n\t\tstop[4].t = 0.00; stop[4].type = GS_weight; stop[4].weight = AW[0];\n\t\tstop[5].t = 0.33; stop[5].type = GS_weight; stop[5].weight = AW[1];\n\t\tstop[6].t = 0.66; stop[6].type = GS_weight; stop[6].weight = AW[2];\n\t\tstop[7].t = 1.00; stop[7].type = GS_weight; stop[7].weight = AW[3];\n\t\tpolybezier( AP, &grad, size_of_AP, &bz_opt);\n\t\tColor black={0,0,0,1};\n\t\tif ( skeleton->value())\n\t\t{\n\t\t\tpolybezier( AP, cc, 1.0, size_of_AP, 0);\n\t\t\tpolyline( AP, black, 1.0, size_of_AP, 0); //control lines\n\t\t}\n\t}\n\telse\n\t{\n\t\tpolyline( AP, AC, AW, size_of_AP, &opt);\n\t\tif ( skeleton->value())\n\t\t\tpolyline( AP, cc, 1.0, size_of_AP, 0);\n\t}\n\n\trenderer::after();\n}\n\nint main(int argc, char **argv)\n{\n\tmain_wnd = new Fl_Window( 600,300,\"VASEr - polyline and bezier example\");\n\t\tmake_form(); //initialize\n\t\tgl_wnd = new Gl_Window( 0,0,400,300);  gl_wnd->end(); //create gl window\n\t\tline_init(4);\n\tmain_wnd->end();\n\tmain_wnd->show();\n\tmain_wnd->redraw();\n\t\n\treturn Fl::run();\n}\n"
  },
  {
    "path": "cpp/samples/readme.txt",
    "content": "FLTK 1.3.x is required http://www.fltk.org/\nlibpng is required for png support\n"
  },
  {
    "path": "cpp/samples/samples.cpp",
    "content": "/*config.h is generated by fltk in your system\n * this file is used with fltk 1.3 with gl enabled.\n * compile by: fltk-config --use-gl --compile samples.cpp\n*/\n#include <math.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"config.h\" //config.h must always be placed before any Fl header\n#include <FL/gl.h>\n#include <FL/Fl_Window.H>\n#include <FL/Fl_Button.H>\n\nnamespace VASEr\n{\n\tstruct Vec2 { double x,y;};\n\tstruct Color { float r,g,b,a;};\n}\n#include \"../vaser/vaser.cpp\"\nusing namespace VASEr;\n\nvoid test_draw();\n#include \"test1_base.cpp\"\n\nFl_Window* main_wnd;\nGl_Window* gl_wnd;\nint current=0;\n\nvoid drag_cb(Fl_Widget* W, void*)\n{\n\tsscanf( W->label(), \"sample%d\", &current);\n\tgl_wnd->redraw();\n}\nvoid make_form()\n{\n\tfor( int i=0; i<6; i++)\n\t{\n\t\tchar* buf=(char*)malloc(10);\n\t\tsnprintf(buf, 10, \"sample%d\",i);\n\t\t(new Fl_Button(600,20*i,100,20,buf))->callback(drag_cb);\n\t}\n}\nvoid sample0()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor col={ 0.5, 0.8, 1.0, 1};\n\tpolyline( AP, col, 8.0, size_of_AP, 0);\n}\nvoid sample1()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor AC[size_of_AP];\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[0]=col;}\n\t\t{ Color col={.8,.8, 0, 0.5}; AC[1]=col;}\n\t\t{ Color col={ 0, 0, 1, 0.5}; AC[2]=col;}\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[3]=col;}\n\tdouble AW[size_of_AP];\n\t\tAW[0] = 1.0;\n\t\tAW[1] = 15.0;\n\t\tAW[2] = 15.0;\n\t\tAW[3] = 1.0;\n\t\n\tpolyline( AP, AC, AW, size_of_AP, 0);\n}\nvoid sample2()\n{\t//spectrum\n\tfor ( int i=0; i < 20; i++)\n\t{\n\t\tVec2  P1 = { 5+29.7*i, 187};\n\t\tVec2  P2 = { 35+29.7*i, 8};\n\t\tColor C1 = { 1.0,0.0,0.5, 1.0};\n\t\tColor C2 = { 0.5,0.0,1.0, 1.0};\n\t\tdouble W1= 0.3*(i+1);\n\t\tdouble W2= W1;\n\t\t\n\t\tsegment(P1,P2, C1,C2, W1,W2, 0);\n\t}\n}\nvoid sample3()\n{\t//radial spectrum\n\tfor ( double ag=0, i=0; ag < 2*vaser_pi-0.1; ag+=vaser_pi/12, i+=1)\n\t{\n\t\tdouble r1 = 30.0;\n\t\tdouble r2 = 90.0;\n\t\t\n\t\tdouble tx2=r2*cos(ag);\n\t\tdouble ty2=r2*sin(ag);\n\t\tdouble tx1=r1*cos(ag);\n\t\tdouble ty1=r1*sin(ag);\n\t\tdouble Ox = 120;\n\t\tdouble Oy = 194+97;\n\t\t\n\t\tVec2  P1 = { Ox+tx1,Oy-ty1};\n\t\tVec2  P2 = { Ox+tx2,Oy-ty2};\n\t\tColor C1 = { 1.0,0.0,0.5, 1.0};\n\t\tColor C2 = { 0.5,0.0,1.0, 1.0};\n\t\tdouble W1= 0.3*(i+1);\n\t\tdouble W2= W1;\n\t\t\n\t\tpolyline_opt opt={0};\n\t\topt.cap = PLC_round;\n\t\topt.joint = PLJ_round;\n\t\tsegment(P1,P2, C1,C2, W1,W2, &opt);\n\t}\n}\nvoid sample4()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor col={ 0.5, 0.8, 1.0, 1};\n\tpolybezier( AP, col, 12.0, size_of_AP, 0);\n}\nvoid sample5()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor AC[size_of_AP];\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[0]=col;}\n\t\t{ Color col={.8,.8, 0, 0.5}; AC[1]=col;}\n\t\t{ Color col={ 0, 0, 1, 0.5}; AC[2]=col;}\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[3]=col;}\n\tdouble AW[size_of_AP];\n\t\tAW[0] = 1.0;\n\t\tAW[1] = 15.0;\n\t\tAW[2] = 15.0;\n\t\tAW[3] = 1.0;\n\tgradient grad = {0};\n\t\tgradient_stop stop[10] = {0};\n\t\tgrad.stops = stop;\n\t\tgrad.length = 10;\n\t\tstop[0].t = 0.00; stop[0].type = GS_rgba; stop[0].color = AC[0];\n\t\tstop[1].t = 0.50; stop[1].type = GS_rgba; stop[1].color = AC[2];\n\t\tstop[2].t = 1.00; stop[2].type = GS_rgba; stop[2].color = AC[3];\n\t\tstop[3].t = 0.00; stop[3].type = GS_weight; stop[3].weight = AW[0];\n\t\tstop[4].t = 0.33; stop[4].type = GS_weight; stop[4].weight = AW[1];\n\t\tstop[5].t = 0.66; stop[5].type = GS_weight; stop[5].weight = AW[2];\n\t\tstop[6].t = 1.00; stop[6].type = GS_weight; stop[6].weight = AW[3];\n\tpolybezier( AP, &grad, size_of_AP, 0);\n}\nvoid sample6()\n{\n}\nvoid sample7()\n{\n}\nvoid sample8()\n{\n}\nvoid sample9()\n{\n}\nvoid test_draw()\n{\n\trenderer::before();\n\tswitch (current)\n\t{\n\t\tcase 0: sample0(); break;\n\t\tcase 1: sample1(); break;\n\t\tcase 2: sample2(); break;\n\t\tcase 3: sample3(); break;\n\t\tcase 4: sample4(); break;\n\t\tcase 5: sample5(); break;\n\t\tcase 6: sample6(); break;\n\t\tcase 7: sample7(); break;\n\t\tcase 8: sample8(); break;\n\t\tcase 9: sample9(); break;\n\t}\n\trenderer::after();\n}\n\nint main(int argc, char **argv)\n{\n\tmain_wnd = new Fl_Window( 700,400,\"VASEr - demo\");\n\t\tmake_form(); //initialize\n\t\tgl_wnd = new Gl_Window( 0,0,600,400);  gl_wnd->end(); //create gl window\n\tmain_wnd->end();\n\tmain_wnd->show();\n\tmain_wnd->redraw();\n\t\n\treturn Fl::run();\n}\n"
  },
  {
    "path": "cpp/samples/segment.cpp",
    "content": "/*config.h is generated by fltk in your system\n * this file is used with fltk 1.3 with gl enabled.\n * compile by: fltk-config --use-gl --compile segment.cpp\n * or something like: g++ -lX11 -lGL 'segment.cpp' -o 'segment'\n*/\n#include <math.h>\n#include <stdio.h>\n\n#include \"config.h\" //config.h must always be placed before any Fl header\n#include <FL/gl.h>\n#include <FL/Fl_Box.H>\n#include <FL/Fl_Value_Slider.H>\n#include <FL/Fl_Window.H>\n#include <FL/Fl_Radio_Light_Button.H>\n\nnamespace VASEr\n{\n\tstruct Vec2 { double x,y;};\n\tstruct Color { float r,g,b,a;};\n}\n#include \"../vaser/vaser.cpp\"\nusing namespace VASEr;\n\nvoid test_draw();\n#include \"test1_base.cpp\"\n\nFl_Window* main_wnd;\nGl_Window* gl_wnd;\nFl_Slider *start_weight, *feathering;\nFl_Button *feather, *no_feather_at_cap, *no_feather_at_core;\nFl_Button *jc_butt, *jc_round, *jc_square, *jc_rect;\nFl_Button *colored, *alphaed, *weighted;\n\nchar get_cap_type()\n{\n\tif ( jc_butt->value())\n\t\treturn PLC_butt;\n\telse if ( jc_round->value())\n\t\treturn PLC_round;\n\telse if ( jc_square->value())\n\t\treturn PLC_square;\n\telse if ( jc_rect->value())\n\t\treturn PLC_rect;\n\telse\n\t\treturn 0;\n}\nvoid drag_cb(Fl_Widget* W, void*)\n{\n\tgl_wnd->redraw();\n}\nvoid make_form()\n{\n\tconst int LD = 606;\n\t//weight feathering\n\tstart_weight = new Fl_Value_Slider(LD,0,200,20,\"spectrum start weight\");\n\tstart_weight->type(FL_HOR_SLIDER);\n\tstart_weight->bounds(0.3,30.0);\n\tstart_weight->step(0.3*20.0);\n\tstart_weight->value(0.0);\n\tstart_weight->callback(drag_cb);\n\t\n\tfeathering = new Fl_Value_Slider(LD,40,200,20,\"feathering\");\n\tfeathering->type(FL_HOR_SLIDER);\n\tfeathering->bounds(1.0,10.0);\n\tfeathering->callback(drag_cb);\n\tfeathering->value(1.0);\n\t\n\t//feather, no_feather_at_cap, no_feather_at_core\n\tfeather\t\t   = new Fl_Light_Button(LD,80,100,15,\"feather\");\n\tno_feather_at_cap  = new Fl_Light_Button(LD+50,95,150,15,\"no_feather_at_cap\");\n\tno_feather_at_core = new Fl_Light_Button(LD+50,110,150,15,\"no_feather_at_core\");\n\tfeather\t\t  ->value(1);\n\tno_feather_at_cap ->value(0);\n\tno_feather_at_core->value(0);\n\tfeather\t\t  ->callback(drag_cb);\n\tno_feather_at_cap ->callback(drag_cb);\n\tno_feather_at_core->callback(drag_cb);\n\t\n\t//cap type\n\t{\n\tFl_Group* o = new Fl_Group(LD,160,200,30);\n\t\tnew Fl_Box(LD,160,80,15,\"cap type:\");\n\t\tjc_butt   = new Fl_Radio_Light_Button(LD+20,175,40,15,\"butt\");\n\t\tjc_round  = new Fl_Radio_Light_Button(LD+60,175,50,15,\"round\");\n\t\tjc_square = new Fl_Radio_Light_Button(LD+110,175,50,15,\"square\");\n\t\tjc_rect   = new Fl_Radio_Light_Button(LD+160,175,40,15,\"rect\");\n\to->end();\n\tjc_butt   ->value(1);\n\tjc_butt   ->callback(drag_cb);\n\tjc_round  ->callback(drag_cb);\n\tjc_square ->callback(drag_cb);\n\tjc_rect   ->callback(drag_cb);\n\t}\n\t\n\t//test options\n\tcolored = new Fl_Light_Button(LD,250,60,15,\"colored\");\n\tcolored->callback(drag_cb);\n\tcolored->value(1);\n\talphaed = new Fl_Light_Button(LD+60,250,70,15,\"alpha-ed\");\n\talphaed->callback(drag_cb);\n\talphaed->value(1);\n\tweighted = new Fl_Light_Button(LD+130,250,70,15,\"weighted\");\n\tweighted->callback(drag_cb);\n}\nvoid test_draw()\n{\n\trenderer::before();\n\t\n\tpolyline_opt opt={0};\n\topt.feather    = feather->value();\n\topt.feathering = feathering->value();\n\topt.no_feather_at_cap = no_feather_at_cap->value();\n\topt.no_feather_at_core = no_feather_at_core->value();\n\topt.cap   = get_cap_type();\n\t\n\tfor ( int i=0; i<20; i++)\n\t{\n\t\tVec2  P1 = { 5+29.7*i, 187};\n\t\tVec2  P2 = { 35+29.7*i, 8};\n\t\tColor C1 = { 0,0,0, 1};\n\t\tColor C2 = C1;\n\t\tdouble W1= 0.3*(i+1) + start_weight->value();\n\t\tdouble W2= W1;\n\t\tif ( colored->value())\n\t\t{\n\t\t\tColor cc1 = { 1.0,0.0,0.5, 1.0};\n\t\t\tC1 = cc1;\n\t\t\tColor cc2 = { 0.5,0.0,1.0, 1.0};\n\t\t\tC2 = cc2;\n\t\t}\n\t\tif ( alphaed->value())\n\t\t{\n\t\t\tC1.a = 0.5f;\n\t\t\tC2.a = 0.5f;\n\t\t}\n\t\tif ( weighted->value())\n\t\t{\n\t\t\tW1 = 0.1;\n\t\t}\n\t\tif ( opt.cap != PLC_butt)\n\t\t{\n\t\t\tdouble end_weight = 0.3*(20) + start_weight->value();\n\t\t\tP1.y -= end_weight*0.5;\n\t\t\tP2.y += end_weight*0.5;\n\t\t}\n\t\tsegment(P1, P2,       //coordinates\n\t\t\tC1, C2,       //colors\n\t\t\tW1, W2,       //weights\n\t\t\t&opt);        //extra options\n\t}\n\t\n\tconst double pi=3.14159265;\n\tfor ( double ag=0, i=0; ag<2*pi-0.1; ag+=pi/12, i+=1)\n\t{\n\t\tdouble r1 = 0.0;\n\t\tdouble r2 = 90.0;\n\t\tif ( !weighted->value())\n\t\t\tr1 = 30.0;\n\t\tif ( opt.cap != PLC_butt)\n\t\t{\n\t\t\tdouble end_weight = 0.3*(12) + start_weight->value();\n\t\t\tr2 -= end_weight*0.5;\n\t\t}\n\t\t\n\t\tdouble tx2=r2*cos(ag);\n\t\tdouble ty2=r2*sin(ag);\n\t\tdouble tx1=r1*cos(ag);\n\t\tdouble ty1=r1*sin(ag);\n\t\tdouble Ox = 120;\n\t\tdouble Oy = 194+97;\n\t\t\n\t\tVec2  P1 = { Ox+tx1,Oy-ty1};\n\t\tVec2  P2 = { Ox+tx2,Oy-ty2};\n\t\tColor C1 = { 0,0,0, 1};\n\t\tColor C2 = C1;\n\t\tdouble W1= 0.3*(i+1) + start_weight->value();\n\t\tdouble W2= W1;\n\t\tif ( colored->value())\n\t\t{\n\t\t\tColor cc1 = { 1.0,0.0,0.5, 1.0};\n\t\t\tC1 = cc1;\n\t\t\tColor cc2 = { 0.5,0.0,1.0, 1.0};\n\t\t\tC2 = cc2;\n\t\t}\n\t\tif ( alphaed->value())\n\t\t{\n\t\t\tC1.a = 0.5f;\n\t\t\tC2.a = 0.5f;\n\t\t}\n\t\tif ( weighted->value())\n\t\t{\n\t\t\tW1 = 0.1;\n\t\t}\n\t\tsegment(P1, P2,       //coordinates\n\t\t\tC1, C2,       //colors\n\t\t\tW1, W2,       //weights\n\t\t\t&opt);        //extra options\n\t}\n\t\n\trenderer::after();\n}\n\nint main(int argc, char **argv)\n{\n\tmain_wnd = new Fl_Window( 606+200,194*2,\"Vase Renderer - segment() example - fltk/opengl\");\n\t\tmake_form(); //initialize\n\t\tgl_wnd = new Gl_Window( 0,0,606,194*2);  gl_wnd->end(); //create gl window\n\tmain_wnd->end();\n\tmain_wnd->show();\n\tmain_wnd->redraw();\n\t\n\treturn Fl::run();\n}\n"
  },
  {
    "path": "cpp/samples/test1_base.cpp",
    "content": "#include \"config.h\"\n#include <FL/Fl.H>\n#include <FL/gl.h>\n#include <FL/Fl_Window.H>\n#include <FL/Fl_Gl_Window.H>\n\nclass Gl_Window : public Fl_Gl_Window\n{\n\t//methods\n\tvoid init();\n\tvoid draw();\n\tint handle(int);\n\tvoid left_mouse_button_down();\n\tvoid left_mouse_button_up();\n\tvoid leftclick_drag();\n\t\n\t//variables\n\tshort curx,cury, lastx,lasty;\n\tshort cur_drag; //index of point which is being draged currently\n\t\t\t// -1 means none\n\tint tsize;\n\tVec2* target;\n\n\tpublic:\n\tGl_Window(int x,int y,int w,int h,const char *l=0)\n\t\t: Fl_Gl_Window(x,y,w,h,l) { init();}\n\tGl_Window(int w,int h,const char *l=0)\n\t\t: Fl_Gl_Window(w,h,l) { init();}\n\tvoid set_drag_target( Vec2* target, int size_of_target)\n\t\t{ this->target=target; this->tsize=size_of_target;}\n};\nvoid Gl_Window::init()\n{\n\tcurx=0; cury=0; lastx=0; lasty=0;\n\tcur_drag=-1;\n\ttsize=0;\n\ttarget=0;\n\t\n}\nvoid Gl_Window::draw()\n{\n\tif (!valid())\n\t{\n\t\tglMatrixMode(GL_PROJECTION);\n\t\t\tglLoadIdentity();\n\t\t\tglOrtho( 0,w(),h(),0,0.0f,100.0f);\n\t\t\tglClearColor( 1.0, 1.0, 1.0, 1.0);\n\t\t\tglClearDepth( 0.0f);\n\t}\n\tglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\n\ttest_draw();\n}\nvoid Gl_Window::left_mouse_button_down()\n{\n\tfor ( int i=0; i<tsize; i++)\n\t{\n\t\tconst short ALW=10;\n\t\tshort dx=curx-target[i].x;\n\t\tshort dy=cury-target[i].y;\n\t\tif ( -ALW<dx && dx<ALW)\n\t\tif ( -ALW<dy && dy<ALW)\n\t\t{\n\t\t\tcur_drag=i;\n\t\t\treturn;\n\t\t}\n\t}\n\tcur_drag=-1;\n};\nvoid Gl_Window::left_mouse_button_up()\n{\n};\nvoid Gl_Window::leftclick_drag()\n{\n\tif ( target)\n\tif ( cur_drag >=0 && cur_drag< tsize)\n\t{\n\t\ttarget[cur_drag].x=curx;\n\t\ttarget[cur_drag].y=cury;\n\t\tthis->redraw();\n\t}\n};\nint Gl_Window::handle(int event)\n{\n\tswitch(event)\n\t{\n\t\tcase FL_PUSH:\n\t\tcase FL_RELEASE:\n\t\tcase FL_DRAG:\n\t\tcase FL_MOVE:\n\t\t\tcurx = Fl::event_x();\n\t\t\tcury = Fl::event_y();\n\t\t\tswitch(event)\n\t\t\t{\n\t\t\t\tcase FL_PUSH:\n\t\t\t\t\tif ( Fl::event_button() == FL_LEFT_MOUSE)\n\t\t\t\t\t\tleft_mouse_button_down();\n\t\t\t\tcase FL_RELEASE:\n\t\t\t\t\tif ( Fl::event_button() == FL_LEFT_MOUSE)\n\t\t\t\t\t\tleft_mouse_button_up();\n\t\t\t\tcase FL_DRAG:\n\t\t\t\t\tif ( (Fl::event_state()&FL_BUTTON1)==FL_BUTTON1)\n\t\t\t\t\t\tleftclick_drag();\n\t\t\t\tcase FL_MOVE:\n\t\t\t\t\t;\n\t\t\t}\n\t\t\tlastx=curx;\n\t\t\tlasty=cury;\n\t\treturn 1;\n\t\t\n\t\tdefault:\n\t\treturn Fl_Window::handle(event);\n\t} //end of switch(event)\n};\n"
  },
  {
    "path": "cpp/vaser/agg_curve4.cpp",
    "content": "//-----------------------------------------------------------------------\n// The Anti-Grain Geometry Project\n// A high quality rendering engine for C++\n// http://antigrain.com\n// \n// Anti-Grain Geometry has dual licensing model. The Modified BSD \n// License was first added in version v2.4 just for convenience.\n// It is a simple, permissive non-copyleft free software license, \n// compatible with the GNU GPL. It's well proven and recognizable.\n// See http://www.fsf.org/licensing/licenses/index_html#ModifiedBSD\n// for details.\n// \n// Note that the Modified BSD license DOES NOT restrict your rights \n// if you choose the Anti-Grain Geometry Public License.\n// \n// Anti-Grain Geometry Public License\n// ====================================================\n// \n// Anti-Grain Geometry - Version 2.4 \n// Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) \n// \n// Permission to copy, use, modify, sell and distribute this software \n// is granted provided this copyright notice appears in all copies. \n// This software is provided \"as is\" without express or implied\n// warranty, and with no claim as to its suitability for any purpose.\n// \n// \n// \n// Modified BSD License\n// ====================================================\n// Anti-Grain Geometry - Version 2.4 \n// Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) \n// \n// Redistribution and use in source and binary forms, with or without \n// modification, are permitted provided that the following conditions \n// are met:\n// \n//   1. Redistributions of source code must retain the above copyright \n//      notice, this list of conditions and the following disclaimer. \n// \n//   2. Redistributions in binary form must reproduce the above copyright\n//      notice, this list of conditions and the following disclaimer in \n//      the documentation and/or other materials provided with the \n//      distribution. \n// \n//   3. The name of the author may not be used to endorse or promote \n//      products derived from this software without specific prior \n//      written permission. \n// \n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR \n// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \n// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE \n// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, \n// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \n// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR \n// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) \n// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING \n// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \n// POSSIBILITY OF SUCH DAMAGE.\n//\n//-----------------------------------------------------------------------\n// Contact: mcseem@antigrain.com\n//          mcseemagg@yahoo.com\n//          http://www.antigrain.com\n//-----------------------------------------------------------------------\n\n#include <math.h>\n#ifndef M_PI\n\t#define M_PI 3.141592653589793238462643\n#endif\n\ndouble calc_sq_distance(double x1, double y1, double x2, double y2)\n{\n\tdouble dx = x2-x1;\n\tdouble dy = y2-y1;\n\treturn dx * dx + dy * dy;\n}\n\nvoid recursive_bezier(   double x1, double y1, \n\t\t\t\tdouble x2, double y2, \n\t\t\t\tdouble x3, double y3, \n\t\t\t\tdouble x4, double y4,\n\t\t\t\tunsigned level,\n\t\t\t\tdouble m_angle_tolerance,\n\t\t\t\tdouble m_cusp_limit,\n\t\t\t\tdouble m_distance_tolerance_square,\n\t\t\t\tvoid (*add_point)(void*,double,double),\n\t\t\t\tvoid* obj )\n{\n\tconst double curve_distance_epsilon                  = 1e-30;\n\tconst double curve_collinearity_epsilon              = 1e-30;\n\tconst double curve_angle_tolerance_epsilon           = 0.01;\n\tconst int curve_recursion_limit = 32;\n\n\tif(level > curve_recursion_limit) \n\t{\n\t\treturn;\n\t}\n\n\t// Calculate all the mid-points of the line segments\n\t//----------------------\n\tdouble x12   = (x1 + x2) / 2;\n\tdouble y12   = (y1 + y2) / 2;\n\tdouble x23   = (x2 + x3) / 2;\n\tdouble y23   = (y2 + y3) / 2;\n\tdouble x34   = (x3 + x4) / 2;\n\tdouble y34   = (y3 + y4) / 2;\n\tdouble x123  = (x12 + x23) / 2;\n\tdouble y123  = (y12 + y23) / 2;\n\tdouble x234  = (x23 + x34) / 2;\n\tdouble y234  = (y23 + y34) / 2;\n\tdouble x1234 = (x123 + x234) / 2;\n\tdouble y1234 = (y123 + y234) / 2;\n\n\t// Try to approximate the full cubic curve by a single straight line\n\t//------------------\n\tdouble dx = x4-x1;\n\tdouble dy = y4-y1;\n\n\tdouble d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx));\n\tdouble d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx));\n\tdouble da1, da2, k;\n\n\tswitch((int(d2 > curve_collinearity_epsilon) << 1) +\n\tint(d3 > curve_collinearity_epsilon))\n\t{\n\t\tcase 0:\n\t\t\t// All collinear OR p1==p4\n\t\t\t//----------------------\n\t\t\tk = dx*dx + dy*dy;\n\t\t\tif(k == 0)\n\t\t\t{\n\t\t\t\td2 = calc_sq_distance(x1, y1, x2, y2);\n\t\t\t\td3 = calc_sq_distance(x4, y4, x3, y3);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tk   = 1 / k;\n\t\t\t\tda1 = x2 - x1;\n\t\t\t\tda2 = y2 - y1;\n\t\t\t\td2  = k * (da1*dx + da2*dy);\n\t\t\t\tda1 = x3 - x1;\n\t\t\t\tda2 = y3 - y1;\n\t\t\t\td3  = k * (da1*dx + da2*dy);\n\t\t\t\tif(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1)\n\t\t\t\t{\n\t\t\t\t\t// Simple collinear case, 1---2---3---4\n\t\t\t\t\t// We can leave just two endpoints\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1);\n\t\t\t\telse if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4);\n\t\t\t\telse             d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);\n\n\t\t\t\tif(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1);\n\t\t\t\telse if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4);\n\t\t\t\telse             d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);\n\t\t\t}\n\t\t\tif(d2 > d3)\n\t\t\t{\n\t\t\t\tif(d2 < m_distance_tolerance_square)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x2,y2);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif(d3 < m_distance_tolerance_square)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x3,y3);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\tbreak;\n\n\t\tcase 1:\n\t\t\t// p1,p2,p4 are collinear, p3 is significant\n\t\t\t//----------------------\n\t\t\tif(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy))\n\t\t\t{\n\t\t\t\tif(m_angle_tolerance < curve_angle_tolerance_epsilon)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x23,y23);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Angle Condition\n\t\t\t\t//----------------------\n\t\t\t\tda1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2));\n\t\t\t\tif(da1 >= M_PI) da1 = 2*M_PI - da1;\n\n\t\t\t\tif(da1 < m_angle_tolerance)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x2, y2);\n\t\t\t\t\tadd_point(obj, x3, y3);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif(m_cusp_limit != 0.0)\n\t\t\t\t{\n\t\t\t\t\tif(da1 > m_cusp_limit)\n\t\t\t\t\t{\n\t\t\t\t\t\tadd_point(obj, x3, y3);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tbreak;\n\n\t\tcase 2:\n\t\t\t// p1,p3,p4 are collinear, p2 is significant\n\t\t\t//----------------------\n\t\t\tif(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy))\n\t\t\t{\n\t\t\t\tif(m_angle_tolerance < curve_angle_tolerance_epsilon)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x23, y23);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Angle Condition\n\t\t\t\t//----------------------\n\t\t\t\tda1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));\n\t\t\t\tif(da1 >= M_PI) da1 = 2*M_PI - da1;\n\n\t\t\t\tif(da1 < m_angle_tolerance)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x2, y2);\n\t\t\t\t\tadd_point(obj, x3, y3);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif(m_cusp_limit != 0.0)\n\t\t\t\t{\n\t\t\t\t\tif(da1 > m_cusp_limit)\n\t\t\t\t\t{\n\t\t\t\t\t\tadd_point(obj, x2, y2);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tbreak;\n\n\t\tcase 3: \n\t\t\t// Regular case\n\t\t\t//-----------------\n\t\t\tif((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy))\n\t\t\t{\n\t\t\t\t// If the curvature doesn't exceed the distance_tolerance value\n\t\t\t\t// we tend to finish subdivisions.\n\t\t\t\t//----------------------\n\t\t\t\tif(m_angle_tolerance < curve_angle_tolerance_epsilon)\n\t\t\t\t{\n\t\t\t\t\tadd_point(obj, x23, y23);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Angle & Cusp Condition\n\t\t\t\t//----------------------\n\t\t\t\tk   = atan2(y3 - y2, x3 - x2);\n\t\t\t\tda1 = fabs(k - atan2(y2 - y1, x2 - x1));\n\t\t\t\tda2 = fabs(atan2(y4 - y3, x4 - x3) - k);\n\t\t\t\tif(da1 >= M_PI) da1 = 2*M_PI - da1;\n\t\t\t\tif(da2 >= M_PI) da2 = 2*M_PI - da2;\n\n\t\t\t\tif(da1 + da2 < m_angle_tolerance)\n\t\t\t\t{\n\t\t\t\t\t// Finally we can stop the recursion\n\t\t\t\t\t//----------------------\n\t\t\t\t\tadd_point(obj, x23, y23);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif(m_cusp_limit != 0.0)\n\t\t\t\t{\n\t\t\t\t\tif(da1 > m_cusp_limit)\n\t\t\t\t\t{\n\t\t\t\t\t\tadd_point(obj, x2, y2);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif(da2 > m_cusp_limit)\n\t\t\t\t\t{\n\t\t\t\t\t\tadd_point(obj, x3, y3);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tbreak;\n\t}\n\n\t// Continue subdivision\n\t//----------------------\n\trecursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1,\n\t\tm_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,\n\t\tadd_point, obj); \n\trecursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1,\n\t\tm_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,\n\t\tadd_point, obj); \n}\n\nint curve4_div(double x1, double y1, \n\t\tdouble x2, double y2, \n\t\tdouble x3, double y3,\n\t\tdouble x4, double y4,\n\t\tdouble m_approximation_scale,\n\t\tdouble m_angle_tolerance,\n\t\tdouble m_cusp_limit,\n\t\tvoid (*add_point)(void*,double,double),\n\t\tvoid* obj )\n{\n\tdouble m_distance_tolerance_square = 0.5 / m_approximation_scale;\n\tm_distance_tolerance_square *= m_distance_tolerance_square;\n\tint m_count = 0;\n\tadd_point(obj, x1, y1);\n\trecursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0,\n\t\tm_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,\n\t\tadd_point, obj);\n\tadd_point(obj, x4, y4);\n}\n\n#undef M_PI\n"
  },
  {
    "path": "cpp/vaser/backend.h",
    "content": "class backend\n{\npublic:\n\tstatic void vah_draw(vertex_array_holder& vah);\n\tstatic void polyline( const Vec2*, Color, double W, int length, const polyline_opt*); //constant color and weight\n};\n"
  },
  {
    "path": "cpp/vaser/color.h",
    "content": "float& Color_get( Color& C, int index)\n{\n\tswitch (index)\n\t{\n\t\tcase 0: return C.r;\n\t\tcase 1: return C.g;\n\t\tcase 2: return C.b;\n\t\tdefault:return C.r;\n\t}\n}\nbool Color_valid_range(float t)\n{\n\treturn t>=0.0f && t<=1.0f;\n}\nColor Color_between( const Color& A, const Color& B, float t=0.5f)\n{\n\tif ( t<0.0f) t = 0.0f;\n\tif ( t>1.0f) t = 1.0f;\n\t\n\tfloat kt = 1.0f - t;\n\tColor C =\n\t{\n\t\tA.r *kt + B.r *t,\n\t\tA.g *kt + B.g *t,\n\t\tA.b *kt + B.b *t,\n\t\tA.a *kt + B.a *t\n\t};\n\treturn C;\n}\nvoid sRGBtolinear( Color& C, bool exact=false)\n{\t//de-Gamma 2.2\n\t//from: http://www.xsi-blog.com/archives/133\n\tif (exact)\n\t{\n\t\tfor ( int i=0; i<3; i++)\n\t\t{\n\t\t\tfloat& cc = Color_get( C,i);\n\t\t\t\n\t\t\tif ( cc > 0.04045)\n\t\t\t\tcc = pow( (cc+0.055)/1.055, 2.4);\n\t\t\telse\n\t\t\t\tcc /= 12.92;\n\t\t}\n\t}\n\telse\n\t{\t//approximate\n\t\tfor ( int i=0; i<3; i++)\n\t\t{\n\t\t\tfloat& cc = Color_get( C,i);\n\t\t\tcc = pow(cc,2.2);\n\t\t}\n\t}\n}\nvoid lineartosRGB( Color& C, bool exact=false)\n{\t//Gamma 2.2\n\tif (exact)\n\t{\n\t\tfor ( int i=0; i<3; i++)\n\t\t{\n\t\t\tfloat& cc = Color_get( C,i);\n\t\t\tif ( cc > 0.0031308)\n\t\t\t\tcc = 1.055 * pow(cc,1.0/2.4) - 0.055;\n\t\t\telse\n\t\t\t\tcc *= 12.92;\n\t\t}\n\t}\n\telse\n\t{\t//approximate\n\t\tfor ( int i=0; i<3; i++)\n\t\t{\n\t\t\tfloat& cc = Color_get( C,i);\n\t\t\tcc = pow(cc,1.0/2.2);\n\t\t}\n\t}\n}\nfloat color_max( float r,float g,float b)\n{\n\treturn r>g? (g>b?r:(r>b?r:b)) : (g>b?g:b);\n}\nfloat color_min( float r, float g, float b)\n{\n\treturn -color_max( -r,-g,-b);\n}\nvoid RGBtoHSV( float r, float g, float b, float *h, float *s, float *v )\n{\t//from: http://www.cs.rit.edu/~ncs/color/t_convert.html\n\t// r,g,b values are from 0 to 1\n\t// h = [0,360], s = [0,1], v = [0,1]\n\t//\t\tif s == 0, then h = -1 (undefined)\n\tfloat min, max, delta;\n\tmin = color_min( r, g, b );\n\tmax = color_max( r, g, b );\n\t*v = max;\t\t\t\t// v\n\tdelta = max - min;\n\tif( max != 0 )\n\t\t*s = delta / max;\t\t// s\n\telse {\n\t\t// r = g = b = 0\t\t// s = 0, v is undefined\n\t\t*s = 0;\n\t\t*h = -1;\n\t\treturn;\n\t}\n\tif( r == max )\n\t\t*h = ( g - b ) / delta;\t\t// between yellow & magenta\n\telse if( g == max )\n\t\t*h = 2 + ( b - r ) / delta;\t// between cyan & yellow\n\telse\n\t\t*h = 4 + ( r - g ) / delta;\t// between magenta & cyan\n\t*h *= 60;\t\t\t\t// degrees\n\tif( *h < 0 )\n\t\t*h += 360;\n}\nvoid HSVtoRGB( float *r, float *g, float *b, float h, float s, float v )\n{\n\tint i;\n\tfloat f, p, q, t;\n\tif( s == 0 ) {\n\t\t// achromatic (grey)\n\t\t*r = *g = *b = v;\n\t\treturn;\n\t}\n\th /= 60;\t\t\t// sector 0 to 5\n\ti = floor( h );\n\tf = h - i;\t\t\t// factorial part of h\n\tp = v * ( 1 - s );\n\tq = v * ( 1 - s * f );\n\tt = v * ( 1 - s * ( 1 - f ) );\n\tswitch( i ){\n\t\tcase 0:\n\t\t\t*r = v; *g = t; *b = p;\t\tbreak;\n\t\tcase 1:\n\t\t\t*r = q; *g = v;\t*b = p;\t\tbreak;\n\t\tcase 2:\n\t\t\t*r = p;\t*g = v;\t*b = t;\t\tbreak;\n\t\tcase 3:\n\t\t\t*r = p;\t*g = q;\t*b = v;\t\tbreak;\n\t\tcase 4:\n\t\t\t*r = t;\t*g = p;\t*b = v;\t\tbreak;\n\t\tdefault:\t\t// case 5:\n\t\t\t*r = v;\t*g = p;\t*b = q;\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "cpp/vaser/curve.cpp",
    "content": "namespace VASErin\n{\t//VASEr internal namespace\n\nclass polyline_buffer\n{\npublic:\n\tstd::vector<Vec2>   P;\n\tstd::vector<Color>  C;\n\tstd::vector<double> W;\n\tstd::vector<double> L; //length along polyline\n\tint N;\n\tdouble path_length; //total segment length\n\n\tpolyline_buffer(): P(),C(),W(),L()\n\t{\n\t\tN=0;\n\t\tpath_length=0.0;\n\t\tL.push_back(0.0);\n\t}\n\tvoid point(double x, double y)\n\t{\n\t\tVec2 V={x,y};\n\t\taddvertex(V);\n\t}\n\tvoid point(Vec2 V)\n\t{\n\t\taddvertex(V);\n\t}\n\tstatic void point_cb(void* obj, double x, double y)\n\t{\n\t\tpolyline_buffer* This = (polyline_buffer*)obj;\n\t\tVec2 V={x,y};\n\t\tThis->addvertex(V);\n\t}\n\tvoid vertex(Vec2 V, Color cc)\n\t{\n\t\taddvertex(V,&cc);\n\t}\n\tvoid vertex(Vec2 V, Color cc, double ww)\n\t{\n\t\taddvertex(V,& cc,ww);\n\t}\n\tvoid set_color(Color cc)\n\t{\n\t\tif( !C.size())\n\t\t\tC.push_back(cc);\n\t\telse\n\t\t\tC.back()=cc;\n\t}\n\tvoid set_weight(double ww)\n\t{\n\t\tif( !W.size())\n\t\t\tW.push_back(ww);\n\t\telse\n\t\t\tW.back()=ww;\n\t}\n\tvoid gradient( const gradient* grad)\n\t{\n\t\tgradient_apply(grad,&C[0],&W[0],&L[0],N,path_length);\n\t}\n\tvoid draw(const polyline_opt* options)\n\t{\n\t\tif( !N) return;\n\t\tpolyline_inopt in_options={0};\n\t\tin_options.segment_length = &L[0];\n\t\tpolyline( &P[0],&C[0],&W[0],N,options,&in_options);\n\t}\nprivate:\n\tbool addvertex(const Vec2& V, const Color* cc=0, double ww=0.0)\n\t{\n\t\tif( N && P[N-1].x==V.x && P[N-1].y==V.y)\n\t\t\treturn false; //duplicate\n\t\telse\n\t\t{\n\t\t\t//point\n\t\t\tP.push_back(V);\n\t\t\tif( N>0)\n\t\t\t{\n\t\t\t\tdouble len = (Point(V)-Point(P[N-1])).length();\n\t\t\t\tpath_length += len;\n\t\t\t\tL.push_back(len);\n\t\t\t}\n\t\t\t//color\n\t\t\tif( cc)\n\t\t\t\tC.push_back(*cc);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif( !C.size())\n\t\t\t\t\tC.push_back(default_color);\n\t\t\t\telse\n\t\t\t\t\tC.push_back(C.back());\n\t\t\t}\n\t\t\t//weight\n\t\t\tif( ww)\n\t\t\t\tW.push_back(ww);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif( !W.size())\n\t\t\t\t\tW.push_back(default_weight);\n\t\t\t\telse\n\t\t\t\t\tW.push_back(W.back());\n\t\t\t}\n\t\t\t//finally\n\t\t\tN++;\n\t\t\treturn true;\n\t\t}\n\t}\n};\n\nstruct polybezier_inopt\n{\n\tbool evaluate_only;\n\tpolyline_buffer* target;\n};\n\nvoid polybezier( const Vec2* P, const gradient* grad, int length, const polybezier_opt* options, const polybezier_inopt* in_options)\n{\n\tpolybezier_opt opt={0};\n\tpolybezier_inopt inopt={0};\n\tpolyline_opt poly={0};\n\tpoly.joint = PLJ_bevel;\n\tif( options) opt = *options;\n\tif( in_options) inopt = *in_options;\n\tif( !opt.poly) opt.poly = &poly;\n\n\tpolyline_buffer _buf;\n\tpolyline_buffer& buf = _buf;\n\tif( inopt.target)\n\t\tbuf = *inopt.target;\n\n\tconst double BZ_default_approximation_scale = 0.5;\n\tconst double BZ_default_angle_tolerance = 15.0/180*vaser_pi;\n\tconst double BZ_default_cusp_limit = 5.0;\n\n\tfor( int i=0; i<length-3; i+=3)\n\t{\n\t\tcurve4_div(\n\t\t\tP[i+0].x,P[i+0].y,\n\t\t\tP[i+1].x,P[i+1].y,\n\t\t\tP[i+2].x,P[i+2].y,\n\t\t\tP[i+3].x,P[i+3].y,\n\t\t\tBZ_default_approximation_scale,\n\t\t\tBZ_default_angle_tolerance,\n\t\t\tBZ_default_cusp_limit,\n\t\t\tpolyline_buffer::point_cb,\n\t\t\t&buf);\n\t}\n\tif( grad)\n\t\tbuf.gradient(grad);\n\tif( !inopt.evaluate_only)\n\t\tbuf.draw(opt.poly);\n}\n\n} //sub namespace VASErin\n\n//export implementations\n\nvoid polybezier( const Vec2* P, const gradient* grad, int length, const polybezier_opt* options)\n{\n\tVASErin::polybezier(P,grad,length,options,0);\n}\n\nvoid polybezier( const Vec2* P, Color cc, double ww, int length, const polybezier_opt* options)\n{\n\tgradient grad = {0};\n\tgradient_stop stop[2] = {0};\n\tgrad.stops = stop;\n\tgrad.length = 2;\n\tstop[0].type = GS_rgba; stop[0].color = cc;\n\tstop[1].type = GS_weight; stop[1].weight = ww;\n\tVASErin::polybezier(P,&grad,length,options,0);\n}\n"
  },
  {
    "path": "cpp/vaser/gradient.cpp",
    "content": "namespace VASErin\n{\t//VASEr internal namespace\n\ndouble grad_getstep(double A,double B,double t,double T)\n{\n\treturn ((T-t)*A + t*B) / T;\n}\n\nvoid gradient_apply(const gradient* gradp, Color* C, double* W, const double* L, int length, double path_length)\n{\n\tif( !gradp) return;\n\tif( !gradp->stops || !gradp->length) return;\n\tconst gradient& grad = *gradp;\n\tconst gradient_stop* stop = grad.stops;\n\n\t//current stops\n\tint las_c=0, las_a=0, las_w=0, //last\n\t\tcur_c=0, cur_a=0, cur_w=0, //current\n\t\tnex_c=0, nex_a=0, nex_w=0; //next\n\tdouble length_along=0.0;\n\tif( grad.length>1)\n\tfor( int i=0; i<length; i++)\n\t{\n\t\tlength_along += L[i];\n\t\tdouble p;\n\t\tif( grad.unit==GD_ratio)\n\t\t\tp = length_along/path_length;\n\t\telse if ( grad.unit==GD_length)\n\t\t\tp = length_along;\n\n\t\t{\t//lookup for cur\n\t\t\tfor( nex_w=cur_w; nex_w < grad.length; nex_w++)\n\t\t\t\tif( stop[nex_w].type==GS_weight && p <= stop[nex_w].t)\n\t\t\t\t\t{ cur_w=nex_w; break;}\n\t\t\tfor( nex_c=cur_c; nex_c < grad.length; nex_c++)\n\t\t\t\tif( (stop[nex_c].type==GS_rgba || stop[nex_c].type==GS_rgb ) &&\n\t\t\t\t\tp <= stop[nex_c].t)\n\t\t\t\t\t{ cur_c=nex_c; break;}\n\t\t\tfor( nex_a=cur_a; nex_a < grad.length; nex_a++)\n\t\t\t\tif( (stop[nex_a].type==GS_rgba || stop[nex_a].type==GS_alpha ) &&\n\t\t\t\t\tp <= stop[nex_a].t)\n\t\t\t\t\t{ cur_a=nex_a; break;}\n\t\t\t//look for las\n\t\t\tfor( nex_w=cur_w; nex_w >= 0; nex_w--)\n\t\t\t\tif( stop[nex_w].type==GS_weight && p >= stop[nex_w].t)\n\t\t\t\t\t{ las_w=nex_w; break;}\n\t\t\tfor( nex_c=cur_c; nex_c >= 0; nex_c--)\n\t\t\t\tif( (stop[nex_c].type==GS_rgba || stop[nex_c].type==GS_rgb ) &&\n\t\t\t\t\tp >= stop[nex_c].t)\n\t\t\t\t\t{ las_c=nex_c; break;}\n\t\t\tfor( nex_a=cur_a; nex_a >= 0; nex_a--)\n\t\t\t\tif( (stop[nex_a].type==GS_rgba || stop[nex_a].type==GS_alpha ) &&\n\t\t\t\t\tp >= stop[nex_a].t)\n\t\t\t\t\t{ las_a=nex_a; break;}\n\t\t}\n\t\t#define SC(x) stop[x].color\n\t\t#define Sw(x) stop[x].weight\n\t\t#define Sa(x) stop[x].color.a\n\t\t#define St(x) stop[x].t\n\t\tif ( cur_c==las_c)\n\t\t\tC[i] = SC(cur_c);\n\t\telse\n\t\t\tC[i] = Color_between(SC(las_c),SC(cur_c), (p-St(las_c))/(St(cur_c)-St(las_c)));\n\t\tif ( cur_w==las_w)\n\t\t\tW[i] = Sw(cur_w);\n\t\telse\n\t\t\tW[i] = grad_getstep(Sw(las_w),Sw(cur_w), p-St(las_w), St(cur_w)-St(las_w));\n\t\tif ( cur_a==las_a)\n\t\t\tC[i].a = Sa(cur_a);\n\t\telse\n\t\t\tC[i].a = grad_getstep(Sa(las_a),Sa(cur_a), p-St(las_a), St(cur_a)-St(las_a));\n\t\t#undef SC\n\t\t#undef Sw\n\t\t#undef Sa\n\t\t#undef St\n\t}\n}\n\n} //sub namespace VASErin\n"
  },
  {
    "path": "cpp/vaser/opengl.cpp",
    "content": "/* OpenGL 1.1 renderer and backend */\n\nnamespace VASErin\n{\t//VASEr internal namespace\n\nvoid backend::vah_draw(vertex_array_holder& vah)\n{\n\tif ( vah.count > 0) //save some effort\n\t{\n\t\tglVertexPointer(2, GL_FLOAT, 0, &vah.vert[0]);\n\t\tglColorPointer (4, GL_FLOAT, 0, &vah.color[0]);\n\t\tglDrawArrays (vah.glmode, 0, vah.count);\n\t}\n}\n\nvoid backend::polyline( const Vec2* P, Color C, double W, int length, const polyline_opt*) //constant color and weight\n{\n\tint type=0;\n\tif( sizeof(Vec2)==16)\n\t\ttype = GL_DOUBLE;\n\telse if( sizeof (Vec2)==8)\n\t\ttype = GL_FLOAT;\n\n\tglColor4f(C.r,C.g,C.b,C.a);\n\tglLineWidth(W);\n\tif( type)\n\t{\n\t\tglEnableClientState(GL_VERTEX_ARRAY);\n\t\tglDisableClientState(GL_COLOR_ARRAY);\n\t\tglVertexPointer(2, type, 0, P);\n\t\tglDrawArrays(GL_LINE_STRIP, 0, length);\n\t\tglEnableClientState(GL_COLOR_ARRAY);\n\t}\n\telse\n\t{\n\t\tglBegin(GL_LINE_STRIP);\n\t\tfor( int i=0; i<length; i++)\n\t\t\tglVertex2f(P[i].x,P[i].y);\n\t\tglEnd();\n\t}\n\tglLineWidth(1);\n}\n\nvoid swap_pixel(unsigned char* img, int w, int x1,int y1, int x2,int y2)\n{\n\tconst int b=4;\n\tunsigned char R = img[y1*w*b+x1*b];\n\tunsigned char G = img[y1*w*b+x1*b+1];\n\tunsigned char B = img[y1*w*b+x1*b+2];\n\tunsigned char A = img[y1*w*b+x1*b+3];\n\t\n\timg[y1*w*b+x1*b] = img[y2*w*b+x2*b];\n\timg[y1*w*b+x1*b+1] = img[y2*w*b+x2*b+1];\n\timg[y1*w*b+x1*b+2] = img[y2*w*b+x2*b+2];\n\timg[y1*w*b+x1*b+3] = img[y2*w*b+x2*b+3];\n\t\n\timg[y2*w*b+x2*b] = R;\n\timg[y2*w*b+x2*b+1] = G;\n\timg[y2*w*b+x2*b+2] = B;\n\timg[y2*w*b+x2*b+3] = A;\n}\n\n} //sub namespace VASErin\n\nvoid renderer::init()\n{\n}\n\nvoid renderer::before()\n{\n\tglEnableClientState(GL_VERTEX_ARRAY);\n\tglEnableClientState(GL_COLOR_ARRAY);\n\t\n\tglDisableClientState(GL_EDGE_FLAG_ARRAY);\n\t//glDisableClientState(GL_FOG_COORD_ARRAY);\n\tglDisableClientState(GL_INDEX_ARRAY);\n\tglDisableClientState(GL_NORMAL_ARRAY);\n\t//glDisableClientState(GL_SECONDARY_COLOR_ARRAY);\n\tglDisableClientState(GL_TEXTURE_COORD_ARRAY);\n\t\n\tglEnable(GL_BLEND);\n\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n}\nvoid renderer::after()\n{\n\tglDisableClientState(GL_VERTEX_ARRAY);\n\tglDisableClientState(GL_COLOR_ARRAY);\n\tglDisable(GL_BLEND);\n}\nImage renderer::get_image()\n{\n\tint res[4];\n\tglGetIntegerv(GL_VIEWPORT,res);\n\tImage im={0};\n\tim.width=res[2];\n\tim.height=res[3];\n\tim.buf=(unsigned char*)malloc(im.width*im.height*4);\n\tif( !im.buf)\n\t\treturn im;\n\tglReadPixels(0, 0, im.width, im.height, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)im.buf);\n\t//flip horizontally\n\tfor( int i=0; i<im.height/2; i++)\n\t\tfor( int j=0; j<im.width; j++)\n\t\t\tVASErin::swap_pixel(im.buf,im.width,j,i,j,im.height-i-1);\n\treturn im;\n}\n\n"
  },
  {
    "path": "cpp/vaser/point.h",
    "content": "class Point : public Vec2\n{\npublic:\n\tPoint() { clear();}\n\tPoint(const Vec2& P) { set(P.x,P.y);}\n\tPoint(const Point& P) { set(P.x,P.y);}\n\tPoint(double X, double Y) { set(X,Y);}\n\t\n\tvoid clear()\t\t\t{ x=0.0; y=0.0;}\n\tvoid set(double X, double Y)\t{ x=X;   y=Y;}\n\t\n\tVec2 vec() {\n\t\tVec2 V;\n\t\tV.x = x; V.y = y;\n\t\treturn V;\n\t}\n\t\n\t//attributes\n\tdouble length() const\n\t{\n\t\treturn sqrt(x*x+y*y);\n\t}\n\tdouble slope() const\n\t{\n\t\treturn y/x;\n\t}\n\tstatic double signed_area(const Point& P1, const Point& P2, const Point& P3)\n\t{\n\t\treturn (P2.x-P1.x)*(P3.y-P1.y) - (P3.x-P1.x)*(P2.y-P1.y);\n\t}\n\t\n\t//vector operators\t\n\tPoint operator+(const Point &b) const\n\t{\n\t\treturn Point( x+b.x, y+b.y);\n\t}\n\tPoint operator-() const\n\t{\n\t\treturn Point( -x, -y);\n\t}\n\tPoint operator-(const Point &b) const\n\t{\n\t\treturn Point( x-b.x, y-b.y);\n\t}\n\tPoint operator*(double k) const\n\t{\n\t\treturn Point( x*k, y*k);\n\t}\n\t\n\tPoint& operator=(const Point& b) {\n\t\tx = b.x; y = b.y;\n\t\treturn *this;\n\t}\n\tPoint& operator+=(const Point& b)\n\t{\n\t\tx += b.x; y += b.y;\n\t\treturn *this;\n\t}\n\tPoint& operator-=(const Point& b)\n\t{\n\t\tx -= b.x; y -= b.y;\n\t\treturn *this;\n\t}\n\tPoint& operator*=(const double k)\n\t{\n\t\tx *= k; y *= k;\n\t\treturn *this;\n\t}\n\tPoint& operator/=(const double k)\n\t{\n\t\tx /= k; y /= k;\n\t\treturn *this;\n\t}\n\t\n\tstatic void dot( const Point& a, const Point& b, Point& o) //dot product: o = a dot b\n\t{\n\t\to.x = a.x * b.x;\n\t\to.y = a.y * b.y;\n\t}\n\tPoint dot_prod( const Point& b) const //return dot product\n\t{\n\t\treturn Point( x*b.x, y*b.y);\n\t}\n\t\n\t//self operations\n\tvoid opposite()\n\t{\n\t\tx = -x;\n\t\ty = -y;\n\t}\n\tvoid opposite_of( const Point& a)\n\t{\n\t\tx = -a.x;\n\t\ty = -a.y;\n\t}\n\tdouble normalize()\n\t{\n\t\tdouble L = length();\n\t\tif ( L > vaser_min_alw)\n\t\t{\n\t\t\tx /= L; y /= L;\n\t\t}\n\t\treturn L;\n\t}\n\tvoid perpen() //perpendicular: anti-clockwise 90 degrees\n\t{\n\t\tdouble y_value=y;\n\t\ty=x;\n\t\tx=-y_value;\n\t}\n\tvoid follow_signs( const Point& a)\n\t{\n\t\tif ( (x>0) != (a.x>0))\tx = -x;\n\t\tif ( (y>0) != (a.y>0))\ty = -y;\n\t}\n\tvoid follow_magnitude( const Point& a);\n\tvoid follow_direction( const Point& a);\n\t\n\t//judgements\n\tstatic inline bool negligible( double M)\n\t{\n\t\treturn -vaser_min_alw < M && M < vaser_min_alw;\n\t}\n\tbool negligible() const\n\t{\n\t\treturn Point::negligible(x) && Point::negligible(y);\n\t}\n\tbool non_negligible() const\n\t{\n\t\treturn !negligible();\n\t}\n\tbool is_zero() const\n\t{\n\t\treturn x==0.0 && y==0.0;\n\t}\n\tbool non_zero() const\n\t{\n\t\treturn !is_zero();\n\t}\n\tstatic bool intersecting( const Point& A, const Point& B,\n\t\tconst Point& C, const Point& D)\n\t{\t//return true if AB intersects CD\n\t\treturn signed_area(A,B,C)>0 != signed_area(A,B,D)>0;\n\t}\n\t\n\t//operations require 2 input points\n\tstatic double distance_squared( const Point& A, const Point& B)\n\t{\n\t\tdouble dx=A.x-B.x;\n\t\tdouble dy=A.y-B.y;\n\t\treturn (dx*dx+dy*dy);\n\t}\n\tstatic inline double distance( const Point& A, const Point& B)\n\t{\n\t\treturn sqrt( distance_squared(A,B));\n\t}\n\tstatic Point midpoint( const Point& A, const Point& B)\n\t{\n\t\treturn (A+B)*0.5;\n\t}\n\tstatic bool opposite_quadrant( const Point& P1, const Point& P2)\n\t{\n\t\tchar P1x = P1.x>0? 1:(P1.x<0?-1:0);\n\t\tchar P1y = P1.y>0? 1:(P1.y<0?-1:0);\n\t\tchar P2x = P2.x>0? 1:(P2.x<0?-1:0);\n\t\tchar P2y = P2.y>0? 1:(P2.y<0?-1:0);\n\t\t\n\t\tif ( P1x!=P2x) {\n\t\t\tif ( P1y!=P2y)\n\t\t\t\treturn true;\n\t\t\tif ( P1y==0 || P2y==0)\n\t\t\t\treturn true;\n\t\t}\n\t\tif ( P1y!=P2y) {\n\t\t\tif ( P1x==0 || P2x==0)\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t//operations of 3 points\n\tstatic inline bool anchor_outward_D( Point& V, const Point& b, const Point& c)\n\t{\n\t\treturn (b.x*V.x - c.x*V.x + b.y*V.y - c.y*V.y) > 0;\n\t}\n\tstatic bool anchor_outward( Point& V, const Point& b, const Point& c, bool reverse=false)\n\t{ //put the correct outward vector at V, with V placed on b, comparing distances from c\n\t\tbool determinant = anchor_outward_D ( V,b,c);\n\t\tif ( determinant == (!reverse)) { //when reverse==true, it means inward\n\t\t\t//positive V is the outward vector\n\t\t\treturn false;\n\t\t} else {\n\t\t\t//negative V is the outward vector\n\t\t\tV.x=-V.x;\n\t\t\tV.y=-V.y;\n\t\t\treturn true; //return whether V is changed\n\t\t}\n\t}\n\tstatic void anchor_inward( Point& V, const Point& b, const Point& c)\n\t{\n\t\tanchor_outward( V,b,c,true);\n\t}\n\t\n\t//operations of 4 points\n\tstatic char intersect( const Point& P1, const Point& P2,  //line 1\n\t\t\tconst Point& P3, const Point& P4, //line 2\n\t\t\tPoint& Pout,\t\t\t  //the output point\n\t\t\tdouble* ua_out=0, double* ub_out=0)\n\t{ //Determine the intersection point of two line segments\n\t\tdouble mua,mub;\n\t\tdouble denom,numera,numerb;\n\n\t\tdenom  = (P4.y-P3.y) * (P2.x-P1.x) - (P4.x-P3.x) * (P2.y-P1.y);\n\t\tnumera = (P4.x-P3.x) * (P1.y-P3.y) - (P4.y-P3.y) * (P1.x-P3.x);\n\t\tnumerb = (P2.x-P1.x) * (P1.y-P3.y) - (P2.y-P1.y) * (P1.x-P3.x);\n\n\t\tif( negligible(numera) &&\n\t\t\tnegligible(numerb) &&\n\t\t\tnegligible(denom)) {\n\t\tPout.x = (P1.x + P2.x) * 0.5;\n\t\tPout.y = (P1.y + P2.y) * 0.5;\n\t\treturn 2; //meaning the lines coincide\n\t\t}\n\n\t\tif ( negligible(denom)) {\n\t\t\tPout.x = 0;\n\t\t\tPout.y = 0;\n\t\t\treturn 0; //meaning lines are parallel\n\t\t}\n\n\t\tmua = numera / denom;\n\t\tmub = numerb / denom;\n\t\tif ( ua_out) *ua_out = mua;\n\t\tif ( ub_out) *ub_out = mub;\n\n\t\tPout.x = P1.x + mua * (P2.x - P1.x);\n\t\tPout.y = P1.y + mua * (P2.y - P1.y);\n\t\t\n\t\tbool out1 = mua < 0 || mua > 1;\n\t\tbool out2 = mub < 0 || mub > 1;\n\t\t\n\t\tif ( out1 & out2) {\n\t\t\treturn 5; //the intersection lies outside both segments\n\t\t} else if ( out1) {\n\t\t\treturn 3; //the intersection lies outside segment 1\n\t\t} else if ( out2) {\n\t\t\treturn 4; //the intersection lies outside segment 2\n\t\t} else {\n\t\t\treturn 1; //great\n\t\t}\n\t//http://paulbourke.net/geometry/lineline2d/\n\t}\n}; //end of class Point\n\n/* after all,\n * sizeof(Vec2)=16  sizeof(Point)=16\n * Point is not heavier, just more powerful :)\n*/\n"
  },
  {
    "path": "cpp/vaser/polyline.cpp",
    "content": "namespace VASErin\n{\t//VASEr internal namespace\n\n/*visual testes:\n * A. points (geometry test)\n *  1. arbitrary polyline of only 2 point\n *  2. polylines of 3 points, making arbitrary included angle\n *  3. arbitrary polyline of 4 or more points\n * B. colors\n *  1. different colors on each point\n * C. weight\n *  1. varying weight\n * D. other drawing options\n *  1. feathering\n *  2. different joint types\n *  3. different cap types\n * E. memory test\n *  1. drawing a polyline of 1000 points\n */\n\n/*known visual bugs: ( \",,\" simply means \"go wild\" as it is too hard to describe)\n * 1.  [solved]when 2 segments are exactly at 90 degrees, the succeeding line segment is reflexed.\n * 1.1 [solved]when 2 segments are exactly at 180 degrees,,\n * 2.  [solved]when polyline is dragged, caps seem to have pseudo-podia.\n * 3.  [solved]when 2 segments are exactly horizontal/ vertical, both segments are reflexed.\n * 3.1 [solved]when a segment is exactly horizontal/ vertical, the cap disappears\n * 4.  [solved]when 2 segments make < 5 degrees,,\n * 4.1 [solved]when 2 segments make exactly 0 degree,,\n * 5.  when a segment is shorter than its own width,,\n */\n\n/* const double vaser_actual_PPI = 96.0;\n\tconst double vaser_standard_PPI = 111.94; //the PPI I used for calibration\n */\n\n//critical weight to do approximation rather than real joint processing\nconst double cri_segment_approx=1.6;\n\nvoid determine_t_r ( double w, double& t, double& R)\n{\n\t//efficiency: can cache one set of w,t,R values\n\t// i.e. when a polyline is of uniform thickness, the same w is passed in repeatedly\n\tdouble f=w-static_cast<int>(w);\n\t\n\t/*   */if ( w>=0.0 && w<1.0) {\n\t\tt=0.05; R=0.768;//R=0.48+0.32*f;\n\t} else if ( w>=1.0 && w<2.0) {\n\t\tt=0.05+f*0.33; R=0.768+0.312*f;\n\t} else if ( w>=2.0 && w<3.0){\n\t\tt=0.38+f*0.58; R=1.08;\n\t} else if ( w>=3.0 && w<4.0){\n\t\tt=0.96+f*0.48; R=1.08;\n\t} else if ( w>=4.0 && w<5.0){\n\t\tt=1.44+f*0.46; R=1.08;\n\t} else if ( w>=5.0 && w<6.0){\n\t\tt=1.9+f*0.6; R=1.08;\n\t} else if ( w>=6.0){\n\t\tdouble ff=w-6.0;\n\t\tt=2.5+ff*0.50; R=1.08;\n\t}\n\t\n\t/* //PPI correction\n\tdouble PPI_correction = vaser_standard_PPI / vaser_actual_PPI;\n\tconst double up_bound = 1.6; //max value of w to receive correction\n\tconst double start_falloff = 1.0;\n\tif ( w>0.0 && w<up_bound)\n\t{\t//here we gracefully apply the correction\n\t\t// so that the effect of correction diminishes starting from w=start_falloff\n\t\t//   and completely disappears when w=up_bound\n\t\tdouble correction = 1.0 + (PPI_correction-1.0)*(up_bound-w)/(up_bound-start_falloff);\n\t\tt *= PPI_correction;\n\t\tR *= PPI_correction;\n\t} */\n}\nfloat get_PLJ_round_dangle(float t, float r)\n{\n\tfloat dangle;\n\tfloat sum = t+r;\n\tif ( sum <= 1.44f+1.08f) //w<=4.0, feathering=1.0\n\t\tdangle = 0.6f/(t+r);\n\telse if ( sum <= 3.25f+1.08f) //w<=6.5, feathering=1.0\n\t\tdangle = 2.8f/(t+r);\n\telse\n\t\tdangle = 4.2f/(t+r);\n\treturn dangle;\n}\n\nvoid make_T_R_C( Point& P1, Point& P2, Point* T, Point* R, Point* C,\n\t\t\t\tdouble w, const polyline_opt& opt,\n\t\t\t\tdouble* rr, double* tt, float* dist,\n\t\t\t\tbool seg_mode=false)\n{\n\tdouble t=1.0,r=0.0;\n\tPoint DP=P2-P1;\n\t\n\t//calculate t,r\n\tdetermine_t_r( w,t,r);\n\t\n\tif ( opt.feather && !opt.no_feather_at_core && opt.feathering != 1.0)\n\t\tr *= opt.feathering;\n\telse if ( seg_mode)\n\t{\n\t\t//TODO: handle correctly for hori/vert segments in a polyline\n\t\tif ( Point::negligible(DP.x) && P1.x==(int)P1.x) {\n\t\t\tif ( w>0.0 && w<=1.0) {\n\t\t\t\tt=0.5; r=0.0;\n\t\t\t\tP2.x = P1.x = (int)P1.x+0.5;\n\t\t\t}\n\t\t} else if ( Point::negligible(DP.y) && P1.y==(int)P1.y) {\n\t\t\tif ( w>0.0 && w<=1.0) {\n\t\t\t\tt=0.5; r=0.0;\n\t\t\t\tP2.y = P1.y = (int)P1.y+0.5;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t//output t,r\n\tif (tt) *tt = t;\n\tif (rr) *rr = r;\n\t\n\t//calculate T,R,C\n\tdouble len = DP.normalize();\n\tif (dist) *dist = (float)len;\n\tif (C) *C = DP;\n\tDP.perpen();\n\t\n\tif (T) *T = DP*t;\n\tif (R) *R = DP*r;\n}\n\nvoid same_side_of_line( Point& V, const Point& ref, const Point& a, const Point& b)\n{\n\tdouble sign1 = Point::signed_area( a+ref,a,b);\n\tdouble sign2 = Point::signed_area( a+V,  a,b);\n\tif ( (sign1>=0) != (sign2>=0))\n\t{\n\t\tV.opposite();\n\t}\n}\n\nstruct st_polyline\n//the struct to hold info for anchor_late() to perform triangluation\n{\n\t//for all joints\n\tPoint vP; //vector to intersection point\n\tPoint vR; //fading vector at sharp end\n\t\t//all vP,vR are outward\n\t\n\t//for djoint==PLJ_bevel\n\tPoint T; //core thickness of a line\n\tPoint R; //fading edge of a line\n\tPoint bR; //out stepping vector, same direction as cap\n\tPoint T1,R1; //alternate vectors, same direction as T21\n\t\t//all T,R,T1,R1 are outward\n\t\n\t//for djoint==PLJ_round\n\tfloat t,r;\n\t\n\t//for degeneration case\n\tbool degenT; //core degenerated\n\tbool degenR; //fade degenerated\n\tbool pre_full; //draw the preceding segment in full\n\tPoint PT,PR;\n\tfloat pt; //parameter at intersection\n\tbool R_full_degen;\n\t\n\tchar djoint; //determined joint\n\t\t\t// e.g. originally a joint is PLJ_miter. but it is smaller than critical angle, should then set djoint to PLJ_bevel\n};\n\nstruct st_anchor\n//the struct to hold memory for the working of anchor()\n{\n\tPoint P[3]; //point\n\tColor C[3]; //color\n\tdouble W[3];//weight\n\t\n\tPoint cap_start, cap_end;\n\tst_polyline SL[3];\n\tvertex_array_holder vah;\n};\n\nvoid inner_arc( vertex_array_holder& hold, const Point& P,\n\t\tconst Color& C, const Color& C2,\n\t\tfloat dangle, float angle1, float angle2,\n\t\tfloat r, float r2, bool ignor_ends,\n\t\tPoint* apparent_P)\t//(apparent center) center of fan\n//draw the inner arc between angle1 and angle2 with dangle at each step.\n// -the arc has thickness, r is the outer radius and r2 is the inner radius,\n//    with color C and C2 respectively.\n//    in case when inner radius r2=0.0f, it gives a pie.\n// -when ignor_ends=false, the two edges of the arc lie exactly on angle1\n//    and angle2. when ignor_ends=true, the two edges of the arc do not touch\n//    angle1 or angle2.\n// -P is the mathematical center of the arc.\n// -when apparent_P points to a valid Point (apparent_P != 0), r2 is ignored,\n//    apparent_P is then the apparent origin of the pie.\n// -the result is pushed to hold, in form of a triangle strip\n// -an inner arc is an arc which is always shorter than or equal to a half circumference\n{\n\tconst double& m_pi = vaser_pi;\n\t\n\tbool incremental=true;\n\t\n\tif ( angle2 > angle1)\n\t{\n\t\tif ( angle2-angle1>m_pi)\n\t\t{\n\t\t\tangle2=angle2-2*m_pi;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif ( angle1-angle2>m_pi)\n\t\t{\n\t\t\tangle1=angle1-2*m_pi;\n\t\t}\n\t}\n\tif ( angle1>angle2)\n\t{\n\t\tincremental = false; //means decremental\n\t}\n\t\n\tif ( incremental)\n\t{\n\t\tif ( ignor_ends)\n\t\t{\n\t\t\tint i=0;\n\t\t\tfor ( float a=angle1+dangle; a<angle2; a+=dangle, i++)\n\t\t\t{\n\t\t\t\tfloat x=cos(a);\n\t\t\t\tfloat y=sin(a);\n\t\t\t\t\n\t\t\t\t#define INNER_ARC_PUSH \\\n\t\t\t\t\thold.push( Point(P.x+x*r,P.y-y*r), C);\\\n\t\t\t\t\tif ( !apparent_P)\\\n\t\t\t\t\t\thold.push( Point(P.x+x*r2,P.y-y*r2), C2);\\\n\t\t\t\t\telse\\\n\t\t\t\t\t\thold.push( *apparent_P, C2);\n\t\t\t\t\n\t\t\t\tINNER_ARC_PUSH\n\t\t\t\t\n\t\t\t\tif ( i>100) {\n\t\t\t\t\tDEBUG(\"trapped in loop: inc,ig_end angle1=%.2f, angle2=%.2f, dangle=%.2f\\n\", angle1, angle2, dangle);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//DEBUG( \"steps=%d \",i); fflush(stdout);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i=0;\n\t\t\tfor ( float a=angle1; ; a+=dangle, i++)\n\t\t\t{\n\t\t\t\tif ( a>angle2)\n\t\t\t\t\ta=angle2;\n\t\t\t\t\n\t\t\t\tfloat x=cos(a);\n\t\t\t\tfloat y=sin(a);\n\t\t\t\t\n\t\t\t\tINNER_ARC_PUSH\n\t\t\t\t\n\t\t\t\tif ( a>=angle2)\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tif ( i>100) {\n\t\t\t\t\tDEBUG(\"trapped in loop: inc,end angle1=%.2f, angle2=%.2f, dangle=%.2f\\n\", angle1, angle2, dangle);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse //decremental\n\t{\n\t\tif ( ignor_ends)\n\t\t{\n\t\t\tint i=0;\n\t\t\tfor ( float a=angle1-dangle; a>angle2; a-=dangle, i++)\n\t\t\t{\n\t\t\t\tfloat x=cos(a);\n\t\t\t\tfloat y=sin(a);\n\t\t\t\t\n\t\t\t\tINNER_ARC_PUSH\n\t\t\t\t\n\t\t\t\tif ( i>100) {\n\t\t\t\t\tDEBUG(\"trapped in loop: dec,ig_end angle1=%.2f, angle2=%.2f, dangle=%.2f\\n\", angle1, angle2, dangle);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i=0;\n\t\t\tfor ( float a=angle1; ; a-=dangle, i++)\n\t\t\t{\n\t\t\t\tif ( a<angle2)\n\t\t\t\t\ta=angle2;\n\t\t\t\t\n\t\t\t\tfloat x=cos(a);\n\t\t\t\tfloat y=sin(a);\n\t\t\t\t\n\t\t\t\tINNER_ARC_PUSH\n\t\t\t\t#undef INNER_ARC_PUSH\n\t\t\t\t\n\t\t\t\tif ( a<=angle2)\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tif ( i>100) {\n\t\t\t\t\tDEBUG(\"trapped in loop: dec,end angle1=%.2f, angle2=%.2f, dangle=%.2f\\n\", angle1, angle2, dangle);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nvoid vectors_to_arc( vertex_array_holder& hold, const Point& P,\n\t\tconst Color& C, const Color& C2,\n\t\tPoint A, Point B, float dangle, float r, float r2, bool ignor_ends,\n\t\tPoint* apparent_P)\n//triangulate an inner arc between vectors A and B,\n//  A and B are position vectors relative to P\n{\n\tconst double& m_pi = vaser_pi;\n\tA *= 1/r;\n\tB *= 1/r;\n\tif ( A.x > 1.0-vaser_min_alw) A.x = 1.0-vaser_min_alw;\n\tif ( A.x <-1.0+vaser_min_alw) A.x =-1.0+vaser_min_alw;\n\tif ( B.x > 1.0-vaser_min_alw) B.x = 1.0-vaser_min_alw;\n\tif ( B.x <-1.0+vaser_min_alw) B.x =-1.0+vaser_min_alw;\n\t\n\tfloat angle1 = acos(A.x);\n\tfloat angle2 = acos(B.x);\n\tif ( A.y>0){ angle1=2*m_pi-angle1;}\n\tif ( B.y>0){ angle2=2*m_pi-angle2;}\n\t//printf( \"steps=%d \",int((angle2-angle1)/den*r));\n\t\n\tinner_arc( hold, P, C,C2, dangle,angle1,angle2, r,r2, ignor_ends, apparent_P);\n}\n\n#ifdef VASER_DEBUG\nvoid annotate( const Point& P, Color cc, int I=-1)\n{\n\tstatic int i=0;\n\tif ( I != -1) i=I;\n\t\n\tglBegin(GL_LINES);\n\t\tglColor3f(1,0,0);\n\t\tglVertex2f(P.x-4,P.y-4);\n\t\tglVertex2f(P.x+4,P.y+4);\n\t\tglVertex2f(P.x-4,P.y+4);\n\t\tglVertex2f(P.x+4,P.y-4);\n\tglEnd();\n\t\n\tchar str[10];\n\tsprintf(str,\"%d\",i);\n\tgl_font( FL_HELVETICA, 8);\n\tgl_draw( str,float(P.x+2),float(P.y));\n\ti++;\n}\nvoid annotate( const Point& P)\n{\n\tColor cc;\n\tannotate(P,cc);\n}\nvoid draw_vector( const Point& P, const Point& V, const char* name)\n{\n\tPoint P2 = P+V;\n\tglBegin(GL_LINES);\n\t\tglColor3f(1,0,0);\n\t\tglVertex2f(P.x,P.y);\n\t\tglColor3f(1,0.9,0.9);\n\t\tglVertex2f(P2.x,P2.y);\n\tglEnd();\n\tif ( name)\n\t{\n\t\tglColor3f(0,0,0);\n\t\tgl_font( FL_HELVETICA, 8);\n\t\tgl_draw( name,float(P2.x+2),float(P2.y));\n\t}\n}\nvoid printpoint( const Point& P, const char* name)\n{\n\tprintf(\"%s(%.4f,%.4f) \",name,P.x,P.y);\n\tfflush(stdout);\n}\n#endif\n/*\nPoint plus_minus( const Point& a, const Point& b, bool plus)\n{\n\tif (plus) return a+b;\n\telse return a-b;\n}\nPoint plus_minus( const Point& a, bool plus)\n{\n\tif (plus) return a;\n\telse return -a;\n}\nbool quad_is_reflexed( const Point& P0, const Point& P1, const Point& P2, const Point& P3)\n{\n\t//points:\n\t//   1------3\n\t//  /      /\n\t// 0------2\n\t// vector 01 parallel to 23\n\t\n\treturn Point::distance_squared(P1,P3) + Point::distance_squared(P0,P2)\n\t\t> Point::distance_squared(P0,P3) + Point::distance_squared(P1,P2);\n}\nvoid push_quad_safe( vertex_array_holder& core,\n\t\tconst Point& P2, const Color& cc2, bool transparent2,\n\t\tconst Point& P3, const Color& cc3, bool transparent3)\n{\n\t//push 2 points to form a quad safely(without reflex)\n\tPoint P0 = core.get_relative_end(-2);\n\tPoint P1 = core.get_relative_end(-1);\n\t\n\tif ( !quad_is_reflexed(P0,P1,P2,P3))\n\t{\n\t\tcore.push(P2,cc2,transparent2);\n\t\tcore.push(P3,cc3,transparent3);\n\t}\n\telse\n\t{\n\t\tcore.push(P3,cc3,transparent3);\n\t\tcore.push(P2,cc2,transparent2);\n\t}\n}*/\n\n#define push_quad(A0,A1,A2,A3,A4,A5,A6,A7,A8) push_quad_(__LINE__,A0,A1,A2,A3,A4,A5,A6,A7,A8)\n#define push_quadf(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12) push_quadf_(__LINE__,A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12)\n\nvoid push_quad_( short line, vertex_array_holder& core,\n\t\tconst Point& P0, const Point& P1, const Point& P2, const Point& P3,\n\t\tconst Color& c0, const Color& c1, const Color& c2, const Color& c3)\n{\n\tif( P0.is_zero()) DEBUG(\"pushed P0 (0,0) at %d\\n\",line);\n\tif( P1.is_zero()) DEBUG(\"pushed P1 (0,0) at %d\\n\",line);\n\tif( P2.is_zero()) DEBUG(\"pushed P2 (0,0) at %d\\n\",line);\n\tif( P3.is_zero()) DEBUG(\"pushed P3 (0,0) at %d\\n\",line);\n\t//interpret P0 to P3 as triangle strip\n\tcore.push3( P0, P1, P2,\n\t\tc0, c1, c2);\n\tcore.push3( P1, P2, P3,\n\t\tc1, c2, c3);\n}\nvoid push_quadf_( short line, vertex_array_holder& core,\n\t\tconst Point& P0, const Point& P1, const Point& P2, const Point& P3,\n\t\tconst Color& c0, const Color& c1, const Color& c2, const Color& c3,\n\t\tbool trans0, bool trans1, bool trans2, bool trans3)\n{\n\tif( P0.is_zero()) DEBUG(\"pushed P0 (0,0) at %d\\n\",line);\n\tif( P1.is_zero()) DEBUG(\"pushed P1 (0,0) at %d\\n\",line);\n\tif( P2.is_zero()) DEBUG(\"pushed P2 (0,0) at %d\\n\",line);\n\tif( P3.is_zero()) DEBUG(\"pushed P3 (0,0) at %d\\n\",line);\n\t//interpret P0 to P3 as triangle strip\n\tcore.push3( P0, P1, P2,\n\t\tc0, c1, c2,\n\t\ttrans0, trans1, trans2);\n\tcore.push3( P1, P2, P3,\n\t\tc1, c2, c3,\n\t\ttrans1, trans2, trans3);\n}\n\nstruct st_knife_cut\n{\n\tPoint T1[4]; //retained polygon, also serves as input triangle\n\tColor C1[4]; //\n\t\n\tPoint T2[4]; //cut away polygon\n\tColor C2[4]; //\n\t\n\tint T1c, T2c; //count of T1 & T2\n\t\t//must be 0,3 or 4\n};\nint triangle_knife_cut( const Point& kn1, const Point& kn2, const Point& kn_out, //knife\n\t\t\t       const Color* kC0, const Color* kC1, //color of knife\n\t\tst_knife_cut& ST)//will modify for output\n//see knife_cut_test for more info\n{\t//return number of points cut away\n\tint points_cut_away = 0;\n\t\n\tbool kn_colored = kC0 && kC1; //if true, use the colors of knife instead\n\tbool std_sign = Point::signed_area( kn1,kn2,kn_out) > 0;\n\tbool s1 = Point::signed_area( kn1,kn2,ST.T1[0])>0 == std_sign; //true means this point should be cut\n\tbool s2 = Point::signed_area( kn1,kn2,ST.T1[1])>0 == std_sign;\n\tbool s3 = Point::signed_area( kn1,kn2,ST.T1[2])>0 == std_sign;\n\tint sums = int(s1)+int(s2)+int(s3);\n\t\n\tif ( sums == 0)\n\t{\t//all 3 points are retained\n\t\tST.T1c = 3;\n\t\tST.T2c = 0;\n\t\t\n\t\tpoints_cut_away = 0;\n\t}\n\telse if ( sums == 3)\n\t{\t//all 3 are cut away\n\t\tST.T1c = 0;\n\t\tST.T2c = 3;\n\t\t\n\t\tST.T2[0] = ST.T1[0];\n\t\tST.T2[1] = ST.T1[1];\n\t\tST.T2[2] = ST.T1[2];\n\t\t\tST.C2[0] = ST.C1[0];\n\t\t\tST.C2[1] = ST.C1[1];\n\t\t\tST.C2[2] = ST.C1[2];\n\t\t\n\t\tpoints_cut_away = 3;\n\t}\n\telse\n\t{\n\t\tif ( sums == 2) {\n\t\t\ts1 = !s1; \n\t\t\ts2 = !s2;\n\t\t\ts3 = !s3;\n\t\t}\n\t\t//\n\t\tPoint ip1,ip2, outp;\n\t\tColor iC1,iC2, outC;\n\t\tif ( s1) { //here assume one point is cut away\n\t\t\t\t// thus only one of s1,s2,s3 is true\n\t\t\toutp= ST.T1[0];  outC= ST.C1[0];\n\t\t\tip1 = ST.T1[1];  iC1 = ST.C1[1];\n\t\t\tip2 = ST.T1[2];  iC2 = ST.C1[2];\n\t\t} else if ( s2) {\n\t\t\toutp= ST.T1[1];  outC= ST.C1[1];\n\t\t\tip1 = ST.T1[0];  iC1 = ST.C1[0];\n\t\t\tip2 = ST.T1[2];  iC2 = ST.C1[2];\n\t\t} else if ( s3) {\n\t\t\toutp= ST.T1[2];  outC= ST.C1[2];\n\t\t\tip1 = ST.T1[0];  iC1 = ST.C1[0];\n\t\t\tip2 = ST.T1[1];  iC2 = ST.C1[1];\n\t\t}\n\t\t\n\t\tPoint interP1,interP2;\n\t\tColor interC1,interC2;\n\t\tdouble ble1,kne1, ble2,kne2;\n\t\tPoint::intersect( kn1,kn2, ip1,outp, interP1, &kne1,&ble1);\n\t\tPoint::intersect( kn1,kn2, ip2,outp, interP2, &kne2,&ble2);\n\t\t\n\t\t{\tif ( kn_colored && Color_valid_range(kne1))\n\t\t\t\tinterC1 = Color_between( *kC0, *kC1, kne1);\n\t\t\telse\n\t\t\t\tinterC1 = Color_between( iC1, outC, ble1);\n\t\t}\n\t\t\n\t\t{\tif ( kn_colored && Color_valid_range(kne2))\n\t\t\t\tinterC2 = Color_between( *kC0, *kC1, kne2);\n\t\t\telse\n\t\t\t\tinterC2 = Color_between( iC2, outC, ble2);\n\t\t}\n\t\t\n\t\t//ip2 first gives a polygon\n\t\t//ip1 first gives a triangle strip\n\t\t\n\t\tif ( sums == 1) {\n\t\t\t//one point is cut away\n\t\t\tST.T1[0] = ip1;      ST.C1[0] = iC1;\n\t\t\tST.T1[1] = ip2;      ST.C1[1] = iC2;\n\t\t\tST.T1[2] = interP1;  ST.C1[2] = interC1;\n\t\t\tST.T1[3] = interP2;  ST.C1[3] = interC2;\n\t\t\tST.T1c = 4;\n\t\t\t\n\t\t\tST.T2[0] = outp;     ST.C2[0] = outC;\n\t\t\tST.T2[1] = interP1;  ST.C2[1] = interC1;\n\t\t\tST.T2[2] = interP2;  ST.C2[2] = interC2;\n\t\t\tST.T2c = 3;\n\t\t\t\n\t\t\tpoints_cut_away = 1;\n\t\t} else if ( sums == 2) {\n\t\t\t//two points are cut away\n\t\t\tST.T2[0] = ip1;      ST.C2[0] = iC1;\n\t\t\tST.T2[1] = ip2;      ST.C2[1] = iC2;\n\t\t\tST.T2[2] = interP1;  ST.C2[2] = interC1;\n\t\t\tST.T2[3] = interP2;  ST.C2[3] = interC2;\n\t\t\tST.T2c = 4;\n\t\t\t\n\t\t\tST.T1[0] = outp;     ST.C1[0] = outC;\n\t\t\tST.T1[1] = interP1;  ST.C1[1] = interC1;\n\t\t\tST.T1[2] = interP2;  ST.C1[2] = interC2;\n\t\t\tST.T1c = 3;\n\t\t\t\n\t\t\tpoints_cut_away = 2;\n\t\t}\n\t\t\n\t\t/*if ( (0.0-vaser_min_alw < kne1 && kne1 < 1.0+vaser_min_alw) ||\n\t\t     (0.0-vaser_min_alw < kne2 && kne2 < 1.0+vaser_min_alw) )\n\t\t{\t//highlight the wound\n\t\t\tglBegin(GL_LINE_STRIP);\n\t\t\t\tglColor3f(1,0,0);\n\t\t\t\tglVertex2f(ST.T1[0].x,ST.T1[0].y);\n\t\t\t\tglVertex2f(ST.T1[1].x,ST.T1[1].y);\n\t\t\t\tglVertex2f(ST.T1[2].x,ST.T1[2].y);\n\t\t\t\tglVertex2f(ST.T1[0].x,ST.T1[0].y);\n\t\t\tglEnd();\n\t\t\t\n\t\t\tif ( ST.T1c > 3)\n\t\t\tglBegin(GL_LINE_STRIP);\n\t\t\t\tglVertex2f(ST.T1[1].x,ST.T1[1].y);\n\t\t\t\tglVertex2f(ST.T1[2].x,ST.T1[2].y);\n\t\t\t\tglVertex2f(ST.T1[3].x,ST.T1[3].y);\n\t\t\t\tglVertex2f(ST.T1[1].x,ST.T1[1].y);\n\t\t\tglEnd();\n\t\t}*/\n\t}\n\t\n\treturn points_cut_away;\n}\nvoid vah_knife_cut( vertex_array_holder& core, //serves as both input and output\n\t\tconst Point& kn1, const Point& kn2, const Point& kn_out)\n//perform knife cut on all triangles (GL_TRIANGLES) in core\n{\n\tst_knife_cut ST;\n\tfor ( int i=0; i<core.count; i+=3)\n\t{\n\t\tST.T1[0] = core.get(i);\n\t\tST.T1[1] = core.get(i+1);\n\t\tST.T1[2] = core.get(i+2);\n\t\tST.C1[0] = core.get_color(i);\n\t\tST.C1[1] = core.get_color(i+1);\n\t\tST.C1[2] = core.get_color(i+2);\n\t\tST.T1c = 3; //will be ignored anyway\n\t\t\n\t\tint result = triangle_knife_cut( kn1,kn2,kn_out,0,0, ST);\n\t\t\n\t\tswitch (result)\n\t\t{\n\t\tcase 0:\n\t\t\t//do nothing\n\t\tbreak;\n\t\t\n\t\tcase 3:\t//degenerate the triangle\n\t\t\tcore.move(i+1,i); //move i into i+1\n\t\t\tcore.move(i+2,i);\n\t\tbreak;\n\t\t\n\t\tcase 1:\n\t\tcase 2:\n\t\t\tcore.replace(i,  ST.T1[0],ST.C1[0]);\n\t\t\tcore.replace(i+1,ST.T1[1],ST.C1[1]);\n\t\t\tcore.replace(i+2,ST.T1[2],ST.C1[2]);\n\t\t\t\n\t\t\tif ( result==1)\n\t\t\t{\t//create a new triangle\n\t\t\t\tPoint dump_P;\n\t\t\t\tColor dump_C;\n\t\t\t\tint a1,a2,a3;\n\t\t\t\ta1 = core.push( dump_P, dump_C);\n\t\t\t\ta2 = core.push( dump_P, dump_C);\n\t\t\t\ta3 = core.push( dump_P, dump_C);\n\t\t\t\t\n\t\t\t\t//copy the original points\n\t\t\t\tcore.move( a1, i+1);\n\t\t\t\tcore.move( a2, i+2);\n\t\t\t\tcore.move( a3, i+2);\n\t\t\t\t\n\t\t\t\t//make the new point\n\t\t\t\tcore.replace( a3, ST.T1[3],ST.C1[3]);\n\t\t\t}\n\t\tbreak;\n\t\t\n\t\t}\n\t}\n}\nvoid vah_N_knife_cut( vertex_array_holder& in, vertex_array_holder& out,\n\t\tconst Point* kn0, const Point* kn1, const Point* kn2,\n\t\tconst Color* kC0, const Color* kC1,\n\t\tint N)\n{\t//an iterative implementation\n\tconst int MAX_ST = 10;\n\tst_knife_cut ST[MAX_ST];\n\t\n\tbool kn_colored = kC0 && kC1;\n\t\n\tif ( N > MAX_ST)\n\t{\n\t\tDEBUG(\"vah_N_knife_cut: max N for current build is %d\\n\", MAX_ST);\n\t\tN = MAX_ST;\n\t}\n\t\n\tfor ( int i=0; i<in.count; i+=3) //each input triangle\n\t{\n\t\tint ST_count = 1;\n\t\tST[0].T1[0] = in.get(i);\n\t\tST[0].T1[1] = in.get(i+1);\n\t\tST[0].T1[2] = in.get(i+2);\n\t\t\tST[0].C1[0] = in.get_color(i);\n\t\t\tST[0].C1[1] = in.get_color(i+1);\n\t\t\tST[0].C1[2] = in.get_color(i+2);\n\t\tST[0].T1c = 3;\n\t\t\n\t\tfor ( int k=0; k<N; k++) //each knife\n\t\t{\n\t\t\tint cur_count = ST_count;\n\t\t\tfor ( int p=0; p<cur_count; p++) //each triangle to be cut\n\t\t\t{\n\t\t\t\t//perform cut\n\t\t\t\tif ( ST[p].T1c > 0)\n\t\t\t\t\tif ( kn_colored)\n\t\t\t\t\ttriangle_knife_cut( kn0[k], kn1[k], kn2[k],\n\t\t\t\t\t\t\t   &kC0[k],&kC1[k],\n\t\t\t\t\t\t\t\tST[p]);\n\t\t\t\t\telse\n\t\t\t\t\ttriangle_knife_cut( kn0[k],kn1[k],kn2[k],\n\t\t\t\t\t\t\t    0,0,ST[p]);\n\t\t\t\t\n\t\t\t\t//push retaining part\n\t\t\t\tif ( ST[p].T1c > 0) {\n\t\t\t\t\tout.push( ST[p].T1[0], ST[p].C1[0]);\n\t\t\t\t\tout.push( ST[p].T1[1], ST[p].C1[1]);\n\t\t\t\t\tout.push( ST[p].T1[2], ST[p].C1[2]);\n\t\t\t\t\tif ( ST[p].T1c > 3) {\n\t\t\t\t\t\tout.push( ST[p].T1[1], ST[p].C1[1]);\n\t\t\t\t\t\tout.push( ST[p].T1[2], ST[p].C1[2]);\n\t\t\t\t\t\tout.push( ST[p].T1[3], ST[p].C1[3]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//store cut away part to be cut again\n\t\t\t\tif ( ST[p].T2c > 0)\n\t\t\t\t{\n\t\t\t\t\tST[p].T1[0] = ST[p].T2[0];\n\t\t\t\t\tST[p].T1[1] = ST[p].T2[1];\n\t\t\t\t\tST[p].T1[2] = ST[p].T2[2];\n\t\t\t\t\t\tST[p].C1[0] = ST[p].C2[0];\n\t\t\t\t\t\tST[p].C1[1] = ST[p].C2[1];\n\t\t\t\t\t\tST[p].C1[2] = ST[p].C2[2];\n\t\t\t\t\tST[p].T1c = 3;\n\t\t\t\t\t\n\t\t\t\t\tif ( ST[p].T2c > 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tST[ST_count].T1[0] = ST[p].T2[1];\n\t\t\t\t\t\tST[ST_count].T1[1] = ST[p].T2[2];\n\t\t\t\t\t\tST[ST_count].T1[2] = ST[p].T2[3];\n\t\t\t\t\t\t\tST[ST_count].C1[0] = ST[p].C2[1];\n\t\t\t\t\t\t\tST[ST_count].C1[1] = ST[p].C2[2];\n\t\t\t\t\t\t\tST[ST_count].C1[2] = ST[p].C2[3];\n\t\t\t\t\t\tST[ST_count].T1c = 3;\n\t\t\t\t\t\tST_count++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tST[p].T1c = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst float cri_core_adapt = 0.0001f;\nvoid anchor_late( const Vec2* P, const Color* C, st_polyline* SL,\n\t\tvertex_array_holder& tris,\n\t\tPoint cap1, Point cap2)\n{\tconst int size_of_P = 3;\n\n\ttris.set_gl_draw_mode(GL_TRIANGLES);\n\t\n\tPoint P_0, P_1, P_2;\n\tP_0 = Point(P[0]);\n\tP_1 = Point(P[1]);\n\tP_2 = Point(P[2]);\n\tif ( SL[0].djoint==PLC_butt || SL[0].djoint==PLC_square)\n\t\tP_0 -= cap1;\n\tif ( SL[2].djoint==PLC_butt || SL[2].djoint==PLC_square)\n\t\tP_2 -= cap2;\n\t\n\tPoint P0, P1, P2, P3, P4, P5, P6, P7;\n\tPoint P0r,P1r,P2r,P3r,P4r,P5r,P6r,P7r; //fade\n\t\n\tP0 = P_1 + SL[1].vP;\n\t\tP0r = P0 + SL[1].vR;\n\tP1 = P_1 - SL[1].vP;\n\t\tP1r = P1 - SL[1].vR;\n\t\n\tP2 = P_1 + SL[1].T1;\n\t\tP2r = P2 + SL[1].R1 + SL[0].bR;\n\tP3 = P_0 + SL[0].T;\n\t\tP3r = P3 + SL[0].R;\n\tP4 = P_0 - SL[0].T;\n\t\tP4r = P4 - SL[0].R;\n\t\n\tP5 = P_1 + SL[1].T;\n\t\tP5r = P5 + SL[1].R - SL[1].bR;\n\tP6 = P_2 + SL[2].T;\n\t\tP6r = P6 + SL[2].R;\n\tP7 = P_2 - SL[2].T;\n\t\tP7r = P7 - SL[2].R;\n\t\t/* annotate( P0,C[0],0);\n\t\tannotate( P1);\n\t\tannotate( P2);\n\t\tannotate( P3);\n\t\tannotate( P4);\n\t\tannotate( P5);\n\t\tannotate( P6);\n\t\tannotate( P7); */\n\n\tint normal_line_core_joint = 1; //0:dont draw, 1:draw, 2:outer only\n\n\t//consider these as inline child functions\n\t#define normal_first_segment \\\n\t\t\ttris.push3( P3,  P2,  P1, \\\n\t\t\t\t  C[0],C[1],C[1]);\\\n\t\t\ttris.push3( P1,  P3,  P4, \\\n\t\t\t\t  C[1],C[0],C[0])\n\t\n\t#define normal_last_segment \\\n\t\t\ttris.push3( P1,  P5,  P6, \\\n\t\t\t\t  C[1],C[1],C[2]);\\\n\t\t\ttris.push3( P1,  P6,  P7, \\\n\t\t\t\t  C[1],C[2],C[2])\n\t\n\tColor Cpt; //color at PT\n\tif ( SL[1].degenT || SL[1].degenR)\n\t{\n\t\tfloat pt = sqrt(SL[1].pt);\n\t\tif ( SL[1].pre_full)\n\t\t\tCpt = Color_between(C[0],C[1], pt);\n\t\telse\n\t\t\tCpt = Color_between(C[1],C[2], 1-pt);\n\t}\n\t\n\tif ( SL[1].degenT)\n\t{\t//degen line core\n\t\tP1 = SL[1].PT;\n\t\t\tif( SL[1].degenR)\n\t\t\t\tP1r = SL[1].PR;\n\t\t\n\t\ttris.push3( P3,  P2,  P1,\n\t\t\t  C[0],C[1],C[1]); //fir seg\n\t\ttris.push3( P1,  P5,  P6,\n\t\t\t  C[1],C[1],C[2]); //las seg\n\t\t\n\t\tif ( SL[1].pre_full)\n\t\t{\ttris.push3( P1,  P3,  P4,\n\t\t\t\t  C[1],C[0],C[0]);\n\t\t}\n\t\telse\n\t\t{\ttris.push3( P1,  P6,  P7,\n\t\t\t\t  C[1],C[2],C[2]);\n\t\t}\n\t}\n\telse if ( SL[1].degenR && SL[1].pt > cri_core_adapt) //&& ! SL[1].degenT\n\t{\t//line core adapted for degenR\n\t\tif ( SL[1].pre_full)\n\t\t{\n\t\t\tnormal_last_segment;\n\n\t\t\t//special first segment\n\t\t\tPoint P9 = SL[1].PT;\n\t\t\ttris.push3( P3,  P2,  P1,\n\t\t\t\t  C[0],C[1],C[1]);\n\t\t\ttris.push3( P3,  P9,  P1,\n\t\t\t\t  C[0], Cpt,C[1]);\n\t\t\ttris.push3( P3,  P9,  P4,\n\t\t\t\t  C[0], Cpt,C[0]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnormal_first_segment;\n\t\t\t\n\t\t\t//special last segment\n\t\t\tPoint P9 = SL[1].PT;\n\t\t\tpush_quad( tris,\n\t\t\t\t  P5,  P1,  P6,  P9,\n\t\t\t\tC[1],C[1],C[2], Cpt);\n\t\t\ttris.push3( P7,  P9,  P6,\n\t\t\t\t  C[2], Cpt,C[2]);\n\t\t\t\t/*annotate(P1,C[1],1);\n\t\t\t\tannotate(P5,C[1],5);\n\t\t\t\tannotate(P6,C[1],6);\n\t\t\t\tannotate(P7,C[1],7);\n\t\t\t\tannotate(P9,C[1],9);*/\n\t\t}\n\t}\n\telse\n\t{\n\t\tnormal_first_segment;\n\t\tnormal_last_segment;\n\t#undef normal_first_segment\n\t#undef normal_last_segment\n\t}\n\t\n\tif (normal_line_core_joint)\n\t{\n\t\tswitch( SL[1].djoint)\n\t\t{\n\t\t\tcase PLJ_miter:\n\t\t\t\ttris.push3( P2,  P5,  P0,\n\t\t\t\t\t  C[1],C[1],C[1]);\n\t\t\tcase PLJ_bevel:\n\t\t\t\tif ( normal_line_core_joint==1)\n\t\t\t\ttris.push3( P2,  P5,  P1,\n\t\t\t\t\t  C[1],C[1],C[1]);\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase PLJ_round: {\n\t\t\t\tvertex_array_holder strip;\n\t\t\t\tstrip.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\t\t\t\t\n\t\t\tif ( normal_line_core_joint==1)\n\t\t\t\tvectors_to_arc( strip, P_1, C[1], C[1],\n\t\t\t\tSL[1].T1, SL[1].T,\n\t\t\t\tget_PLJ_round_dangle(SL[1].t,SL[1].r),\n\t\t\t\tSL[1].t, 0.0f, false, &P1);\n\t\t\telse if ( normal_line_core_joint==2)\n\t\t\t\tvectors_to_arc( strip, P_1, C[1], C[1],\n\t\t\t\tSL[1].T1, SL[1].T,\n\t\t\t\tget_PLJ_round_dangle(SL[1].t,SL[1].r),\n\t\t\t\tSL[1].t, 0.0f, false, &P5);\n\t\t\t\t\n\t\t\t\ttris.push(strip);\n\t\t\t} break;\n\t\t}\n\t}\n\t\n\tif ( SL[1].degenR)\n\t{\t//degen inner fade\n\t\tPoint P9 = SL[1].PT;\n\t\t\tPoint P9r = SL[1].PR;\n\t\t//annotate(P9,C[0],9);\n\t\t//annotate(P9r);\n\n\t\tColor ccpt=Cpt;\n\t\tif ( SL[1].degenT)\n\t\t\tccpt = C[1];\n\t\t\n\t\tif ( SL[1].pre_full)\n\t\t{\tpush_quadf( tris,\n\t\t\t\t    P9,  P4, P9r, P4r,\n\t\t\t\t  ccpt,C[0],C[1],C[0],\n\t\t\t\t     0,   0,   1,   1); //fir seg\n\n\t\t\tif ( !SL[1].degenT)\n\t\t\t{\n\t\t\t\tPoint mid = Point::midpoint(P9,P7);\n\t\t\t\ttris.push3( P1,  P9, mid,\n\t\t\t\t\t  C[1], Cpt,C[1],\n\t\t\t\t\t     0,   0,   1);\n\t\t\t\ttris.push3( P1,  P7, mid,\n\t\t\t\t\t  C[1],C[2],C[1],\n\t\t\t\t\t     0,   0,   1);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\tpush_quadf( tris,\n\t\t\t\t    P9,  P7, P9r, P7r,\n\t\t\t\t  ccpt,C[2],C[1],C[2],\n\t\t\t\t     0,   0,   1,   1); //las seg\n\t\t\t\n\t\t\tif ( !SL[1].degenT)\n\t\t\t{\n\t\t\t\tPoint mid = Point::midpoint(P9,P4);\n\t\t\t\ttris.push3( P1,  P9, mid,\n\t\t\t\t\t  C[1], Cpt,C[1],\n\t\t\t\t\t     0,   0,   1);\n\t\t\t\ttris.push3( P1,  P4, mid,\n\t\t\t\t\t  C[1],C[0],C[1],\n\t\t\t\t\t     0,   0,   1);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\t//normal inner fade\n\t\tpush_quadf( tris,\n\t\t\t    P1,  P4, P1r, P4r,\n\t\t\t  C[1],C[0],C[1],C[0],\n\t\t\t     0,   0,   1,   1); //fir seg\n\t\tpush_quadf( tris,\n\t\t\t    P1,  P7, P1r, P7r,\n\t\t\t  C[1],C[2],C[1],C[2],\n\t\t\t     0,   0,   1,   1); //las seg\n\t}\n\t\n\t{\t//outer fade, whether degen or normal\n\t\tpush_quadf( tris,\n\t\t\t    P2,  P3, P2r, P3r,\n\t\t\t  C[1],C[0],C[1],C[0],\n\t\t\t     0,   0,   1,   1); //fir seg\n\t\tpush_quadf( tris,\n\t\t\t    P5,  P6, P5r, P6r,\n\t\t\t  C[1],C[2],C[1],C[2],\n\t\t\t     0,   0,   1,   1); //las seg\n\t\tswitch( SL[1].djoint)\n\t\t{\t//line fade joint\n\t\t\tcase PLJ_miter:\n\t\t\t\tpush_quadf( tris,\n\t\t\t\t\t    P0,  P5, P0r, P5r,\n\t\t\t\t\t  C[1],C[1],C[1],C[1],\n\t\t\t\t\t     0,   0,   1,   1);\n\t\t\t\tpush_quadf( tris,\n\t\t\t\t\t    P0,  P2, P0r, P2r,\n\t\t\t\t\t  C[1],C[1],C[1],C[1],\n\t\t\t\t\t     0,   0,   1,   1);\n\t\t\tbreak;\n\t\t\tcase PLJ_bevel:\n\t\t\t\tpush_quadf( tris,\n\t\t\t\t\t    P2,  P5, P2r, P5r,\n\t\t\t\t\t  C[1],C[1],C[1],C[1],\n\t\t\t\t\t     0,   0,   1,   1);\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase PLJ_round: {\n\t\t\t\tvertex_array_holder strip;\n\t\t\t\tstrip.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\t\t\t\tColor C2 = C[1]; C2.a = 0.0f;\n\t\t\t\tvectors_to_arc( strip, P_1, C[1], C2,\n\t\t\t\tSL[1].T1, SL[1].T,\n\t\t\t\tget_PLJ_round_dangle(SL[1].t,SL[1].r),\n\t\t\t\tSL[1].t, SL[1].t+SL[1].r, false, 0);\n\t\t\t\t\n\t\t\t\ttris.push(strip);\n\t\t\t} break;\n\t\t}\n\t}\n} //anchor_late\n\nvoid anchor_cap( const Vec2* P, const Color* C, st_polyline* SL,\n\t\tvertex_array_holder& tris,\n\t\tPoint cap1, Point cap2)\n{\n\tPoint P4 = Point(P[0]) - SL[0].T;\n\tPoint P7 = Point(P[2]) - SL[2].T;\n\tfor ( int i=0,k=0; k<=1; i=2, k++)\n\t{\n\t\tvertex_array_holder cap;\n\t\tPoint& cur_cap = i==0? cap1:cap2;\n\t\tif ( cur_cap.non_zero())\n\t\t{\n\t\t\tcap.set_gl_draw_mode(GL_TRIANGLES);\n\t\t\tbool perform_cut = ( SL[1].degenR && SL[1].R_full_degen) &&\n\t\t\t\t\t((k==0 && !SL[1].pre_full) ||\n\t\t\t\t\t (k==1 &&  SL[1].pre_full) );\n\t\t\t\n\t\t\tPoint P3 = Point(P[i])-SL[i].T*2-SL[i].R+cur_cap;\n\t\t\t\n\t\t\tif ( SL[i].djoint == PLC_round)\n\t\t\t{\t//round caps\n\t\t\t\t{\tvertex_array_holder strip;\n\t\t\t\t\tstrip.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\t\t\t\t\t\n\t\t\t\t\tColor C2 = C[i]; C2.a = 0.0f;\n\t\t\t\t\tPoint O  = Point(P[i]);\n\t\t\t\t\tPoint app_P = O+SL[i].T;\n\t\t\t\t\tPoint bR = SL[i].bR;\n\t\t\t\t\tbR.follow_signs(cur_cap);\n\t\t\t\t\tfloat dangle = get_PLJ_round_dangle(SL[i].t,SL[i].r);\n\t\t\t\t\t\n\t\t\t\t\tvectors_to_arc( strip, O, C[i], C[i],\n\t\t\t\t\tSL[i].T+bR, -SL[i].T+bR,\n\t\t\t\t\tdangle,\n\t\t\t\t\tSL[i].t, 0.0f, false, &app_P);\n\t\t\t\t\t\tstrip.push( O-SL[i].T,C[i]);\n\t\t\t\t\t\tstrip.push( app_P,C[i]);\n\n\t\t\t\t\tstrip.jump();\n\n\t\t\t\t\tPoint a1 = O+SL[i].T;\n\t\t\t\t\tPoint a2 = O+SL[i].T*(1/SL[i].t)*(SL[i].t+SL[i].r);\n\t\t\t\t\tPoint b1 = O-SL[i].T;\n\t\t\t\t\tPoint b2 = O-SL[i].T*(1/SL[i].t)*(SL[i].t+SL[i].r);\n\t\t\t\t\t\n\t\t\t\t\t\tstrip.push( a1,C[i]);\n\t\t\t\t\t\tstrip.push( a2,C2);\n\t\t\t\t\tvectors_to_arc( strip, O, C[i], C2,\n\t\t\t\t\tSL[i].T+bR, -SL[i].T+bR,\n\t\t\t\t\tdangle,\n\t\t\t\t\tSL[i].t, SL[i].t+SL[i].r, false, 0);\t\t\t\t\t\t\n\t\t\t\t\t\tstrip.push( b1,C[i]);\n\t\t\t\t\t\tstrip.push( b2,C2);\n\t\t\t\t\tcap.push(strip);\n\t\t\t\t}\n\t\t\t\tif ( perform_cut)\n\t\t\t\t{\n\t\t\t\t\tPoint P4k;\n\t\t\t\t\tif ( !SL[1].pre_full)\n\t\t\t\t\t\tP4k = P7; //or P7r ?\n\t\t\t\t\telse\n\t\t\t\t\t\tP4k = P4;\n\t\t\t\t\t\n\t\t\t\t\tvah_knife_cut( cap, SL[1].PT, P4k, P3);\n\t\t\t\t\t/*annotate(SL[1].PT,C[i],0);\n\t\t\t\t\tannotate(P3,C[i],3);\n\t\t\t\t\tannotate(P4k,C[i],4);*/\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //if ( SL[i].djoint == PLC_butt | SL[i].cap == PLC_square | SL[i].cap == PLC_rect)\n\t\t\t{\t//rectangle caps\n\t\t\t\tPoint P_cur = P[i];\n\t\t\t\tbool degen_nxt=0, degen_las=0;\n\t\t\t\tif ( k == 0)\n\t\t\t\t\tif ( SL[0].djoint==PLC_butt || SL[0].djoint==PLC_square)\n\t\t\t\t\t\tP_cur -= cap1;\n\t\t\t\tif ( k == 1)\n\t\t\t\t\tif ( SL[2].djoint==PLC_butt || SL[2].djoint==PLC_square)\n\t\t\t\t\t\tP_cur -= cap2;\n\t\t\t\t\n\t\t\t\tPoint P0,P1,P2,P3,P4,P5,P6;\n\t\t\t\t\n\t\t\t\tP0 = P_cur+SL[i].T+SL[i].R;\n\t\t\t\tP1 = P0+cur_cap;\n\t\t\t\tP2 = P_cur+SL[i].T;\n\t\t\t\tP4 = P_cur-SL[i].T;\n\t\t\t\tP3 = P4-SL[i].R+cur_cap;\n\t\t\t\tP5 = P4-SL[i].R;\n\t\t\t\t\n\t\t\t\tcap.push( P0, C[i],true);\n\t\t\t\tcap.push( P1, C[i],true);\n\t\t\t\tcap.push( P2, C[i]);\n\t\t\t\t\n\t\t\t\t\t\tcap.push( P1, C[i],true);\n\t\t\t\t\tcap.push( P2, C[i]);\n\t\t\t\tcap.push( P3, C[i],true);\n\t\t\t\t\n\t\t\t\t\t\tcap.push( P2, C[i]);\n\t\t\t\t\tcap.push( P3, C[i],true);\n\t\t\t\tcap.push( P4, C[i]);\n\t\t\t\t\n\t\t\t\t\t\tcap.push( P3, C[i],true);\n\t\t\t\t\tcap.push( P4, C[i]);\n\t\t\t\tcap.push( P5, C[i],true);\n\t\t\t\t//say if you want to use triangle strip,\n\t\t\t\t//  just push P0~ P5 in sequence\n\t\t\t\tif ( perform_cut)\n\t\t\t\t{\n\t\t\t\t\tvah_knife_cut( cap, SL[1].PT, SL[1].PR, P3);\n\t\t\t\t\t/*annotate(SL[1].PT,C[i],0);\n\t\t\t\t\tannotate(SL[1].PR);\n\t\t\t\t\tannotate(P3);\n\t\t\t\t\tannotate(P4);*/\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttris.push(cap);\n\t}\n} //anchor_cap\n\nvoid segment_late( const Vec2* P, const Color* C, st_polyline* SL,\n\t\tvertex_array_holder& tris,\n\t\tPoint cap1, Point cap2)\n{\n\ttris.set_gl_draw_mode(GL_TRIANGLES);\n\t\n\tPoint P_0, P_1, P_2;\n\tP_0 = Point(P[0]);\n\tP_1 = Point(P[1]);\n\tif ( SL[0].djoint==PLC_butt || SL[0].djoint==PLC_square)\n\t\tP_0 -= cap1;\n\tif ( SL[1].djoint==PLC_butt || SL[1].djoint==PLC_square)\n\t\tP_1 -= cap2;\n\t\n\tPoint P1, P2, P3, P4;  //core\n\tPoint P1c,P2c,P3c,P4c; //cap\n\tPoint P1r,P2r,P3r,P4r; //fade\n\n\tP1 = P_0 + SL[0].T;\n\t\tP1r = P1 + SL[0].R;\n\t\tP1c = P1r + cap1;\n\tP2 = P_0 - SL[0].T;\n\t\tP2r = P2 - SL[0].R;\n\t\tP2c = P2r + cap1;\n\tP3 = P_1 + SL[1].T;\n\t\tP3r = P3 + SL[1].R;\n\t\tP3c = P3r + cap2;\n\tP4 = P_1 - SL[1].T;\n\t\tP4r = P4 - SL[1].R;\n\t\tP4c = P4r + cap2;\n\t//core\n\tpush_quad( tris,\n\t\t  P1,  P2,  P3,  P4,\n\t\tC[0],C[0],C[1],C[1] );\n\t//fade\n\tpush_quadf( tris,\n\t\t  P1, P1r,  P3, P3r,\n\t\tC[0],C[0],C[1],C[1],\n\t\t   0,   1,   0,   1 );\n\tpush_quadf( tris,\n\t\t  P2, P2r,  P4, P4r,\n\t\tC[0],C[0],C[1],C[1],\n\t\t   0,   1,   0,   1 );\n\t//caps\n\tfor ( int j=0; j<2; j++)\n\t{\n\t\tvertex_array_holder cap;\n\t\tcap.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\t\tPoint &cur_cap = j==0?cap1:cap2;\n\t\tif( cur_cap.is_zero())\n\t\t\tcontinue;\n\t\t\n\t\tif ( SL[j].djoint == PLC_round)\n\t\t{\t//round cap\n\t\t\tColor C2 = C[j]; C2.a = 0.0f;\n\t\t\tPoint O  = Point(P[j]);\n\t\t\tPoint app_P = O+SL[j].T;\n\t\t\tPoint bR = SL[j].bR;\n\t\t\tbR.follow_signs( j==0?cap1:cap2);\n\t\t\tfloat dangle = get_PLJ_round_dangle(SL[j].t,SL[j].r);\n\t\t\t\n\t\t\tvectors_to_arc( cap, O, C[j], C[j],\n\t\t\tSL[j].T+bR, -SL[j].T+bR,\n\t\t\tdangle,\n\t\t\tSL[j].t, 0.0f, false, &app_P);\n\t\t\t\tcap.push( O-SL[j].T,C[j]);\n\t\t\t\tcap.push( app_P,C[j]);\n\t\t\t\n\t\t\tcap.jump();\n\t\t\t\n\t\t\t{\t//fade\n\t\t\t\tPoint a1 = O+SL[j].T;\n\t\t\t\tPoint a2 = O+SL[j].T*(1/SL[j].t)*(SL[j].t+SL[j].r);\n\t\t\t\tPoint b1 = O-SL[j].T;\n\t\t\t\tPoint b2 = O-SL[j].T*(1/SL[j].t)*(SL[j].t+SL[j].r);\n\t\t\t\t\n\t\t\t\t\tcap.push( a1,C[j]);\n\t\t\t\t\tcap.push( a2,C2);\n\t\t\t\tvectors_to_arc( cap, O, C[j], C2,\n\t\t\t\tSL[j].T+bR, -SL[j].T+bR,\n\t\t\t\tdangle,\n\t\t\t\tSL[j].t, SL[j].t+SL[j].r, false, 0);\t\t\t\t\t\t\n\t\t\t\t\tcap.push( b1,C[j]);\n\t\t\t\t\tcap.push( b2,C2);\n\t\t\t}\n\t\t}\n\t\telse //if ( SL[j].djoint == PLC_butt | SL[j].cap == PLC_square | SL[j].cap == PLC_rect)\n\t\t{\t//rectangle cap\n\t\t\tPoint Pj,Pjr,Pjc, Pk,Pkr,Pkc;\n\t\t\tif ( j==0)\n\t\t\t{\n\t\t\t\tPj = P1;\n\t\t\t\tPjr= P1r;\n\t\t\t\tPjc= P1c;\n\t\t\t\t\n\t\t\t\tPk = P2;\n\t\t\t\tPkr= P2r;\n\t\t\t\tPkc= P2c;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPj = P3;\n\t\t\t\tPjr= P3r;\n\t\t\t\tPjc= P3c;\n\t\t\t\t\n\t\t\t\tPk = P4;\n\t\t\t\tPkr= P4r;\n\t\t\t\tPkc= P4c;\n\t\t\t}\n\t\t\t\n\t\t\tcap.push( Pkr, C[j], 1);\n\t\t\tcap.push( Pkc, C[j], 1);\n\t\t\tcap.push( Pk , C[j], 0);\n\t\t\tcap.push( Pjc, C[j], 1);\n\t\t\tcap.push( Pj , C[j], 0);\n\t\t\tcap.push( Pjr, C[j], 1);\n\t\t}\n\t\ttris.push(cap);\n\t}\n\t\n\t/*annotate(P1,C[0],1);\n\tannotate(P2,C[0],2);\n\tannotate(P3,C[0],3);\n\tannotate(P4,C[0],4);\n\t\tannotate(P1c,C[0],11);\n\t\tannotate(P2c,C[0],21);\n\t\tannotate(P3c,C[0],31);\n\t\tannotate(P4c,C[0],41);\n\t\t\n\t\tannotate(P1r,C[0],12);\n\t\tannotate(P2r,C[0],22);\n\t\tannotate(P3r,C[0],32);\n\t\tannotate(P4r,C[0],42);\n\t*/\n}\n\nvoid segment( st_anchor& SA, const polyline_opt* options,  bool cap_first, bool cap_last, char last_cap_type=-1)\n{\n\tdouble* weight = SA.W;\n\tif ( !SA.P || !SA.C || !weight) return;\n\t\n\tPoint P[2]; P[0]=SA.P[0]; P[1]=SA.P[1];\n\tColor C[2]; C[0]=SA.C[0]; C[1]=SA.C[1];\n\t\n\tpolyline_opt opt={0};\n\tif ( options)\n\t\topt = (*options);\n\t\n\tPoint T1,T2;\n\tPoint R1,R2;\n\tPoint bR;\n\tdouble t,r;\n\t\n\tbool varying_weight = !(weight[0]==weight[1]);\n\t\n\tPoint cap_start, cap_end;\n\tst_polyline SL[2];\n\t\n\tfor ( int i=0; i<2; i++)\n\t{\n\t\tif ( weight[i]>=0.0 && weight[i]<1.0)\n\t\t{\n\t\t\tdouble f=weight[i]-static_cast<int>(weight[i]);\n\t\t\tC[i].a *=f;\n\t\t}\n\t}\n\t\n\t{\tint i=0;\n\t\tmake_T_R_C( P[i], P[i+1], &T2,&R2,&bR, weight[i],opt, &r,&t,0, true);\n\t\t\n\t\tif ( cap_first)\n\t\t{\n\t\t\tif ( opt.cap==PLC_square)\n\t\t\t{\n\t\t\t\tP[0] = Point(P[0]) - bR * (t+r);\n\t\t\t}\n\t\t\tcap_start = bR;\n\t\t\tcap_start.opposite(); if ( opt.feather && !opt.no_feather_at_cap)\n\t\t\tcap_start*=opt.feathering;\n\t\t}\n\t\t\n\t\tSL[i].djoint=opt.cap;\n\t\tSL[i].t=t;\n\t\tSL[i].r=r;\n\t\tSL[i].T=T2;\n\t\tSL[i].R=R2;\n\t\tSL[i].bR=bR*0.01;\n\t\tSL[i].degenT = false;\n\t\tSL[i].degenR = false;\n\t}\n\t\n\t{\tint i=1;\n\t\tif ( varying_weight)\n\t\t\tmake_T_R_C( P[i-1], P[i], &T2,&R2,&bR,weight[i],opt, &r,&t,0, true);\n\t\t\n\t\tlast_cap_type = last_cap_type==-1 ? opt.cap:last_cap_type;\n\t\t\n\t\tif ( cap_last)\n\t\t{\n\t\t\tif ( last_cap_type==PLC_square)\n\t\t\t{\n\t\t\t\tP[1] = Point(P[1]) + bR * (t+r);\n\t\t\t}\n\t\t\tcap_end = bR;\n\t\t\tif ( opt.feather && !opt.no_feather_at_cap)\n\t\t\t\tcap_end*=opt.feathering;\n\t\t}\n\t\t\n\t\tSL[i].djoint = last_cap_type;\n\t\tSL[i].t=t;\n\t\tSL[i].r=r;\n\t\tSL[i].T=T2;\n\t\tSL[i].R=R2;\n\t\tSL[i].bR=bR*0.01;\n\t\tSL[i].degenT = false;\n\t\tSL[i].degenR = false;\n\t}\n\t\n\tsegment_late( P,C,SL,SA.vah, cap_start,cap_end);\n}\n\nint anchor( st_anchor& SA, const polyline_opt* options, bool cap_first, bool cap_last)\n{\n\tpolyline_opt opt={0};\n\tif ( options)\n\t\topt = (*options);\n\t\n\tPoint* P = SA.P;\n\tColor* C = SA.C;\n\tdouble* weight = SA.W;\n\n\t{\tst_polyline emptySL;\n\t\tSA.SL[0]=emptySL; SA.SL[1]=emptySL; SA.SL[2]=emptySL;\n\t}\n\tst_polyline* SL = SA.SL;\n\tSA.vah.set_gl_draw_mode(GL_TRIANGLES);\n\tSA.cap_start = Point();\n\tSA.cap_end = Point();\n\t\n\t//const double critical_angle=11.6538;\n\t//\tcritical angle in degrees where a miter is force into bevel\n\t//\tit is _similar_ to cairo_set_miter_limit () but cairo works with ratio while VASEr works with included angle\n\tconst double cos_cri_angle=0.979386; //cos(critical_angle)\n\t\n\tbool varying_weight = !(weight[0]==weight[1] & weight[1]==weight[2]);\n\t\n\tdouble combined_weight=weight[1]+(opt.feather?opt.feathering:0.0);\n\tif ( combined_weight < cri_segment_approx)\n\t{\n\t\tsegment( SA, &opt, cap_first,false, opt.joint==PLJ_round?PLC_round:PLC_butt);\n\t\tchar ori_cap = opt.cap;\n\t\topt.cap = opt.joint==PLJ_round?PLC_round:PLC_butt;\n\t\tSA.P[0]=SA.P[1]; SA.P[1]=SA.P[2];\n\t\tSA.C[0]=SA.C[1]; SA.C[1]=SA.C[2];\n\t\tSA.W[0]=SA.W[1]; SA.W[1]=SA.W[2];\n\t\tsegment( SA, &opt, false,cap_last, ori_cap);\n\t\treturn 0;\n\t}\n\t\n\tPoint T1,T2,T21,T31;\t\t//]these are for calculations in early stage\n\tPoint R1,R2,R21,R31;\t\t//]\n\t\n\tfor ( int i=0; i<3; i++)\n\t{\t//lower the transparency for weight < 1.0\n\t\tif ( weight[i]>=0.0 && weight[i]<1.0)\n\t\t{\n\t\t\tdouble f=weight[i];\n\t\t\tC[i].a *=f;\n\t\t}\n\t}\n\t\n\t{\tint i=0;\n\t\n\t\tPoint cap1;\n\t\tdouble r,t;\t\t\n\t\tmake_T_R_C( P[i], P[i+1], &T2,&R2,&cap1, weight[i],opt, &r,&t,0);\n\t\tif ( varying_weight) {\n\t\tmake_T_R_C( P[i], P[i+1], &T31,&R31,0, weight[i+1],opt, 0,0,0);\n\t\t} else {\n\t\t\tT31 = T2;\n\t\t\tR31 = R2;\n\t\t}\n\t\tPoint::anchor_outward(R2, P[i+1],P[i+2] /*,inward_first->value()*/);\n\t\t\tT2.follow_signs(R2);\n\t\t\n\t\tSL[i].bR=cap1;\n\t\t\n\t\tif ( cap_first)\n\t\t{\n\t\t\tif ( opt.cap==PLC_square)\n\t\t\t{\n\t\t\t\tP[0] = Point(P[0]) - cap1 * (t+r);\n\t\t\t}\n\t\t\tcap1.opposite(); if ( opt.feather && !opt.no_feather_at_cap)\n\t\t\tcap1*=opt.feathering;\n\t\t\tSA.cap_start = cap1;\n\t\t}\n\t\t\n\t\tSL[i].djoint=opt.cap;\n\t\tSL[i].T=T2;\n\t\tSL[i].R=R2;\n\t\tSL[i].t=(float)t;\n\t\tSL[i].r=(float)r;\n\t\tSL[i].degenT = false;\n\t\tSL[i].degenR = false;\n\t\t\n\t\tSL[i+1].T1=T31;\n\t\tSL[i+1].R1=R31;\n\t}\n\t\n\tif ( cap_last)\n\t{\tint i=2;\n\n\t\tPoint cap2;\n\t\tdouble t,r;\n\t\tmake_T_R_C( P[i-1],P[i], 0,0,&cap2,weight[i],opt, &r,&t,0);\n\t\tif ( opt.cap==PLC_square)\n\t\t{\n\t\t\tP[2] = Point(P[2]) + cap2 * (t+r);\n\t\t}\n\t\t\n\t\tSL[i].bR=cap2;\n\t\t\n\t\tif ( opt.feather && !opt.no_feather_at_cap)\n\t\t\tcap2*=opt.feathering;\n\t\tSA.cap_end = cap2;\n\t}\n\t\n\t{\tint i=1;\n\t\n\t\tdouble r,t;\n\t\tPoint P_cur = P[i]; //current point //to avoid calling constructor repeatedly\n\t\tPoint P_nxt = P[i+1]; //next point\n\t\tPoint P_las = P[i-1]; //last point\n\t\tif ( opt.cap==PLC_butt || opt.cap==PLC_square)\n\t\t{\n\t\t\tP_nxt -= SA.cap_end;\n\t\t\tP_las -= SA.cap_start;\n\t\t}\n\t\t\n\t\t{\n\t\tPoint bR; float length_cur, length_nxt;\n\t\tmake_T_R_C( P_las, P_cur,  &T1,&R1, 0, weight[i-1],opt,0,0, &length_cur);\n\t\tif ( varying_weight) {\n\t\tmake_T_R_C( P_las, P_cur, &T21,&R21,0, weight[i],opt,  0,0,0);\n\t\t} else {\n\t\t\tT21 = T1;\n\t\t\tR21 = R1;\n\t\t}\n\t\t\n\t\tmake_T_R_C( P_cur, P_nxt,  &T2,&R2,&bR, weight[i],opt, &r,&t, &length_nxt);\n\t\tif ( varying_weight) {\n\t\tmake_T_R_C( P_cur, P_nxt, &T31,&R31,0, weight[i+1],opt, 0,0,0);\n\t\t} else {\n\t\t\tT31 = T2;\n\t\t\tR31 = R2;\n\t\t}\n\t\t\n\t\tSL[i].T=T2;\n\t\tSL[i].R=R2;\n\t\tSL[i].bR=bR;\n\t\tSL[i].t=(float)t;\n\t\tSL[i].r=(float)r;\n\t\tSL[i].degenT = false;\n\t\tSL[i].degenR = false;\n\t\t\n\t\tSL[i+1].T1=T31;\n\t\tSL[i+1].R1=R31;\n\t\t}\n\t\t\n\t\t{\t//2nd to 2nd last point\n\t\t\t\n\t\t\t//find the angle between the 2 line segments\n\t\t\tPoint ln1,ln2, V;\n\t\t\tln1 = P_cur - P_las;\n\t\t\tln2 = P_nxt - P_cur;\n\t\t\tln1.normalize();\n\t\t\tln2.normalize();\n\t\t\tPoint::dot(ln1,ln2, V);\n\t\t\tdouble cos_tho=-V.x-V.y;\n\t\t\tbool zero_degree = Point::negligible(cos_tho-1);\n\t\t\tbool d180_degree = cos_tho < -1+0.0001;\n\t\t\tbool smaller_than_30_degree = cos_tho > 0.8660254;\n\t\t\tchar result3 = 1;\n\t\t\t\n\t\t\tif ( (cos_tho < 0 && opt.joint==PLJ_bevel) ||\n\t\t\t     (opt.joint!=PLJ_bevel && opt.cap==PLC_round) ||\n\t\t\t     (opt.joint==PLJ_round)\n\t\t\t   )\n\t\t\t{\t//when greater than 90 degrees\n\t\t\t\tSL[i-1].bR *= 0.01;\n\t\t\t\tSL[i]  .bR *= 0.01;\n\t\t\t\tSL[i+1].bR *= 0.01;\n\t\t\t\t//to solve an overdraw in bevel and round joint\n\t\t\t}\n\t\t\t\n\t\t\tPoint::anchor_outward( T1, P_cur,P_nxt);\n\t\t\t\tR1.follow_signs(T1);\n\t\t\tPoint::anchor_outward( T21, P_cur,P_nxt);\n\t\t\t\tR21.follow_signs(T21);\n\t\t\t\tSL[i].T1.follow_signs(T21);\n\t\t\t\tSL[i].R1.follow_signs(T21);\n\t\t\tPoint::anchor_outward( T2, P_cur,P_las);\n\t\t\t\tR2.follow_signs(T2);\n\t\t\t\tSL[i].T.follow_signs(T2);\n\t\t\t\tSL[i].R.follow_signs(T2);\n\t\t\tPoint::anchor_outward( T31, P_cur,P_las);\n\t\t\t\tR31.follow_signs(T31);\n\t\t\t\n\t\t\t{ //must do intersection\n\t\t\t\tPoint interP, vP;\n\t\t\t\tresult3 = Point::intersect( P_las+T1, P_cur+T21,\n\t\t\t\t\t\t\tP_nxt+T31, P_cur+T2,\n\t\t\t\t\t\t\tinterP);\n\t\t\t\t\n\t\t\t\tif ( result3) {\n\t\t\t\t\tvP = interP - P_cur;\n\t\t\t\t\tSL[i].vP=vP;\n\t\t\t\t\tSL[i].vR=vP*(r/t);\n\t\t\t\t} else {\n\t\t\t\t\tSL[i].vP=SL[i].T;\n\t\t\t\t\tSL[i].vR=SL[i].R;\n\t\t\t\t\tDEBUG( \"intersection failed: cos(angle)=%.4f, angle=%.4f(degree)\\n\", cos_tho, acos(cos_tho)*180/3.14159);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tT1.opposite();\t\t//]inward\n\t\t\t\tR1.opposite();\n\t\t\tT21.opposite();\n\t\t\t\tR21.opposite();\n\t\t\tT2.opposite();\n\t\t\t\tR2.opposite();\n\t\t\tT31.opposite();\n\t\t\t\tR31.opposite();\n\t\t\t\n\t\t\t//make intersections\n\t\t\tPoint PR1,PR2, PT1,PT2;\n\t\t\tdouble pt1,pt2;\n\t\t\t\n\t\t\tchar result1r = Point::intersect( P_nxt-T31-R31, P_nxt+T31+R31,\n\t\t\t\t\t\tP_las+T1+R1, P_cur+T21+R21, //knife1\n\t\t\t\t\t\tPR1); //fade\n\t\t\tchar result2r = Point::intersect( P_las-T1-R1, P_las+T1+R1,\n\t\t\t\t\t\tP_nxt+T31+R31, P_cur+T2+R2, //knife2\n\t\t\t\t\t\tPR2);\n\t\t\tbool is_result1r = result1r == 1;\n\t\t\tbool is_result2r = result2r == 1;\n\t\t\t//\n\t\t\tchar result1t = Point::intersect( P_nxt-T31, P_nxt+T31,\n\t\t\t\t\t\tP_las+T1, P_cur+T21, //knife1_a\n\t\t\t\t\t\tPT1, 0,&pt1); //core\n\t\t\tchar result2t = Point::intersect( P_las-T1, P_las+T1,\n\t\t\t\t\t\tP_nxt+T31, P_cur+T2, //knife2_a\n\t\t\t\t\t\tPT2, 0,&pt2);\n\t\t\tbool is_result1t = result1t == 1;\n\t\t\tbool is_result2t = result2t == 1;\n\t\t\t//\n\t\t\tbool inner_sec = Point::intersecting( P_las+T1+R1, P_cur+T21+R21,\n\t\t\t\t\t\tP_nxt+T31+R31, P_cur+T2+R2);\n\t\t\t//\n\t\t\tif ( zero_degree)\n\t\t\t{\n\t\t\t\tbool pre_full = is_result1t;\n\t\t\t\topt.no_feather_at_cap=true;\n\t\t\t\tif ( pre_full)\n\t\t\t\t{\n\t\t\t\t\tsegment( SA, &opt, true,cap_last, opt.joint==PLJ_round?PLC_round:PLC_butt);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar ori_cap = opt.cap;\n\t\t\t\t\topt.cap = opt.joint==PLJ_round?PLC_round:PLC_butt;\n\t\t\t\t\tSA.P[0]=SA.P[1]; SA.P[1]=SA.P[2];\n\t\t\t\t\tSA.C[0]=SA.C[1]; SA.C[1]=SA.C[2];\n\t\t\t\t\tSA.W[0]=SA.W[1]; SA.W[1]=SA.W[2];\n\t\t\t\t\tsegment( SA, &opt, true,cap_last, ori_cap);\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t\n\t\t\tif ( (is_result1r | is_result2r) && !inner_sec)\n\t\t\t{\t//fade degeneration\n\t\t\t\tSL[i].degenR=true;\n\t\t\t\tSL[i].PT = is_result1r? PT1:PT2; //this is is_result1r!!\n\t\t\t\tSL[i].PR = is_result1r? PR1:PR2;\n\t\t\t\tSL[i].pt = float(is_result1r? pt1:pt2);\n\t\t\t\t\tif ( SL[i].pt < 0)\n\t\t\t\t\t\tSL[i].pt = cri_core_adapt;\n\t\t\t\tSL[i].pre_full = is_result1r;\n\t\t\t\tSL[i].R_full_degen = false;\n\t\t\t\t\n\t\t\t\tPoint P_nxt = P[i+1]; //override that in the parent scope\n\t\t\t\tPoint P_las = P[i-1];\n\t\t\t\tPoint PR;\n\t\t\t\tif ( opt.cap==PLC_rect || opt.cap==PLC_round)\n\t\t\t\t{\n\t\t\t\t\tP_nxt += SA.cap_end;\n\t\t\t\t\tP_las += SA.cap_start;\n\t\t\t\t}\n\t\t\t\tchar result2;\n\t\t\t\tif ( is_result1r)\n\t\t\t\t{\n\t\t\t\t\tresult2 = Point::intersect( P_nxt-T31-R31, P_nxt+T31,\n\t\t\t\t\t\tP_las+T1+R1, P_cur+T21+R21, //knife1\n\t\t\t\t\t\tPR); \t//fade\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult2 = Point::intersect( P_las-T1-R1, P_las+T1,\n\t\t\t\t\t\tP_nxt+T31+R31, P_cur+T2+R2, //knife2\n\t\t\t\t\t\tPR);\n\t\t\t\t}\n\t\t\t\tif ( result2 == 1)\n\t\t\t\t{\n\t\t\t\t\tSL[i].R_full_degen = true;\n\t\t\t\t\tSL[i].PR = PR;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif ( is_result1t | is_result2t)\n\t\t\t{\t//core degeneration\n\t\t\t\tSL[i].degenT=true;\n\t\t\t\tSL[i].pre_full=is_result1t;\n\t\t\t\tSL[i].PT = is_result1t? PT1:PT2;\n\t\t\t\tSL[i].pt = float(is_result1t? pt1:pt2);\n\t\t\t}\n\t\t\t\n\t\t\t//make joint\n\t\t\tSL[i].djoint = opt.joint;\n\t\t\tif ( opt.joint == PLJ_miter)\n\t\t\t\tif ( cos_tho >= cos_cri_angle)\n\t\t\t\t\tSL[i].djoint=PLJ_bevel;\n\t\t\t\n\t\t\t/*if ( varying_weight && smaller_than_30_degree)\n\t\t\t{\t//not sure why, but it appears to solve a visual bug for varing weight\n\t\t\t\tPoint interR,vR;\n\t\t\t\tchar result3 = Point::intersect( P_las-T1-R1, P_cur-T21-R21,\n\t\t\t\t\t\t\tP_nxt-T31-R31, P_cur-T2-R2,\n\t\t\t\t\t\t\tinterR);\n\t\t\t\tSL[i].vR = P_cur-interR-SL[i].vP;\n\t\t\t\tannotate(interR,C[i],9);\n\t\t\t\tdraw_vector(P_las-T1-R1, P_cur-T21-R21 - P_las+T1+R1,\"1\");\n\t\t\t\tdraw_vector(P_nxt-T31-R31, P_cur-T2-R2 - P_nxt+T31+R31,\"2\");\n\t\t\t}*/\n\t\t\t\n\t\t\tif ( d180_degree | !result3)\n\t\t\t{\t//to solve visual bugs 3 and 1.1\n\t\t\t\t//efficiency: if color and weight is same as previous and next point\n\t\t\t\t// ,do not generate vertices\n\t\t\t\tsame_side_of_line( SL[i].R, SL[i-1].R, P_cur,P_las);\n\t\t\t\t\tSL[i].T.follow_signs(SL[i].R);\n\t\t\t\tSL[i].vP=SL[i].T;\n\t\t\t\tSL[i].T1.follow_signs(SL[i].T);\n\t\t\t\tSL[i].R1.follow_signs(SL[i].T);\n\t\t\t\tSL[i].vR=SL[i].R;\n\t\t\t\tSL[i].djoint=PLJ_miter;\n\t\t\t}\n\t\t} //2nd to 2nd last point\n\t}\n\t\n\t{\tint i=2;\n\n\t\tdouble r,t;\n\t\tmake_T_R_C( P[i-1],P[i], &T2,&R2,0,weight[i],opt,  &r,&t,0);\n\t\t\tsame_side_of_line( R2, SL[i-1].R, P[i-1],P[i]);\n\t\t\t\tT2.follow_signs(R2);\n\t\t\n\t\tSL[i].djoint=opt.cap;\n\t\tSL[i].T=T2;\n\t\tSL[i].R=R2;\n\t\tSL[i].t=(float)t;\n\t\tSL[i].r=(float)r;\n\t\tSL[i].degenT = false;\n\t\tSL[i].degenR = false;\n\t}\n\t\n\tif( cap_first || cap_last)\n\t\tanchor_cap( SA.P,SA.C, SA.SL,SA.vah, SA.cap_start,SA.cap_end);\n\tanchor_late( SA.P,SA.C, SA.SL,SA.vah, SA.cap_start,SA.cap_end);\n\treturn 1;\n} //anchor\n\n#ifdef VASE_RENDERER_EXPER\ntemplate <typename T>\nclass circular_array\n{\n\tconst int size;\n\tint cur; //current\n\tT* array;\npublic:\n\tcircular_array(int size_) : size(size_)\n\t{\n\t\tarray = new T[size];\n\t\tcur = 0;\n\t}\n\t\n\t~circular_array()\n\t{\n\t\tdelete[] array;\n\t}\n\t\n\tvoid push( T obj)\n\t{\n\t\tarray[cur] = obj;\n\t\tmove(1);\n\t}\n\t\n\tint get_size() const\n\t\t{ return size;}\n\t\n\tint get_i( int i) const //get valid index relative to current\n\t{\n\t\tint des = cur + i%size;\n\t\tif ( des > size-1)\n\t\t{\n\t\t\tdes -= size;\n\t\t}\n\t\tif ( des < 0)\n\t\t{\n\t\t\tdes = size+i;\n\t\t}\n\t\treturn des;\n\t}\n\t\n\tvoid move( int i) //move current relatively\n\t{\n\t\tcur = get_i(i);\n\t}\n\t\n\tT& operator[] (int i) //get element at relative position\n\t{\n\t\treturn array[get_i(i)];\n\t}\n};\n#endif //VASE_RENDERER_EXPER\n\nstruct polyline_inopt\n{\n\tbool const_color;\n\tbool const_weight;\n\tbool no_cap_first;\n\tbool no_cap_last;\n\tbool join_first;\n\tbool join_last;\n\tdouble* segment_length; //array of length of each segment\n};\n\nvoid poly_point_inter( const Point* P, const Color* C, const double* W, const polyline_inopt* inopt,\n\t\tPoint& p, Color& c, double& w,\n\t\tint at, double t)\n{\n\t#define color(I)  C[inopt&&inopt->const_color?0: I]\n\t#define weight(I) W[inopt&&inopt->const_weight?0: I]\n\tif( t==0.0)\n\t{\n\t\tp = P[at];\n\t\tc = color(at);\n\t\tw = weight(at);\n\t}\n\telse if( t==1.0)\n\t{\n\t\tp = P[at+1];\n\t\tc = color(at+1);\n\t\tw = weight(at+1);\n\t}\n\telse\n\t{\n\t\tp = (P[at]+P[at+1]) * t;\n\t\tc = Color_between(color(at),color(at+1), t);\n\t\tw = (weight(at)+weight(at+1)) * t;\n\t}\n\t#undef color\n\t#undef weight\n}\n\nvoid polyline_approx(\n\tconst Vec2* points,\n\tconst Color* C,\n\tconst double* W,\n\tint length,\n\tconst polyline_opt* opt,\n\tconst polyline_inopt* inopt)\n{\n\tconst Point* P=(Point*)points;\n\tbool cap_first= inopt? !inopt->no_cap_first :true;\n\tbool cap_last=  inopt? !inopt->no_cap_last  :true;\n\tdouble* seg_len=inopt? inopt->segment_length: 0;\n\n\tst_anchor SA1,SA2;\n\tvertex_array_holder vcore;  //curve core\n\tvertex_array_holder vfadeo; //outer fade\n\tvertex_array_holder vfadei; //inner fade\n\tvcore.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\tvfadeo.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\tvfadei.set_gl_draw_mode(GL_TRIANGLE_STRIP);\n\n\tif( length<2)\n\t\treturn;\n\n\t#define color(I)  C[inopt&&inopt->const_color?0: I]\n\t#define weight(I) W[inopt&&inopt->const_weight?0: I]\n\n\tfor( int i=1; i<length-1; i++)\n\t{\n\t\tdouble t,r;\n\t\tdetermine_t_r(weight(i),t,r);\n\t\tif ( opt && opt->feather && !opt->no_feather_at_core)\n\t\t\tr*=opt->feathering;\n\t\tPoint V=P[i]-P[i-1];\n\t\tV.perpen();\n\t\tV.normalize();\n\t\tPoint F=V*r;\n\t\tV*=t;\n\t\tvcore.push( P[i]+V,  color(i));\n\t\tvcore.push( P[i]-V,  color(i));\n\t\tvfadeo.push( P[i]+V, color(i));\n\t\tvfadeo.push( P[i]+V+F, color(i), 1);\n\t\tvfadei.push( P[i]-V,   color(i));\n\t\tvfadei.push( P[i]-V-F, color(i), 1);\n\t}\n\tPoint P_las,P_fir;\n\tColor C_las,C_fir;\n\tdouble W_las,W_fir;\n\tpoly_point_inter( P,C,W,inopt, P_las,C_las,W_las, length-2, 0.5);\n\t{\n\t\tdouble t,r;\n\t\tdetermine_t_r(W_las,t,r);\n\t\tif ( opt && opt->feather && !opt->no_feather_at_core)\n\t\t\tr*=opt->feathering;\n\t\tPoint V=P[length-1]-P[length-2];\n\t\tV.perpen();\n\t\tV.normalize();\n\t\tPoint F=V*r;\n\t\tV*=t;\n\t\tvcore.push( P_las+V, C_las);\n\t\tvcore.push( P_las-V, C_las);\n\t\tvfadeo.push( P_las+V, C_las);\n\t\tvfadeo.push( P_las+V+F, C_las, 1);\n\t\tvfadei.push( P_las-V, C_las);\n\t\tvfadei.push( P_las-V-F, C_las, 1);\n\t}\n\n\t//first caps\n\t{\n\t\tpoly_point_inter( P,C,W,inopt, P_fir,C_fir,W_fir, 0, inopt&&inopt->join_first? 0.5:0.0);\n\t\tSA1.P[0] = P_fir;\n\t\tSA1.P[1] = P[1];\n\t\tSA1.C[0] = C_fir;\n\t\tSA1.C[1] = color(1);\n\t\tSA1.W[0] = W_fir;\n\t\tSA1.W[1] = weight(1);\n\t\tsegment( SA1, opt, cap_first,false);\n\t}\n\t//last cap\n\tif( !(inopt&&inopt->join_last))\n\t{\n\t\tSA2.P[0] = P_las;\n\t\tSA2.P[1] = P[length-1];\n\t\tSA2.C[0] = C_las;\n\t\tSA2.C[1] = color(length-1);\n\t\tSA2.W[0] = W_las;\n\t\tSA2.W[1] = weight(length-1);\n\t\tsegment( SA2, opt, false,cap_last);\n\t}\n\n\t#undef color\n\t#undef weight\n\n\tif( opt && opt->tess && opt->tess->tessellate_only && opt->tess->holder)\n\t{\n\t\tvertex_array_holder& holder = *(vertex_array_holder*)opt->tess->holder;\n\t\tholder.push(vcore);\n\t\tholder.push(vfadeo);\n\t\tholder.push(vfadei);\n\t\tholder.push(SA1.vah);\n\t\tholder.push(SA2.vah);\n\t}\n\telse\n\t{\n\t\tvcore.draw();\n\t\tvfadeo.draw();\n\t\tvfadei.draw();\n\t\tSA1.vah.draw();\n\t\tSA2.vah.draw();\n\t}\n\n\tif ( opt && opt->tess && opt->tess->triangulation)\n\t{\n\t\tvcore.draw_triangles();\n\t\tvfadeo.draw_triangles();\n\t\tvfadei.draw_triangles();\n\t\tSA1.vah.draw_triangles();\n\t\tSA2.vah.draw_triangles();\n\t}\n}\n\nvoid polyline_exact(\n\tconst Vec2* P,\n\tconst Color* C,\n\tconst double* W,\n\tint size_of_P,\n\tconst polyline_opt* opt,\n\tconst polyline_inopt* inopt)\n{\n\tbool cap_first= inopt? !inopt->no_cap_first :true;\n\tbool cap_last=  inopt? !inopt->no_cap_last  :true;\n\tbool join_first = inopt && inopt->join_first;\n\tbool join_last =  inopt && inopt->join_last;\n\n\t#define color(I)  C[inopt&&inopt->const_color?0: I]\n\t#define weight(I) W[inopt&&inopt->const_weight?0: I]\n\n\tPoint mid_l, mid_n; //the last and the next mid point\n\tColor c_l, c_n;\n\tdouble w_l, w_n;\n\t{\t//init for the first anchor\n\t\tpoly_point_inter( (Point*)P,C,W,inopt, mid_l,c_l,w_l, 0, join_first?0.5:0);\n\t}\n\n\tst_anchor SA;\n\tif ( size_of_P == 2)\n\t{\n\t\tSA.P[0] = P[0];\n\t\tSA.P[1] = P[1];\n\t\tSA.C[0] = color(0);\n\t\tSA.C[1] = color(1);\n\t\tSA.W[0] = weight(0);\n\t\tSA.W[1] = weight(1);\n\t\tsegment( SA, opt, cap_first, cap_last);\n\t}\n\telse\n\tfor ( int i=1; i<size_of_P-1; i++)\n\t{\n\t\tif ( i==size_of_P-2 && !join_last)\n\t\t\tpoly_point_inter( (Point*)P,C,W,inopt, mid_n,c_n,w_n, i, 1.0);\n\t\telse\n\t\t\tpoly_point_inter( (Point*)P,C,W,inopt, mid_n,c_n,w_n, i, 0.5);\n\n\t\tSA.P[0]=mid_l.vec(); SA.C[0]=c_l;  SA.W[0]=w_l;\n\t\tSA.P[2]=mid_n.vec(); SA.C[2]=c_n;  SA.W[2]=w_n;\n\n\t\tSA.P[1]=P[i];\n\t\tSA.C[1]=color(i);\n\t\tSA.W[1]=weight(i);\n\n\t\tanchor( SA, opt, i==1&&cap_first, i==size_of_P-2&&cap_last);\n\n\t\tmid_l = mid_n;\n\t\tc_l = c_n;\n\t\tw_l = w_n;\n\t}\n\t//draw or not\n\tif( opt && opt->tess && opt->tess->tessellate_only && opt->tess->holder)\n\t\t(*(vertex_array_holder*)opt->tess->holder).push(SA.vah);\n\telse\n\t\tSA.vah.draw();\n\t//draw triangles\n\tif( opt && opt->tess && opt->tess->triangulation)\n\t\tSA.vah.draw_triangles();\n\n\t#undef color\n\t#undef weight\n}\n\nvoid polyline_range(\n\tconst Vec2* P,\n\tconst Color* C,\n\tconst double* W,\n\tint length,\n\tconst polyline_opt* opt,\n\tconst polyline_inopt* in_options,\n\tint from, int to,\n\tbool approx)\n{\n\tpolyline_inopt inopt={0};\n\tif( in_options) inopt=*in_options;\n\tif( from>0) from-=1;\n\tinopt.join_first = from!=0;\n\tinopt.join_last = to!=(length-1);\n\tinopt.no_cap_first = inopt.no_cap_first || inopt.join_first;\n\tinopt.no_cap_last = inopt.no_cap_last || inopt.join_last;\n\n\tif( approx)\n\t\tpolyline_approx( P+from, C+(inopt.const_color?0:from), W+(inopt.const_weight?0:from), to-from+1, opt, &inopt);\n\telse\n\t\tpolyline_exact ( P+from, C+(inopt.const_color?0:from), W+(inopt.const_weight?0:from), to-from+1, opt, &inopt);\n}\n\nvoid polyline(\n\tconst Vec2* PP,  //pointer to array of point of a polyline\n\tconst Color* C,  //array of color\n\tconst double* W, //array of weight\n\tint length, //size of the buffer P\n\tconst polyline_opt* options, //options\n\tconst polyline_inopt* in_options) //internal options\n{\n\tpolyline_opt   opt={0};\n\tpolyline_inopt inopt={0};\n\tif( options)    opt=*options;\n\tif( in_options) inopt=*in_options;\n\n\t/* if( opt.fallback)\n\t{\n\t\tbackend::polyline(PP,C[0],W[0],length,0);\n\t\treturn;\n\t} */\n\n\tif( opt.cap >= 10)\n\t{\n\t\tchar dec=(opt.cap/10)*10;\n\t\tif( dec==PLC_first || dec==PLC_none)\n\t\t\tinopt.no_cap_last=true;\n\t\tif( dec==PLC_last || dec==PLC_none)\n\t\t\tinopt.no_cap_first=true;\n\t\topt.cap -= dec;\n\t}\n\n\tif( inopt.const_weight && W[0] < cri_segment_approx)\n\t{\n\t\tpolyline_exact(PP,C,W,length,&opt,&inopt);\n\t\treturn;\n\t}\n\n\tconst Point* P=(Point*)PP;\n\tint A=0,B=0;\n\tbool on=false;\n\tfor( int i=1; i<length-1; i++)\n\t{\n\t\tPoint V1=P[i]-P[i-1];\n\t\tPoint V2=P[i+1]-P[i];\n\t\tdouble len=0.0;\n\t\tif( inopt.segment_length)\n\t\t{\n\t\t\tV1 /= inopt.segment_length[i];\n\t\t\tV2 /= inopt.segment_length[i+1];\n\t\t\tlen += (inopt.segment_length[i]+inopt.segment_length[i+1])*0.5;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlen += V1.normalize()*0.5;\n\t\t\tlen += V2.normalize()*0.5;\n\t\t}\n\t\tdouble costho = V1.x*V2.x+V1.y*V2.y;\n\t\t//double angle = acos(costho)*180/vaser_pi;\n\t\tconst double cos_a = cos(15*vaser_pi/180);\n\t\tconst double cos_b = cos(10*vaser_pi/180);\n\t\tconst double cos_c = cos(25*vaser_pi/180);\n\t\tdouble weight = W[inopt.const_weight?0:i];\n\t\tbool approx = false;\n\t\tif( (weight<7 && costho>cos_a) ||\n\t\t\t(costho>cos_b) || //when the angle difference at an anchor is smaller than a critical degree, do polyline approximation\n\t\t\t(len<weight && costho>cos_c) ) //when vector length is smaller than weight, do approximation\n\t\t\tapprox = true;\n\t\tif( approx && !on)\n\t\t{\n\t\t\tA=i; if( A==1) A=0;\n\t\t\ton=true;\n\t\t\tif( A>1)\n\t\t\t\tpolyline_range(PP,C,W,length,&opt,&inopt,B,A,false);\n\t\t}\n\t\telse if( !approx && on)\n\t\t{\n\t\t\tB=i;\n\t\t\ton=false;\n\t\t\tpolyline_range(PP,C,W,length,&opt,&inopt,A,B,true);\n\t\t}\n\t}\n\tif( on && B<length-1)\n\t{\n\t\tB=length-1;\n\t\tpolyline_range(PP,C,W,length,&opt,&inopt,A,B,true);\n\t}\n\telse if( !on && A<length-1)\n\t{\n\t\tA=length-1;\n\t\tpolyline_range(PP,C,W,length,&opt,&inopt,B,A,false);\n\t}\n}\n\n#undef push_quad\n#undef push_quadf\n\n} //sub namespace VASErin\n\n//export implementations\n\nvoid polyline( const Vec2* P, const Color* C, const double* W, int length, const polyline_opt* opt)\n{\n\tVASErin::polyline(P,C,W,length,opt,0);\n}\nvoid polyline( const Vec2* P, Color C, double W, int length, const polyline_opt* opt) //constant color and weight\n{\n\tVASErin::polyline_inopt inopt={0};\n\tinopt.const_color=true;\n\tinopt.const_weight=true;\n\tVASErin::polyline(P,&C,&W,length,opt,&inopt);\n}\nvoid polyline( const Vec2* P, const Color* C, double W, int length, const polyline_opt* opt) //constant weight\n{\n\tVASErin::polyline_inopt inopt={0};\n\tinopt.const_weight=true;\n\tVASErin::polyline(P,C,&W,length,opt,&inopt);\n}\nvoid polyline( const Vec2* P, Color C, const double* W, int length, const polyline_opt* opt) //constant color\n{\n\tVASErin::polyline_inopt inopt={0};\n\tinopt.const_color=true;\n\tVASErin::polyline(P,&C,W,length,opt,&inopt);\n}\n\nvoid segment(  Vec2 P1, Vec2 P2, Color C1, Color C2, double W1, double W2, const polyline_opt* options)\n{\n\tVec2   AP[2];\n\tColor  AC[2];\n\tdouble AW[2];\n\t\tAP[0] = P1; AC[0] = C1; AW[0] = W1;\n\t\tAP[1] = P2; AC[1] = C2; AW[1] = W2;\n\tpolyline( AP, AC, AW, 2, options);\n}\nvoid segment(  Vec2 P1, Vec2 P2, Color C, double W, const polyline_opt* options) //constant color and weight\n{\n\tVec2   AP[2];\n\t\tAP[0] = P1;\n\t\tAP[1] = P2;\n\tpolyline( AP, C, W, 2, options);\n}\nvoid segment(  Vec2 P1, Vec2 P2, Color C1, Color C2, double W, const polyline_opt* options) //constant weight\n{\n\tVec2   AP[2];\n\tColor  AC[2];\n\tdouble AW[2];\n\t\tAP[0] = P1; AC[0] = C1; AW[0] = W;\n\t\tAP[1] = P2; AC[1] = C2; AW[1] = W;\n\tpolyline( AP, AC, AW, 2, options);\n}\nvoid segment(  Vec2 P1, Vec2 P2, Color C, double W1, double W2, const polyline_opt* options) //const color\n{\n\tVec2   AP[2];\n\tColor  AC[2];\n\tdouble AW[2];\n\t\tAP[0] = P1; AC[0] = C; AW[0] = W1;\n\t\tAP[1] = P2; AC[1] = C; AW[1] = W2;\n\tpolyline( AP, AC, AW, 2, options);\n}\n"
  },
  {
    "path": "cpp/vaser/vaser.cpp",
    "content": "#ifndef VASER_CPP\n#define VASER_CPP\n\n#include \"vaser.h\"\n\n#ifdef VASER_DEBUG\n\t#define DEBUG printf\n#else\n\t#define DEBUG ;//\n#endif\n\n#include <math.h>\n#include <vector>\n#include <stdlib.h>\n\nnamespace VASEr\n{\nnamespace VASErin\n{\t//VASEr internal namespace\nconst double vaser_min_alw=0.00000000001; //smallest value not regarded as zero\nconst Color default_color = {0,0,0,1};\nconst double default_weight = 1.0;\n#include \"point.h\"\n#include \"color.h\"\nclass vertex_array_holder;\n#include \"backend.h\"\n#include \"vertex_array_holder.h\"\n#include \"agg_curve4.cpp\"\n}\n#include \"opengl.cpp\"\n#include \"polyline.cpp\"\n#include \"gradient.cpp\"\n#include \"curve.cpp\"\n\n} //namespace VASEr\n\n#undef DEBUG\n#endif\n"
  },
  {
    "path": "cpp/vaser/vaser.h",
    "content": "#ifndef VASER_H\n#define VASER_H\n/* Vase Renderer first draft, version 0.3 */\n\n/* Basic usage\n* You should provide these structs before any vase_renderer include, using\nstruct Vec2 { double x,y;};\nstruct Color { float r,g,b,a;};\n* or\ntypedef your_vec2 Vec2;\ntypedef your_color Color;\n*/\n\nnamespace VASEr\n{\nconst double vaser_pi=3.141592653589793;\n\nstruct gradient_stop\n{\n\tdouble t; //position\n\tchar type; //GS_xx\n\tunion\n\t{\n\t\tColor color;\n\t\tdouble weight;\n\t};\n};\n\tconst char GS_none  =0;\n\tconst char GS_rgba  =1;\n\tconst char GS_rgb   =2; //rgb only\n\tconst char GS_alpha =3;\n\tconst char GS_weight=4;\nstruct gradient\n{\n\tgradient_stop* stops; //array must be sorted in ascending order of t\n\tint length; //number of stops\n\tchar unit; //use_GD_XX\n};\n\tconst char GD_ratio =0; //default\n\tconst char GD_length=1;\n\nstruct Image\n{\n\tunsigned char* buf; //must **free** buffer manually\n\tshort width;\n\tshort height;\n};\n\nclass renderer\n{\npublic:\n\tstatic void init();\n\tstatic void before();\n\tstatic void after();\n\tstatic Image get_image();\n};\n\nstruct tessellator_opt\n{\n\t//set the whole structure to 0 will give default options\n\tbool triangulation;\n\tchar parts; //use TS_xx\n\tbool tessellate_only;\n\tvoid* holder; //used as (VASErin::vertex_array_holder*) if tessellate_only is true\n};\n\t//for tessellator_opt.parts\n\tconst char TS_core_fade =0; //default\n\tconst char TS_core      =1;\n\tconst char TS_outer_fade=2;\n\tconst char TS_inner_fade=3;\n\nstruct polyline_opt\n{\n\t//set the whole structure to 0 will give default options\n\tconst tessellator_opt* tess;\n\tchar joint; //use PLJ_xx\n\tchar cap;   //use PLC_xx\n\tbool feather;\n\t\tdouble feathering;\n\t\tbool no_feather_at_cap;\n\t\tbool no_feather_at_core;\n};\n\t//for polyline_opt.joint\n\tconst char PLJ_miter =0; //default\n\tconst char PLJ_bevel =1;\n\tconst char PLJ_round =2;\n\t//for polyline_opt.cap\n\tconst char PLC_butt  =0; //default\n\tconst char PLC_round =1;\n\tconst char PLC_square=2;\n\tconst char PLC_rect  =3;\n\tconst char PLC_both  =0; //default\n\tconst char PLC_first =10;\n\tconst char PLC_last  =20;\n\tconst char PLC_none  =30;\n\nvoid polyline( const Vec2*, const Color*, const double*, int length, const polyline_opt*);\nvoid polyline( const Vec2*, Color, double W, int length, const polyline_opt*); //constant color and weight\nvoid polyline( const Vec2*, const Color*, double W, int length, const polyline_opt*); //constant weight\nvoid polyline( const Vec2*, Color, const double* W, int length, const polyline_opt*); //constant color\nvoid segment( Vec2, Vec2, Color, Color, double W1, double W2, const polyline_opt*);\nvoid segment( Vec2, Vec2, Color, double W, const polyline_opt*); //constant color and weight\nvoid segment( Vec2, Vec2, Color, Color, double W, const polyline_opt*); //constant weight\nvoid segment( Vec2, Vec2, Color, double W1, double W2, const polyline_opt*); //const color\n\nstruct polybezier_opt\n{\n\t//set the whole structure to 0 will give default options\n\tconst polyline_opt* poly;\n};\n\nvoid polybezier( const Vec2*, const gradient*, int length, const polybezier_opt*);\nvoid polybezier( const Vec2*, Color, double W, int length, const polybezier_opt*);\n\n} //namespace VASEr\n\n#endif\n"
  },
  {
    "path": "cpp/vaser/vertex_array_holder.h",
    "content": "#ifndef VASER_VERTEX_ARRAY_HOLDER_H\n#define VASER_VERTEX_ARRAY_HOLDER_H\n\nclass vertex_array_holder\n{\npublic:\n\tint count; //counter\n\tint glmode; //drawing mode in opengl\n\tbool jumping;\n\tstd::vector<float> vert; //because it holds 2d vectors\n\tstd::vector<float> color; //RGBA\n\n\tvertex_array_holder()\n\t{\n\t\tcount = 0;\n\t\tglmode = GL_TRIANGLES;\n\t\tjumping = false;\n\t}\n\t\n\tvoid set_gl_draw_mode( int gl_draw_mode)\n\t{\n\t\tglmode = gl_draw_mode;\n\t}\n\t\n\tvoid clear()\n\t{\n\t\tcount = 0;\n\t}\n\tvoid move( int a, int b) //move b into a\n\t{\n\t\tvert[a*2]   = vert[b*2];\n\t\tvert[a*2+1] = vert[b*2+1];\n\t\t\n\t\tcolor[a*4]  = color[b*4];\n\t\tcolor[a*4+1]= color[b*4+1];\n\t\tcolor[a*4+2]= color[b*4+2];\n\t\tcolor[a*4+3]= color[b*4+3];\n\t}\n\tvoid replace( int a, Point P, Color C)\n\t{\n\t\tvert[a*2]   = P.x;\n\t\tvert[a*2+1] = P.y;\n\t\t\n\t\tcolor[a*4]  = C.r;\n\t\tcolor[a*4+1]= C.g;\n\t\tcolor[a*4+2]= C.b;\n\t\tcolor[a*4+3]= C.a;\n\t}\n\t\n\t/* int draw_and_flush()\n\t{\n\t\tint& i = count;\n\t\tdraw();\n\t\tswitch( glmode)\n\t\t{\n\t\t\tcase GL_POINTS:\n\t\t\t\ti=0;\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase GL_LINES:\n\t\t\t\tif ( i%2 == 0) {\n\t\t\t\t\ti=0;\n\t\t\t\t} else {\n\t\t\t\t\tgoto copy_the_last_point;\n\t\t\t\t}\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase GL_TRIANGLES:\n\t\t\t\tif ( i%3 == 0) {\n\t\t\t\t\ti=0;\n\t\t\t\t} else if ( i%3 == 1) {\n\t\t\t\t\tgoto copy_the_last_point;\n\t\t\t\t} else {\n\t\t\t\t\tgoto copy_the_last_2_points;\n\t\t\t\t}\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase GL_LINE_STRIP: case GL_LINE_LOOP:\n\t\t\t//for line loop it is not correct\n\t\t\tcopy_the_last_point:\n\t\t\t\tmove(0,MAX_VERT-1);\n\t\t\t\ti=1;\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase GL_TRIANGLE_STRIP:\n\t\t\tcopy_the_last_2_points:\n\t\t\t\tmove(0,MAX_VERT-2);\n\t\t\t\tmove(1,MAX_VERT-1);\n\t\t\t\ti=2;\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase GL_TRIANGLE_FAN:\n\t\t\t\t//retain the first point,\n\t\t\t\t// and copy the last point\n\t\t\t\tmove(1,MAX_VERT-1);\n\t\t\t\ti=2;\n\t\t\tbreak;\n\t\t\t\n\t\t\tcase GL_QUAD_STRIP:\n\t\t\tcase GL_QUADS:\n\t\t\tcase GL_POLYGON:\n\t\t\t//let it be and I cannot help\n\t\t\t\ti=0;\n\t\t\tbreak;\n\t\t}\n\t\tif ( i == MAX_VERT) //as a double check\n\t\t\ti=0;\n\t}*/\n\t\n\tint push( const Point& P, const Color& cc, bool trans=false)\n\t{\n\t\tint cur = count;\n\t\tvert.push_back(P.x);\n\t\tvert.push_back(P.y);\n\t\tcolor.push_back(cc.r);\n\t\tcolor.push_back(cc.g);\n\t\tcolor.push_back(cc.b);\n\t\tcolor.push_back(trans?0.0f:cc.a);\n\n\t\tcount++;\n\t\tif ( jumping)\n\t\t{\n\t\t\tjumping=false;\n\t\t\trepeat_last_push();\n\t\t}\n\t\treturn cur;\n\t}\n\t\n\tvoid push3( const Point& P1, const Point& P2, const Point& P3,\n\t\t\tconst Color& C1, const Color& C2, const Color& C3,\n\t\t\tbool trans1=0, bool trans2=0, bool trans3=0)\n\t{\n\t\tpush( P1,C1,trans1);\n\t\tpush( P2,C2,trans2);\n\t\tpush( P3,C3,trans3);\n\t}\n\t\n\tvoid push( const vertex_array_holder& hold)\n\t{\n\t\tif ( glmode == hold.glmode)\n\t\t{\n\t\t\tcount += hold.count;\n\t\t\tvert.insert(vert.end(), hold.vert.begin(), hold.vert.end());\n\t\t\tcolor.insert(color.end(), hold.color.begin(), hold.color.end());\n\t\t}\n\t\telse if ( glmode == GL_TRIANGLES &&\n\t\t\thold.glmode == GL_TRIANGLE_STRIP)\n\t\t{\t\t\n\t\t\tint& a = count;\n\t\t\tfor (int b=2; b < hold.count; b++)\n\t\t\t{\n\t\t\t\tfor ( int k=0; k<3; k++,a++)\n\t\t\t\t{\n\t\t\t\t\tint B = b-2 + k;\n\t\t\t\t\tvert.push_back(hold.vert[B*2]);\n\t\t\t\t\tvert.push_back(hold.vert[B*2+1]);\n\t\t\t\t\tcolor.push_back(hold.color[B*4]);\n\t\t\t\t\tcolor.push_back(hold.color[B*4+1]);\n\t\t\t\t\tcolor.push_back(hold.color[B*4+2]);\n\t\t\t\t\tcolor.push_back(hold.color[B*4+3]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDEBUG( \"vertex_array_holder:push: unknown type\\n\");\n\t\t}\n\t}\n\t\n\tPoint get(int i)\n\t{\n\t\tPoint P;\n\t\tP.x = vert[i*2];\n\t\tP.y = vert[i*2+1];\n\t\treturn P;\n\t}\n\tColor get_color(int b)\n\t{\n\t\tColor C;\n\t\tC.r = color[b*4];\n\t\tC.g = color[b*4+1];\n\t\tC.b = color[b*4+2];\n\t\tC.a = color[b*4+3];\n\t\treturn C;\n\t}\n\tPoint get_relative_end(int di=-1)\n\t{\t//di=-1 is the last one\n\t\tint i = count+di;\n\t\tif ( i<0) i=0;\n\t\tif ( i>=count) i=count-1;\n\t\treturn get(i);\n\t}\n\tvoid repeat_last_push()\n\t{\n\t\tPoint P;\n\t\tColor cc;\n\t\t\n\t\tint i = count-1;\n\t\t\n\t\tP.x = vert[i*2];\n\t\tP.y = vert[i*2+1];\n\t\tcc.r = color[i*4];\n\t\tcc.g = color[i*4+1];\n\t\tcc.b = color[i*4+2];\n\t\tcc.a = color[i*4+3];\n\t\t\n\t\tpush(P,cc);\n\t}\n\tvoid jump() //to make a jump in triangle strip by degenerated triangles\n\t{\n\t\tif ( glmode == GL_TRIANGLE_STRIP)\n\t\t{\n\t\t\trepeat_last_push();\n\t\t\tjumping=true;\n\t\t}\n\t}\n\tvoid draw()\n\t{\n\t\tbackend::vah_draw(*this);\n\t}\n\tvoid draw_triangles()\n\t{\n\t\tColor col={1 , 0, 0, 0.5};\n\t\tif ( glmode == GL_TRIANGLES)\n\t\t{\n\t\t\tfor ( int i=0; i<count; i++)\n\t\t\t{\n\t\t\t\tPoint P[4];\n\t\t\t\tP[0] = get(i); i++;\n\t\t\t\tP[1] = get(i); i++;\n\t\t\t\tP[2] = get(i);\n\t\t\t\tP[3] = P[0];\n\t\t\t\tpolyline((Vec2*)P,col,1.0,4,0);\n\t\t\t}\n\t\t}\n\t\telse if ( glmode == GL_TRIANGLE_STRIP)\n\t\t{\n\t\t\tfor ( int i=2; i<count; i++)\n\t\t\t{\n\t\t\t\tPoint P[3];\n\t\t\t\tP[0] = get(i-2);\n\t\t\t\tP[1] = get(i);\n\t\t\t\tP[2] = get(i-1);\n\t\t\t\tpolyline((Vec2*)P,col,1.0,3,0);\n\t\t\t}\n\t\t}\n\t}\n\tvoid swap(vertex_array_holder& B)\n\t{\n\t\tint hold_count=count;\n\t\tint hold_glmode=glmode;\n\t\tbool hold_jumping=jumping;\n\t\tcount = B.count;\n\t\tglmode = B.glmode;\n\t\tjumping = B.jumping;\n\t\tB.count = hold_count;\n\t\tB.glmode = hold_glmode;\n\t\tB.jumping = hold_jumping;\n\t\tvert.swap(B.vert);\n\t\tcolor.swap(B.color);\n\t}\n};\n\n#endif\n"
  },
  {
    "path": "cpp/workbench/base.html",
    "content": "<body>\n<script src='raphael-min.js'></script>\n<script src='dragger.js'></script>\n<script src='drawer.js'></script>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n<div class='side_pane'>\n\tThis is description.\n</div>\n\n<script>\n//an application contains:\n//-TP (drawer)\n//-DG (dragger)\n//-canvas1 (html element)\n//-rl (Raphael canvas)\nvar app_count=0;\n\nfunction demo_application1()\n{\n\tvar i=this.i=app_count;\n\tvar parent=this;\n\tapp_count++;\n\tdocument.write(\"<div class='main_canvas' id='canvas\"+i+\"'></div>\");\n\t\n\tthis.TP = new drawer();\n\tthis.DG = new dragger(this.TP,'canvas'+i);\n\tthis.rl = Raphael('canvas'+i);\n\t//\n\tthis.TP.px=[30,30,200,200];\n\tthis.TP.py=[100,200,100,200];\n\t\n\tthis.TP.redraw=function()\n\t{\n\t\tvar rl=parent.rl;\n\t\tvar TP=parent.TP;\n\t\trl.clear();\n\t\trl.rect(0,0,800-1,600-1);\n\t\t\n\t\t//drawings,,,\n\t\trl.text(120,80,\"I am application \"+parent.i+\"!\");\n\t\tfor ( var i=0; i<4; i++)\n\t\t{\n\t\t\tTP.drawpoint(TP.px[i],TP.py[i], 4,0);\n\t\t}\n\t\t\n\t\tTP.drawpath(rl);\n\t}\n\tthis.TP.redraw();\n}\n\napp1 = new demo_application1();\n//app2 = new demo_application1();\n</script>\n</body>\n"
  },
  {
    "path": "cpp/workbench/dragger.js",
    "content": "function dragger(TP,div_id)\n{\n\tthis.drag=-1;\n\tthis.TP=TP;\n\n\tthis.onMouseDown = function(e)\n\t{\n\t\tif (e.token=='in') {\n\t\t\tthis.parent=e.parent;\n\t\t} else {\n\t\t\tvar T=this.parent;\n\t\t\tvar TP=T.TP;\n\n\t\t\te=e?e:event;\n\t\t\tT.xmouse=e.clientX-T.canx+document.body.scrollLeft;\n\t\t\tT.ymouse=e.clientY-T.cany+document.body.scrollTop;\n\t\t\tT.lastxmouse=T.xmouse;\n\t\t\tT.lastymouse=T.ymouse;\n\t\t\t//\n\t\t\tfor ( var i=0; i<TP.px.length; i++)\n\t\t\t{\n\t\t\t\tif (Math.abs(T.xmouse - TP.px[i]) < 12 && Math.abs(T.ymouse - TP.py[i]) < 12)\n\t\t\t\t{\n\t\t\t\t\tT.drag = i;\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tT.drag = -1;\n\t\t}\n\t}\n\n\tthis.onMouseUp = function(e)\n\t{\n\t\tif (e.token=='in') {\n\t\t\tthis.parent=e.parent;\n\t\t} else {\n\t\t\tvar T=this.parent;\n\t\t\tvar TP=T.TP;\n\n\t\t\tT.drag=-1;\n\t\t}\n\t}\n\tthis.onMouseMove = function(e)\n\t{\n\t\tif (e.token=='in') {\n\t\t\tthis.parent=e.parent;\n\t\t} else {\n\t\t\tvar T=this.parent;\n\t\t\tvar TP=T.TP;\n\n\t\t\te=e?e:event;\n\t\t\tT.xmouse=e.clientX-T.canx+document.body.scrollLeft;\n\t\t\tT.ymouse=e.clientY-T.cany+document.body.scrollTop;\n\t\t\tif ( T.drag!=-1)\n\t\t\t{\n\t\t\t\tvar i = T.drag;\n\t\t\t\tTP.px[i] = T.xmouse;\n\t\t\t\tTP.py[i] = T.ymouse;\n\t\t\t\tTP.redraw();\n\t\t\t}\n\t\t\tT.lastxmouse=T.xmouse;\n\t\t\tT.lastymouse=T.ymouse;\n\t\t}\n\t}\n\tthis.printpoints = function(TP)\n\t{\n\t\txstr = \"this.TP.px=[\";\n\t\tystr = \"this.TP.py=[\";\n\t\tfor ( var i=0; i<TP.px.length; i++)\n\t\t{\n\t\t\tif ( i != 0)\n\t\t\t{\n\t\t\t\txstr+=\",\";\n\t\t\t\tystr+=\",\";\n\t\t\t}\n\t\t\txstr += TP.px[i];\n\t\t\tystr += TP.py[i];\n\t\t}\n\t\txstr += \"];\";\n\t\tystr += \"];\";\n\t\talert(xstr+\"\\n\"+ystr);\n\t}\n\n\ttardiv = document.getElementById(div_id);\n\t\n\tfunction getPositionLeft(This){\n\t\tvar el=This;var pL=0;\n\t\twhile(el){pL+=el.offsetLeft;el=el.offsetParent;}\n\t\treturn pL\n\t}\n\tfunction getPositionTop(This){\n\t\tvar el=This;var pT=0;\n\t\twhile(el){pT+=el.offsetTop;el=el.offsetParent;}\n\t\treturn pT\n\t}\n\tthis.canx=getPositionLeft(tardiv);\n\tthis.cany=getPositionTop(tardiv);\n\t\n\ttardiv.onmousedown\t=this.onMouseDown;\n\ttardiv.onmouseup\t=this.onMouseUp;\n\ttardiv.onmousemove\t=this.onMouseMove;\n\n\ttardiv.onmousedown({token:'in', parent:this});\n\ttardiv.onmouseup({token:'in', parent:this});\n\ttardiv.onmousemove({token:'in', parent:this});\n}\n"
  },
  {
    "path": "cpp/workbench/drawer.js",
    "content": "function drawer()\n{\n\tvar T=this;\n\tT.canx=0; T.cany=0;\n\tT.linestring=[\"\",\"\",\"\",\"\",\"\"];\n\tT.linecolor=[\"#000\",\"#F00\",\"#0F0\",\"#00B\",\"#AAA\"];\n\tT.moveTo=function( x,y,C)\n\t{\n\t\tif ( C==null) {C=4;}\n\t\tT.linestring[C] += \" M \"+x+\",\"+y;\n\t}\n\tT.lineTo=function( x,y,C)\n\t{\n\t\tif ( C==null) {C=4;}\n\t\tT.linestring[C] += \" L \"+x+\",\"+y;\n\t}\n\tT.drawpoint=function(x,y,s,C)\n\t{\n\t\tif (!s) {s=2;}\n\t\tif (C==null) {C=0;}\n\t\tT.moveTo(x-s,y-s,C);\n\t\tT.lineTo(x+s,y+s,C);\n\t\tT.moveTo(x-s,y+s,C);\n\t\tT.lineTo(x+s,y-s,C);\n\t\tT.moveTo(x,y,C);\n\t}\n\tT.drawarrow=function( ax,ay,bx,by, size,C)\n\t{\n\t\tif ( size != null)\n\t\t{\n\t\t\tvar vx=ay-by;\n\t\t\tvar vy=bx-ax;\n\t\t\tvar nsize=Math.sqrt(vx*vx+vy*vy);\n\t\t\tvar cx=vy*size/nsize;\n\t\t\tvar cy=vx*size/nsize;\n\t\t\tvar bx=ax+cx;\n\t\t\tvar by=ay-cy;\n\t\t}\n\t\tT.moveTo(ax,ay, C);\n\t\tT.lineTo(bx,by, C);\n\t\t//\n\t\tvar R=0.9; var K=0.04;\n\t\tvar vx=ay-by;\n\t\tvar vy=bx-ax;\n\t\tvar acx=vy*R+ax;\n\t\tvar acy=-vx*R+ay;\n\t\t//\n\t\tT.moveTo(acx+vx*K,acy+vy*K, C)\n\t\tT.lineTo(bx,by, C);\n\t\tT.lineTo(acx-vx*K,acy-vy*K, C);\n\t}\n\tT.drawpath=function(rl)\n\t{\n\t\tfor ( var i=0; i<5; i++)\n\t\t\trl.path( T.linestring[i]).attr( {stroke: T.linecolor[i], \"stroke-width\": 0.5});\n\t\tT.linestring=[\"\",\"\",\"\",\"\",\"\"];\n\t}\n\tT.fillpath=function(rl,i)\n\t{\n\t\trl.path( T.linestring[i]).attr( {fill: T.linecolor[i]});\n\t\tT.linestring[i]=\"\";\n\t}\n}\n"
  },
  {
    "path": "cpp/workbench/gradient_along_path.html",
    "content": "<!DOCTYPE html>\n<meta charset=\"utf-8\">\n<body>\n<script src=\"http://d3js.org/d3.v3.min.js\"></script>\n<script>\n//code comes from https://gist.github.com/mbostock/4163057\n\nvar points = [\n  [86, 388],\n  [788, 40],\n  [805, 447],\n  [93, 72]\n];\n \nvar width = 960,\n    height = 500;\n \nvar color = d3.interpolateLab(\"#008000\", \"#c83a22\");\n \nvar svg = d3.select(\"body\").append(\"svg\")\n    .attr(\"width\", width)\n    .attr(\"height\", height);\n \nvar line = d3.svg.line()\n    .interpolate(\"basis\");\n \nsvg.selectAll(\"path\")\n    .data(quad(sample(line(points), 8)))\n  .enter().append(\"path\")\n    .style(\"fill\", function(d) { return color(d.t); })\n    .style(\"stroke\", function(d) { return color(d.t); })\n    .attr(\"d\", function(d) { return lineJoin(d[0], d[1], d[2], d[3], 32); });\n \n// Sample the SVG path string \"d\" uniformly with the specified precision.\nfunction sample(d, precision) {\n  var path = document.createElementNS(d3.ns.prefix.svg, \"path\");\n  path.setAttribute(\"d\", d);\n \n  var n = path.getTotalLength(), t = [0], i = 0, dt = precision;\n  while ((i += dt) < n) t.push(i);\n  t.push(n);\n \n  return t.map(function(t) {\n    var p = path.getPointAtLength(t), a = [p.x, p.y];\n    a.t = t / n;\n    return a;\n  });\n}\n \n// Compute quads of adjacent points [p0, p1, p2, p3].\nfunction quad(points) {\n  return d3.range(points.length - 1).map(function(i) {\n    var a = [points[i - 1], points[i], points[i + 1], points[i + 2]];\n    a.t = (points[i].t + points[i + 1].t) / 2;\n    return a;\n  });\n}\n \n// Compute stroke outline for segment p12.\nfunction lineJoin(p0, p1, p2, p3, width) {\n  var u12 = perp(p1, p2),\n      r = width / 2,\n      a = [p1[0] + u12[0] * r, p1[1] + u12[1] * r],\n      b = [p2[0] + u12[0] * r, p2[1] + u12[1] * r],\n      c = [p2[0] - u12[0] * r, p2[1] - u12[1] * r],\n      d = [p1[0] - u12[0] * r, p1[1] - u12[1] * r];\n \n  if (p0) { // clip ad and dc using average of u01 and u12\n    var u01 = perp(p0, p1), e = [p1[0] + u01[0] + u12[0], p1[1] + u01[1] + u12[1]];\n    a = lineIntersect(p1, e, a, b);\n    d = lineIntersect(p1, e, d, c);\n  }\n \n  if (p3) { // clip ab and dc using average of u12 and u23\n    var u23 = perp(p2, p3), e = [p2[0] + u23[0] + u12[0], p2[1] + u23[1] + u12[1]];\n    b = lineIntersect(p2, e, a, b);\n    c = lineIntersect(p2, e, d, c);\n  }\n \n  return \"M\" + a + \"L\" + b + \" \" + c + \" \" + d + \"Z\";\n}\n \n// Compute intersection of two infinite lines ab and cd.\nfunction lineIntersect(a, b, c, d) {\n  var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3,\n      y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3,\n      ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);\n  return [x1 + ua * x21, y1 + ua * y21];\n}\n \n// Compute unit vector perpendicular to p01.\nfunction perp(p0, p1) {\n  var u01x = p0[1] - p1[1], u01y = p1[0] - p0[0],\n      u01d = Math.sqrt(u01x * u01x + u01y * u01y);\n  return [u01x / u01d, u01y / u01d];\n}\n \n</script>\n"
  },
  {
    "path": "cpp/workbench/knife_cut_test.cpp",
    "content": "/*config.h is generated by fltk in your system\n * this file is used with fltk 1.3 with gl enabled.\n * compile by: fltk-config --use-gl --compile knife_cut_test.cpp\n * or something like: g++ -lX11 -lGL 'test1_drag.cpp' -o 'test1_drag'\n*/\n#include <math.h>\n#include <stdio.h>\n\n#include \"../samples/config.h\" //config.h must always be placed before any Fl header\n#include <FL/gl.h>\n#include <FL/Fl_Box.H>\n#include <FL/Fl_Value_Slider.H>\n#include <FL/Fl_Window.H>\n#include <FL/Fl_Radio_Light_Button.H>\n\nnamespace VASEr\n{\nstruct Vec2 { double x,y;};\nstruct Color { float r,g,b,a;};\n}\n#include \"../vaser/vaser.cpp\"\nusing namespace VASEr;\n\nvoid test_draw();\n#include \"../samples/test1_base.cpp\"\n\nconst int buf_size=20;\n\nVec2 AP[buf_size];\nColor AC[buf_size];\nint size_of_AP = 0;\n\nFl_Window* main_wnd;\nGl_Window* gl_wnd;\nFl_Box* text;\nFl_Button *nk1, *nk2, *nk3;\n\nusing namespace VASEr;\nusing namespace VASErin;\n\n//application\nvoid line_update()\n{\n\tColor cc[3];\n\t{ Color col={1 , 0, 0, 1}; cc[0]=col;}\n\t{ Color col={.8,.8, 0, 1}; cc[1]=col;}\n\t{ Color col={ 0, 0, 1, 1}; cc[2]=col;}\n\tColor grey={.5,.5,.5, 1};\n\t\n\tfor ( int i=0; i<size_of_AP; i++)\n\t{\n\t\tif ( i<3) {\n\t\t\tAC[i] = cc[i%3];\n\t\t\tAC[i].a = 0.5f;\n\t\t} else {\n\t\t\tAC[i] = grey;\n\t\t\tAC[i].a = 0.2f;\n\t\t}\n\t}\n}\nvoid line_init( int)\n{\n\tAP[0].x=300; AP[0].y=30;\n\tAP[1].x=120; AP[1].y=250;\n\tAP[2].x=480; AP[2].y=250;\n\t\n\tAP[3].x=240; AP[3].y=130;\n\tAP[4].x=360; AP[4].y=130;\n\tAP[5].x=300; AP[5].y=220;\n\tsize_of_AP = 6;\n\t\n\tline_update();\n\tgl_wnd->set_drag_target( AP, size_of_AP); \n}\nvoid drag_cb(Fl_Widget* W, void*)\n{\n\tgl_wnd->redraw();\n}\nvoid nk_cb(Fl_Widget* W, void*)\n{\n\tgl_wnd->redraw();\n}\nint N_knife()\n{\n\tif ( nk1->value())\n\t\treturn 1;\n\telse if ( nk2->value())\n\t\treturn 2;\n\telse if ( nk3->value())\n\t\treturn 3;\n}\nvoid make_form()\n{\n\ttext = new Fl_Box(FL_FRAME_BOX,0,300,600,80,\n\t\"This is the test of general knife cut. \"\n\t\"The grey triangle define the knifes, which cut the colored \"\n\t\"triangle into parts. By using the 3 sides of a triangle as knives, \"\n\t\"you can obtain the result of triangle A minus triangle B. \"\n\t\"Note that the knife cut method takes care also the interpolation \"\n\t\"of colors. \\n\"\n\t\"Drag the points of the triangles to play.\"\n\t);\n\ttext->align( FL_ALIGN_TOP | FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);\n\t\n\t{\tFl_Group* o = new Fl_Group(0,380,600,20);\n\t\tnew Fl_Box(0,380,30,20,\"N =\");\n\t\tnk1 = new Fl_Radio_Light_Button(30,380,80,20,\"1 knife\");\n\t\tnk2 = new Fl_Radio_Light_Button(110,380,30,20,\"2\");\n\t\tnk3 = new Fl_Radio_Light_Button(140,380,30,20,\"3\");\n\t\t\n\t\tnk1->callback(nk_cb);\n\t\tnk2->callback(nk_cb);\n\t\tnk3->callback(nk_cb);\n\t\t\n\t\tnk1->value(1);\n\t\to->end();\n\t}\n}\nvoid draw_triangles_outline( vertex_array_holder& tris)\n{\n\tfor ( int i=0; i<tris.count; i++)\n\t{\n\t\tPoint P1 = tris.get(i); i++;\n\t\tPoint P2 = tris.get(i); i++;\n\t\tPoint P3 = tris.get(i);\n\t\t\n\t\tglBegin(GL_LINE_STRIP);\n\t\t\tglColor3f(1,0,0);\n\t\t\tglVertex2f( P1.x,P1.y);\n\t\t\tglVertex2f( P2.x,P2.y);\n\t\t\tglVertex2f( P3.x,P3.y);\n\t\t\tglVertex2f( P1.x,P1.y);\n\t\tglEnd();\n\t}\n}\nvoid test_draw()\n{\n\trenderer::before();\n\t\n\t{\tvertex_array_holder tri_in,tri_out;\n\t\ttri_in .set_gl_draw_mode(GL_TRIANGLES);\n\t\ttri_out.set_gl_draw_mode(GL_TRIANGLES);\n\t\t\n\t\tfor ( int i=0; i<3; i++)\n\t\t{\n\t\t\ttri_in.push( AP[i], AC[i]);\n\t\t}\n\t\t\n\t\tPoint kn0[3]={AP[3],AP[4],AP[5]};\n\t\tPoint kn1[3]={AP[4],AP[5],AP[3]};\n\t\tPoint kn2[3]={AP[5],AP[3],AP[4]};\n\t\t\n\t\tvah_N_knife_cut( tri_in, tri_out, kn0,kn1,kn2, 0,0,N_knife());\n\t\ttri_out.draw();\n\t\tdraw_triangles_outline( tri_out);\n\t}\n\t\n\t{\tvertex_array_holder kn;\n\t\tkn.set_gl_draw_mode(GL_TRIANGLES);\n\t\tfor ( int i=3; i<6; i++)\n\t\t{\n\t\t\tkn.push( AP[i],AC[i]);\n\t\t}\n\t\tkn.draw();\n\t}\n\t\n\trenderer::after();\n}\n\nint main(int argc, char **argv)\n{\n\tmain_wnd = new Fl_Window( 600,400,\"knife cut test (fltk-opengl)\");\n\t\tmake_form(); //initialize\n\t\tgl_wnd = new Gl_Window( 0,0,600,300);  gl_wnd->end(); //create gl window\n\t\tline_init(3);\n\tmain_wnd->end();\n\tmain_wnd->show();\n\tmain_wnd->redraw();\n\t\n\treturn Fl::run();\n}\n"
  },
  {
    "path": "cpp/workbench/layout.css",
    "content": ".main_canvas\n{\n\twidth:800px; height:600px;\n}\n\n.side_pane\n{\n\tposition:absolute;\n\tleft:810px; top:10px;\n}\n"
  },
  {
    "path": "cpp/workbench/outward_vector.html",
    "content": "<body>\n<script src='raphael-min.js'></script>\n<script src='dragger.js'></script>\n<script src='drawer.js'></script>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n\n<div class='main_canvas' id='canvas1'></div>\n<div class='side_pane'>\nBlue vectors are outward perpendicular vectors.<br />\nRed ones are outward bisecting vectors.<br />\nThis method only works for these 2 special case: outward perpendicular\nand outward bisecting vectors.\nDrag control points to observe.\n</div>\n\n<script>\nTP = new drawer();\nDG = new dragger(TP,'canvas1');\nTP.px=[300,300,500,500]; //feel free to expand these arrays\nTP.py=[400,200,200,400];\n\nTP.redraw=function()\n{\n\trl.clear();\n\trl.rect(0,0,800-1,600-1);\n\n\tTP.moveTo( TP.px[0],TP.py[0],0);\n\tfor ( var i=0; i<TP.px.length; i++)\n\t{\n\t\tTP.lineTo( TP.px[i],TP.py[i],0);\n\t\tTP.drawpoint( TP.px[i],TP.py[i],4,0);\n\t}\n\tmake_perpen_vector( TP.px,TP.py);\n\tmake_outward_vector( TP.px,TP.py);\n\n\tTP.drawpath(rl);\n}\n\nfunction distance( ax,ay,bx,by)\n{\n\treturn Math.sqrt( (bx-ax)*(bx-ax) + (by-ay)*(by-ay));\n}\nfunction normalize( V)\n{\n\tL= Math.sqrt(V.x*V.x+V.y*V.y);\n\treturn { x:V.x/L, y:V.y/L};\n}\nfunction choose_outward(V, b,c)\n{\n\t/* //the brute force way to calculate determinant:\n\tvar determinant = distance(b.x+V.x,b.y+V.y,c.x,c.y) - \n\t\t\tdistance(b.x-V.x,b.y-V.y,c.x,c.y);*/\n\t//a simplified way:\n\tvar determinant = b.x*V.x - V.x*c.x + b.y*V.y - V.y*c.y;\n\t\n\tif ( determinant>0)\n\t{\n\t\t//positive V is the outward vector\n\t} else {\n\t\t//negative V is the outward vector\n\t\tV.x=-V.x;\n\t\tV.y=-V.y;\n\t}\n\treturn V;\n}\nfunction signed_area(p1,p2,p3)\n{\n\tvar D = (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);\n\treturn D;\n}\n\nfunction make_perpen_vector( px,py)\n{\n\tvar L={x:0,y:0};\n\tvar V={x:0,y:0};\n\tfor ( var i=0; i<px.length; i++)\n\t{\n\t\tvar j1,j0;\n\t\tif ( i==0) {\n\t\t\tj1=1; j0=0;\n\t\t} else if ( i==px.length-1) {\n\t\t\tj1=px.length-2; j0=px.length-1;\n\t\t} else {\n\t\t\tj0=i; j1=i-1;\n\t\t}\n\n\t\tL.x=px[j1]-px[j0]; //line vector\n\t\tL.y=py[j1]-py[j0];\n\t\tV.x=-L.y; //perpendicular vector\n\t\tV.y=L.x;\n\t\t\n\t\tV=normalize(V);\n\t\tV.x*=30; //make it longer for observation\n\t\tV.y*=30;\n\t\tif ( i==0) {\n\t\t\tV=choose_outward( V,{x:px[j1],y:py[j1]},{x:px[j1+1],y:py[j1+1]} );\t\n\t\t} else if ( i==px.length-1) {\n\t\t\tV=choose_outward( V,{x:px[j1],y:py[j1]},{x:px[j1-1],y:py[j1-1]} );\n\t\t} else {\n\t\t\tV=choose_outward( V,{x:px[j0],y:py[j0]},{x:px[j0+1],y:py[j0+1]} );\n\t\t}\n\t\t\n\t\tTP.drawarrow( px[j0],py[j0],px[j0]+V.x,py[j0]+V.y,null,3);\n\t}\n}\nfunction make_outward_vector( px,py)\n{\n\tvar L1={x:0,y:0};\n\tvar L2={x:0,y:0};\n\tvar V={x:0,y:0};\n\tfor ( var i=1; i<px.length-1; i++)\n\t{\n\t\tL1.x=px[i]-px[i-1];\n\t\tL1.y=py[i]-py[i-1];\n\t\tL2.x=px[i+1]-px[i];\n\t\tL2.y=py[i+1]-py[i];\n\t\tL1=normalize(L1);\n\t\tL2=normalize(L2);\n\n\t\tV.x=L1.x-L2.x; //average vector\n\t\tV.y=L1.y-L2.y;\n\t\tV=normalize(V);\n\t\tV.x*=30; //make it longer for observation\n\t\tV.y*=30;\n\t\tV=choose_outward( V,{x:px[i],y:py[i]},{x:px[i+1],y:py[i+1]} );\t\t\n\n\t\tTP.drawarrow( px[i],py[i],px[i]+V.x,py[i]+V.y,null,1);\n\t}\n}\n\nrl = Raphael('canvas1');\nTP.redraw();\n</script>\n</body>\n"
  },
  {
    "path": "cpp/workbench/quad_reflex.html",
    "content": "<body>\n<script src='raphael-min.js'></script>\n<script src='dragger.js'></script>\n<script src='drawer.js'></script>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n<div class='side_pane'>\n\tNote that we always draw the 2 blue lines correctly, even when\n\tthe quad is reflexed.<br />\n\tThis is especially useful when drawing quad by triangle strip\n\tto ensure there is no reflex.<br />\n\t<button onclick=\"app1.DG.printpoints(app1.TP)\">print points</button>\n</div>\n\n<script>\nvar app_count=0;\n\nfunction distance_squared(x1,y1,x2,y2)\n{\n\tdx = x2-x1;\n\tdy = y2-y1;\n\treturn (dx*dx+dy*dy);\n}\n\nfunction quad_is_reflexed(px,py)\n{\n\t//points:\n\t//   1------3\n\t//  /      /\n\t// 0------2\n\t// vector 01 parallel to 23\n\t\n\treturn distance_squared(px[1],py[1],px[3],py[3]) + distance_squared(px[0],py[0],px[2],py[2])\n\t\t> distance_squared(px[0],py[0],px[3],py[3]) + distance_squared(px[1],py[1],px[2],py[2]);\n\t\n\t//when dist(1,3)+dist(0,2) > dist(0,3)+dist(1,2),\n\t//  then the quad is reflexed\n}\n\nfunction demo_application1()\n{\n\tvar i=this.i=app_count;\n\tvar parent=this;\n\tapp_count++;\n\tdocument.write(\"<div class='main_canvas' id='canvas\"+i+\"'></div>\");\n\t\n\tthis.TP = new drawer();\n\tthis.DG = new dragger(this.TP,'canvas'+i);\n\tthis.rl = Raphael('canvas'+i);\n\t//\n\tthis.TP.px=[219,248,441,540];\n\tthis.TP.py=[335,223,338,231];\n\t\n\tthis.TP.redraw=function()\n\t{\n\t\tvar rl=parent.rl;\n\t\tvar TP=parent.TP;\n\t\trl.clear();\n\t\trl.rect(0,0,800-1,600-1);\n\t\t\n\t\tTP.drawarrow( TP.px[0],TP.py[0], TP.px[1],TP.py[1], null, 0);\n\t\tTP.drawarrow( TP.px[2],TP.py[2], TP.px[3],TP.py[3], null, 0);\n\t\t\n\t\tif ( !quad_is_reflexed(TP.px,TP.py))\n\t\t{\n\t\t\tTP.moveTo( TP.px[0],TP.py[0],3);\n\t\t\tTP.lineTo( TP.px[2],TP.py[2],3);\n\t\t\t\n\t\t\tTP.moveTo( TP.px[1],TP.py[1],3);\n\t\t\tTP.lineTo( TP.px[3],TP.py[3],3);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTP.moveTo( TP.px[0],TP.py[0],3);\n\t\t\tTP.lineTo( TP.px[3],TP.py[3],3);\n\t\t\t\n\t\t\tTP.moveTo( TP.px[1],TP.py[1],3);\n\t\t\tTP.lineTo( TP.px[2],TP.py[2],3);\n\t\t}\n\t\t\n\t\tTP.drawpath(rl);\n\t}\n\tthis.TP.redraw();\n}\n\napp1 = new demo_application1();\n</script>\n</body>\n"
  },
  {
    "path": "cpp/workbench/raphael-min.js",
    "content": "/*\n * Raphael 1.5.2 - JavaScript Vector Library\n *\n * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com)\n * Licensed under the MIT (http://raphaeljs.com/license.html) license.\n */\n(function(){function a(){if(a.is(arguments[0],G)){var b=arguments[0],d=bV[m](a,b.splice(0,3+a.is(b[0],E))),e=d.set();for(var g=0,h=b[w];g<h;g++){var i=b[g]||{};c[f](i.type)&&e[L](d[i.type]().attr(i))}return e}return bV[m](a,arguments)}a.version=\"1.5.2\";var b=/[, ]+/,c={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},d=/\\{(\\d+)\\}/g,e=\"prototype\",f=\"hasOwnProperty\",g=document,h=window,i={was:Object[e][f].call(h,\"Raphael\"),is:h.Raphael},j=function(){this.customAttributes={}},k,l=\"appendChild\",m=\"apply\",n=\"concat\",o=\"createTouch\"in g,p=\"\",q=\" \",r=String,s=\"split\",t=\"click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend orientationchange touchcancel gesturestart gesturechange gestureend\"[s](q),u={mousedown:\"touchstart\",mousemove:\"touchmove\",mouseup:\"touchend\"},v=\"join\",w=\"length\",x=r[e].toLowerCase,y=Math,z=y.max,A=y.min,B=y.abs,C=y.pow,D=y.PI,E=\"number\",F=\"string\",G=\"array\",H=\"toString\",I=\"fill\",J=Object[e][H],K={},L=\"push\",M=/^url\\(['\"]?([^\\)]+?)['\"]?\\)$/i,N=/^\\s*((#[a-f\\d]{6})|(#[a-f\\d]{3})|rgba?\\(\\s*([\\d\\.]+%?\\s*,\\s*[\\d\\.]+%?\\s*,\\s*[\\d\\.]+(?:%?\\s*,\\s*[\\d\\.]+)?)%?\\s*\\)|hsba?\\(\\s*([\\d\\.]+(?:deg|\\xb0|%)?\\s*,\\s*[\\d\\.]+%?\\s*,\\s*[\\d\\.]+(?:%?\\s*,\\s*[\\d\\.]+)?)%?\\s*\\)|hsla?\\(\\s*([\\d\\.]+(?:deg|\\xb0|%)?\\s*,\\s*[\\d\\.]+%?\\s*,\\s*[\\d\\.]+(?:%?\\s*,\\s*[\\d\\.]+)?)%?\\s*\\))\\s*$/i,O={\"NaN\":1,Infinity:1,\"-Infinity\":1},P=/^(?:cubic-)?bezier\\(([^,]+),([^,]+),([^,]+),([^\\)]+)\\)/,Q=y.round,R=\"setAttribute\",S=parseFloat,T=parseInt,U=\" progid:DXImageTransform.Microsoft\",V=r[e].toUpperCase,W={blur:0,\"clip-rect\":\"0 0 1e9 1e9\",cursor:\"default\",cx:0,cy:0,fill:\"#fff\",\"fill-opacity\":1,font:\"10px \\\"Arial\\\"\",\"font-family\":\"\\\"Arial\\\"\",\"font-size\":\"10\",\"font-style\":\"normal\",\"font-weight\":400,gradient:0,height:0,href:\"http://raphaeljs.com/\",opacity:1,path:\"M0,0\",r:0,rotation:0,rx:0,ry:0,scale:\"1 1\",src:\"\",stroke:\"#000\",\"stroke-dasharray\":\"\",\"stroke-linecap\":\"butt\",\"stroke-linejoin\":\"butt\",\"stroke-miterlimit\":0,\"stroke-opacity\":1,\"stroke-width\":1,target:\"_blank\",\"text-anchor\":\"middle\",title:\"Raphael\",translation:\"0 0\",width:0,x:0,y:0},X={along:\"along\",blur:E,\"clip-rect\":\"csv\",cx:E,cy:E,fill:\"colour\",\"fill-opacity\":E,\"font-size\":E,height:E,opacity:E,path:\"path\",r:E,rotation:\"csv\",rx:E,ry:E,scale:\"csv\",stroke:\"colour\",\"stroke-opacity\":E,\"stroke-width\":E,translation:\"csv\",width:E,x:E,y:E},Y=\"replace\",Z=/^(from|to|\\d+%?)$/,$=/\\s*,\\s*/,_={hs:1,rg:1},ba=/,?([achlmqrstvxz]),?/gi,bb=/([achlmqstvz])[\\s,]*((-?\\d*\\.?\\d*(?:e[-+]?\\d+)?\\s*,?\\s*)+)/ig,bc=/(-?\\d*\\.?\\d*(?:e[-+]?\\d+)?)\\s*,?\\s*/ig,bd=/^r(?:\\(([^,]+?)\\s*,\\s*([^\\)]+?)\\))?/,be=function(a,b){return a.key-b.key};a.type=h.SVGAngle||g.implementation.hasFeature(\"http://www.w3.org/TR/SVG11/feature#BasicStructure\",\"1.1\")?\"SVG\":\"VML\";if(a.type==\"VML\"){var bf=g.createElement(\"div\"),bg;bf.innerHTML=\"<v:shape adj=\\\"1\\\"/>\";bg=bf.firstChild;bg.style.behavior=\"url(#default#VML)\";if(!(bg&&typeof bg.adj==\"object\"))return a.type=null;bf=null}a.svg=!(a.vml=a.type==\"VML\");j[e]=a[e];k=j[e];a._id=0;a._oid=0;a.fn={};a.is=function(a,b){b=x.call(b);if(b==\"finite\")return!O[f](+a);return b==\"null\"&&a===null||b==typeof a||b==\"object\"&&a===Object(a)||b==\"array\"&&Array.isArray&&Array.isArray(a)||J.call(a).slice(8,-1).toLowerCase()==b};a.angle=function(b,c,d,e,f,g){{if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return((h<0)*180+y.atan(-i/-h)*180/D+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)}};a.rad=function(a){return a%360*D/180};a.deg=function(a){return a*180/D%360};a.snapTo=function(b,c,d){d=a.is(d,\"finite\")?d:10;if(a.is(b,G)){var e=b.length;while(e--)if(B(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(f<d)return c-f;if(f>b-d)return c-f+b}return c};function bh(){var a=[],b=0;for(;b<32;b++)a[b]=(~(~(y.random()*16)))[H](16);a[12]=4;a[16]=(a[16]&3|8)[H](16);return\"r-\"+a[v](\"\")}a.setWindow=function(a){h=a;g=h.document};var bi=function(b){if(a.vml){var c=/^\\s+|\\s+$/g,d;try{var e=new ActiveXObject(\"htmlfile\");e.write(\"<body>\");e.close();d=e.body}catch(a){d=createPopup().document.body}var f=d.createTextRange();bi=bm(function(a){try{d.style.color=r(a)[Y](c,p);var b=f.queryCommandValue(\"ForeColor\");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return\"#\"+(\"000000\"+b[H](16)).slice(-6)}catch(a){return\"none\"}})}else{var h=g.createElement(\"i\");h.title=\"Raphaël Colour Picker\";h.style.display=\"none\";g.body[l](h);bi=bm(function(a){h.style.color=a;return g.defaultView.getComputedStyle(h,p).getPropertyValue(\"color\")})}return bi(b)},bj=function(){return\"hsb(\"+[this.h,this.s,this.b]+\")\"},bk=function(){return\"hsl(\"+[this.h,this.s,this.l]+\")\"},bl=function(){return this.hex};a.hsb2rgb=function(b,c,d,e){if(a.is(b,\"object\")&&\"h\"in b&&\"s\"in b&&\"b\"in b){d=b.b;c=b.s;b=b.h;e=b.o}return a.hsl2rgb(b,c,d/2,e)};a.hsl2rgb=function(b,c,d,e){if(a.is(b,\"object\")&&\"h\"in b&&\"s\"in b&&\"l\"in b){d=b.l;c=b.s;b=b.h}if(b>1||c>1||d>1){b/=360;c/=100;d/=100}var f={},g=[\"r\",\"g\",\"b\"],h,i,j,k,l,m;if(c){d<0.5?h=d*(1+c):h=d+c-d*c;i=2*d-h;for(var n=0;n<3;n++){j=b+1/3*-(n-1);j<0&&j++;j>1&&j--;j*6<1?f[g[n]]=i+(h-i)*6*j:j*2<1?f[g[n]]=h:j*3<2?f[g[n]]=i+(h-i)*(2/3-j)*6:f[g[n]]=i}}else f={r:d,g:d,b:d};f.r*=255;f.g*=255;f.b*=255;f.hex=\"#\"+(16777216|f.b|f.g<<8|f.r<<16).toString(16).slice(1);a.is(e,\"finite\")&&(f.opacity=e);f.toString=bl;return f};a.rgb2hsb=function(b,c,d){if(c==null&&a.is(b,\"object\")&&\"r\"in b&&\"g\"in b&&\"b\"in b){d=b.b;c=b.g;b=b.r}if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r;c=e.g;d=e.b}if(b>1||c>1||d>1){b/=255;c/=255;d/=255}var f=z(b,c,d),g=A(b,c,d),h,i,j=f;{if(g==f)return{h:0,s:0,b:f,toString:bj};var k=f-g;i=k/f;b==f?h=(c-d)/k:c==f?h=2+(d-b)/k:h=4+(b-c)/k;h/=6;h<0&&h++;h>1&&h--}return{h:h,s:i,b:j,toString:bj}};a.rgb2hsl=function(b,c,d){if(c==null&&a.is(b,\"object\")&&\"r\"in b&&\"g\"in b&&\"b\"in b){d=b.b;c=b.g;b=b.r}if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r;c=e.g;d=e.b}if(b>1||c>1||d>1){b/=255;c/=255;d/=255}var f=z(b,c,d),g=A(b,c,d),h,i,j=(f+g)/2,k;if(g==f)k={h:0,s:0,l:j};else{var l=f-g;i=j<0.5?l/(f+g):l/(2-f-g);b==f?h=(c-d)/l:c==f?h=2+(d-b)/l:h=4+(b-c)/l;h/=6;h<0&&h++;h>1&&h--;k={h:h,s:i,l:j}}k.toString=bk;return k};a._path2string=function(){return this.join(\",\")[Y](ba,\"$1\")};function bm(a,b,c){function d(){var g=Array[e].slice.call(arguments,0),h=g[v](\"►\"),i=d.cache=d.cache||{},j=d.count=d.count||[];if(i[f](h))return c?c(i[h]):i[h];j[w]>=1000&&delete i[j.shift()];j[L](h);i[h]=a[m](b,g);return c?c(i[h]):i[h]}return d}a.getRGB=bm(function(b){if(!b||!(!((b=r(b)).indexOf(\"-\")+1)))return{r:-1,g:-1,b:-1,hex:\"none\",error:1};if(b==\"none\")return{r:-1,g:-1,b:-1,hex:\"none\"};!(_[f](b.toLowerCase().substring(0,2))||b.charAt()==\"#\")&&(b=bi(b));var c,d,e,g,h,i,j,k=b.match(N);if(k){if(k[2]){g=T(k[2].substring(5),16);e=T(k[2].substring(3,5),16);d=T(k[2].substring(1,3),16)}if(k[3]){g=T((i=k[3].charAt(3))+i,16);e=T((i=k[3].charAt(2))+i,16);d=T((i=k[3].charAt(1))+i,16)}if(k[4]){j=k[4][s]($);d=S(j[0]);j[0].slice(-1)==\"%\"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)==\"%\"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)==\"%\"&&(g*=2.55);k[1].toLowerCase().slice(0,4)==\"rgba\"&&(h=S(j[3]));j[3]&&j[3].slice(-1)==\"%\"&&(h/=100)}if(k[5]){j=k[5][s]($);d=S(j[0]);j[0].slice(-1)==\"%\"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)==\"%\"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)==\"%\"&&(g*=2.55);(j[0].slice(-3)==\"deg\"||j[0].slice(-1)==\"°\")&&(d/=360);k[1].toLowerCase().slice(0,4)==\"hsba\"&&(h=S(j[3]));j[3]&&j[3].slice(-1)==\"%\"&&(h/=100);return a.hsb2rgb(d,e,g,h)}if(k[6]){j=k[6][s]($);d=S(j[0]);j[0].slice(-1)==\"%\"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)==\"%\"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)==\"%\"&&(g*=2.55);(j[0].slice(-3)==\"deg\"||j[0].slice(-1)==\"°\")&&(d/=360);k[1].toLowerCase().slice(0,4)==\"hsla\"&&(h=S(j[3]));j[3]&&j[3].slice(-1)==\"%\"&&(h/=100);return a.hsl2rgb(d,e,g,h)}k={r:d,g:e,b:g};k.hex=\"#\"+(16777216|g|e<<8|d<<16).toString(16).slice(1);a.is(h,\"finite\")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:\"none\",error:1}},a);a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||0.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=0.075;if(b.h>1){b.h=0;b.s-=0.2;b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b})}return c.hex};a.getColor.reset=function(){delete this.start};a.parsePathString=bm(function(b){if(!b)return null;var c={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},d=[];a.is(b,G)&&a.is(b[0],G)&&(d=bo(b));d[w]||r(b)[Y](bb,function(a,b,e){var f=[],g=x.call(b);e[Y](bc,function(a,b){b&&f[L](+b)});if(g==\"m\"&&f[w]>2){d[L]([b][n](f.splice(0,2)));g=\"l\";b=b==\"m\"?\"l\":\"L\"}while(f[w]>=c[g]){d[L]([b][n](f.splice(0,c[g])));if(!c[g])break}});d[H]=a._path2string;return d});a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,l=C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h,m=a+2*i*(c-a)+i*i*(e-2*c+a),n=b+2*i*(d-b)+i*i*(f-2*d+b),o=c+2*i*(e-c)+i*i*(g-2*e+c),p=d+2*i*(f-d)+i*i*(h-2*f+d),q=(1-i)*a+i*c,r=(1-i)*b+i*d,s=(1-i)*e+i*g,t=(1-i)*f+i*h,u=90-y.atan((m-o)/(n-p))*180/D;(m>o||n<p)&&(u+=180);return{x:k,y:l,m:{x:m,y:n},n:{x:o,y:p},start:{x:q,y:r},end:{x:s,y:t},alpha:u}};var bn=bm(function(a){if(!a)return{x:0,y:0,width:0,height:0};a=bw(a);var b=0,c=0,d=[],e=[],f;for(var g=0,h=a[w];g<h;g++){f=a[g];if(f[0]==\"M\"){b=f[1];c=f[2];d[L](b);e[L](c)}else{var i=bv(b,c,f[1],f[2],f[3],f[4],f[5],f[6]);d=d[n](i.min.x,i.max.x);e=e[n](i.min.y,i.max.y);b=f[5];c=f[6]}}var j=A[m](0,d),k=A[m](0,e);return{x:j,y:k,width:z[m](0,d)-j,height:z[m](0,e)-k}}),bo=function(b){var c=[];if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);for(var d=0,e=b[w];d<e;d++){c[d]=[];for(var f=0,g=b[d][w];f<g;f++)c[d][f]=b[d][f]}c[H]=a._path2string;return c},bp=bm(function(b){if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);var c=[],d=0,e=0,f=0,g=0,h=0;if(b[0][0]==\"M\"){d=b[0][1];e=b[0][2];f=d;g=e;h++;c[L]([\"M\",d,e])}for(var i=h,j=b[w];i<j;i++){var k=c[i]=[],l=b[i];if(l[0]!=x.call(l[0])){k[0]=x.call(l[0]);switch(k[0]){case\"a\":k[1]=l[1];k[2]=l[2];k[3]=l[3];k[4]=l[4];k[5]=l[5];k[6]=+(l[6]-d).toFixed(3);k[7]=+(l[7]-e).toFixed(3);break;case\"v\":k[1]=+(l[1]-e).toFixed(3);break;case\"m\":f=l[1];g=l[2];default:for(var m=1,n=l[w];m<n;m++)k[m]=+(l[m]-(m%2?d:e)).toFixed(3)}}else{k=c[i]=[];if(l[0]==\"m\"){f=l[1]+d;g=l[2]+e}for(var o=0,p=l[w];o<p;o++)c[i][o]=l[o]}var q=c[i][w];switch(c[i][0]){case\"z\":d=f;e=g;break;case\"h\":d+=+c[i][q-1];break;case\"v\":e+=+c[i][q-1];break;default:d+=+c[i][q-2];e+=+c[i][q-1]}}c[H]=a._path2string;return c},0,bo),bq=bm(function(b){if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);var c=[],d=0,e=0,f=0,g=0,h=0;if(b[0][0]==\"M\"){d=+b[0][1];e=+b[0][2];f=d;g=e;h++;c[0]=[\"M\",d,e]}for(var i=h,j=b[w];i<j;i++){var k=c[i]=[],l=b[i];if(l[0]!=V.call(l[0])){k[0]=V.call(l[0]);switch(k[0]){case\"A\":k[1]=l[1];k[2]=l[2];k[3]=l[3];k[4]=l[4];k[5]=l[5];k[6]=+(l[6]+d);k[7]=+(l[7]+e);break;case\"V\":k[1]=+l[1]+e;break;case\"H\":k[1]=+l[1]+d;break;case\"M\":f=+l[1]+d;g=+l[2]+e;default:for(var m=1,n=l[w];m<n;m++)k[m]=+l[m]+(m%2?d:e)}}else for(var o=0,p=l[w];o<p;o++)c[i][o]=l[o];switch(k[0]){case\"Z\":d=f;e=g;break;case\"H\":d=k[1];break;case\"V\":e=k[1];break;case\"M\":f=c[i][c[i][w]-2];g=c[i][c[i][w]-1];default:d=c[i][c[i][w]-2];e=c[i][c[i][w]-1]}}c[H]=a._path2string;return c},null,bo),br=function(a,b,c,d){return[a,b,c,d,c,d]},bs=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},bt=function(a,b,c,d,e,f,g,h,i,j){var k=D*120/180,l=D/180*(+e||0),m=[],o,p=bm(function(a,b,c){var d=a*y.cos(c)-b*y.sin(c),e=a*y.sin(c)+b*y.cos(c);return{x:d,y:e}});if(j){G=j[0];H=j[1];E=j[2];F=j[3]}else{o=p(a,b,-l);a=o.x;b=o.y;o=p(h,i,-l);h=o.x;i=o.y;var q=y.cos(D/180*e),r=y.sin(D/180*e),t=(a-h)/2,u=(b-i)/2,x=t*t/(c*c)+u*u/(d*d);if(x>1){x=y.sqrt(x);c=x*c;d=x*d}var z=c*c,A=d*d,C=(f==g?-1:1)*y.sqrt(B((z*A-z*u*u-A*t*t)/(z*u*u+A*t*t))),E=C*c*u/d+(a+h)/2,F=C*-d*t/c+(b+i)/2,G=y.asin(((b-F)/d).toFixed(9)),H=y.asin(((i-F)/d).toFixed(9));G=a<E?D-G:G;H=h<E?D-H:H;G<0&&(G=D*2+G);H<0&&(H=D*2+H);g&&G>H&&(G=G-D*2);!g&&H>G&&(H=H-D*2)}var I=H-G;if(B(I)>k){var J=H,K=h,L=i;H=G+k*(g&&H>G?1:-1);h=E+c*y.cos(H);i=F+d*y.sin(H);m=bt(h,i,c,d,e,0,g,K,L,[H,J,E,F])}I=H-G;var M=y.cos(G),N=y.sin(G),O=y.cos(H),P=y.sin(H),Q=y.tan(I/4),R=4/3*c*Q,S=4/3*d*Q,T=[a,b],U=[a+R*N,b-S*M],V=[h+R*P,i-S*O],W=[h,i];U[0]=2*T[0]-U[0];U[1]=2*T[1]-U[1];{if(j)return[U,V,W][n](m);m=[U,V,W][n](m)[v]()[s](\",\");var X=[];for(var Y=0,Z=m[w];Y<Z;Y++)X[Y]=Y%2?p(m[Y-1],m[Y],l).y:p(m[Y],m[Y+1],l).x;return X}},bu=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,y:C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h}},bv=bm(function(a,b,c,d,e,f,g,h){var i=e-2*c+a-(g-2*e+c),j=2*(c-a)-2*(e-c),k=a-c,l=(-j+y.sqrt(j*j-4*i*k))/2/i,n=(-j-y.sqrt(j*j-4*i*k))/2/i,o=[b,h],p=[a,g],q;B(l)>\"1e12\"&&(l=0.5);B(n)>\"1e12\"&&(n=0.5);if(l>0&&l<1){q=bu(a,b,c,d,e,f,g,h,l);p[L](q.x);o[L](q.y)}if(n>0&&n<1){q=bu(a,b,c,d,e,f,g,h,n);p[L](q.x);o[L](q.y)}i=f-2*d+b-(h-2*f+d);j=2*(d-b)-2*(f-d);k=b-d;l=(-j+y.sqrt(j*j-4*i*k))/2/i;n=(-j-y.sqrt(j*j-4*i*k))/2/i;B(l)>\"1e12\"&&(l=0.5);B(n)>\"1e12\"&&(n=0.5);if(l>0&&l<1){q=bu(a,b,c,d,e,f,g,h,l);p[L](q.x);o[L](q.y)}if(n>0&&n<1){q=bu(a,b,c,d,e,f,g,h,n);p[L](q.x);o[L](q.y)}return{min:{x:A[m](0,p),y:A[m](0,o)},max:{x:z[m](0,p),y:z[m](0,o)}}}),bw=bm(function(a,b){var c=bq(a),d=b&&bq(b),e={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g=function(a,b){var c,d;if(!a)return[\"C\",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case\"M\":b.X=a[1];b.Y=a[2];break;case\"A\":a=[\"C\"][n](bt[m](0,[b.x,b.y][n](a.slice(1))));break;case\"S\":c=b.x+(b.x-(b.bx||b.x));d=b.y+(b.y-(b.by||b.y));a=[\"C\",c,d][n](a.slice(1));break;case\"T\":b.qx=b.x+(b.x-(b.qx||b.x));b.qy=b.y+(b.y-(b.qy||b.y));a=[\"C\"][n](bs(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case\"Q\":b.qx=a[1];b.qy=a[2];a=[\"C\"][n](bs(b.x,b.y,a[1],a[2],a[3],a[4]));break;case\"L\":a=[\"C\"][n](br(b.x,b.y,a[1],a[2]));break;case\"H\":a=[\"C\"][n](br(b.x,b.y,a[1],b.y));break;case\"V\":a=[\"C\"][n](br(b.x,b.y,b.x,a[1]));break;case\"Z\":a=[\"C\"][n](br(b.x,b.y,b.X,b.Y));break}return a},h=function(a,b){if(a[b][w]>7){a[b].shift();var e=a[b];while(e[w])a.splice(b++,0,[\"C\"][n](e.splice(0,6)));a.splice(b,1);k=z(c[w],d&&d[w]||0)}},i=function(a,b,e,f,g){if(a&&b&&a[g][0]==\"M\"&&b[g][0]!=\"M\"){b.splice(g,0,[\"M\",f.x,f.y]);e.bx=0;e.by=0;e.x=a[g][1];e.y=a[g][2];k=z(c[w],d&&d[w]||0)}};for(var j=0,k=z(c[w],d&&d[w]||0);j<k;j++){c[j]=g(c[j],e);h(c,j);d&&(d[j]=g(d[j],f));d&&h(d,j);i(c,d,e,f,j);i(d,c,f,e,j);var l=c[j],o=d&&d[j],p=l[w],q=d&&o[w];e.x=l[p-2];e.y=l[p-1];e.bx=S(l[p-4])||e.x;e.by=S(l[p-3])||e.y;f.bx=d&&(S(o[q-4])||f.x);f.by=d&&(S(o[q-3])||f.y);f.x=d&&o[q-2];f.y=d&&o[q-1]}return d?[c,d]:c},null,bo),bx=bm(function(b){var c=[];for(var d=0,e=b[w];d<e;d++){var f={},g=b[d].match(/^([^:]*):?([\\d\\.]*)/);f.color=a.getRGB(g[1]);if(f.color.error)return null;f.color=f.color.hex;g[2]&&(f.offset=g[2]+\"%\");c[L](f)}for(d=1,e=c[w]-1;d<e;d++){if(!c[d].offset){var h=S(c[d-1].offset||0),i=0;for(var j=d+1;j<e;j++){if(c[j].offset){i=c[j].offset;break}}if(!i){i=100;j=e}i=S(i);var k=(i-h)/(j-d+1);for(;d<j;d++){h+=k;c[d].offset=h+\"%\"}}}return c}),by=function(b,c,d,e){var f;if(a.is(b,F)||a.is(b,\"object\")){f=a.is(b,F)?g.getElementById(b):b;if(f.tagName)return c==null?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:c,height:d}}else return{container:1,x:b,y:c,width:d,height:e}},bz=function(a,b){var c=this;for(var d in b){if(b[f](d)&&!(d in a))switch(typeof b[d]){case\"function\":(function(b){a[d]=a===c?b:function(){return b[m](c,arguments)}})(b[d]);break;case\"object\":a[d]=a[d]||{};bz.call(this,a[d],b[d]);break;default:a[d]=b[d];break}}},bA=function(a,b){a==b.top&&(b.top=a.prev);a==b.bottom&&(b.bottom=a.next);a.next&&(a.next.prev=a.prev);a.prev&&(a.prev.next=a.next)},bB=function(a,b){if(b.top===a)return;bA(a,b);a.next=null;a.prev=b.top;b.top.next=a;b.top=a},bC=function(a,b){if(b.bottom===a)return;bA(a,b);a.next=b.bottom;a.prev=null;b.bottom.prev=a;b.bottom=a},bD=function(a,b,c){bA(a,c);b==c.top&&(c.top=a);b.next&&(b.next.prev=a);a.next=b.next;a.prev=b;b.next=a},bE=function(a,b,c){bA(a,c);b==c.bottom&&(c.bottom=a);b.prev&&(b.prev.next=a);a.prev=b.prev;b.prev=a;a.next=b},bF=function(a){return function(){throw new Error(\"Raphaël: you are calling to method “\"+a+\"” of removed object\")}};a.pathToRelative=bp;if(a.svg){k.svgns=\"http://www.w3.org/2000/svg\";k.xlink=\"http://www.w3.org/1999/xlink\";Q=function(a){return+a+(~(~a)===a)*0.5};var bG=function(a,b){if(b)for(var c in b)b[f](c)&&a[R](c,r(b[c]));else{a=g.createElementNS(k.svgns,a);a.style.webkitTapHighlightColor=\"rgba(0,0,0,0)\";return a}};a[H]=function(){return\"Your browser supports SVG.\\nYou are running Raphaël \"+this.version};var bH=function(a,b){var c=bG(\"path\");b.canvas&&b.canvas[l](c);var d=new bN(c,b);d.type=\"path\";bK(d,{fill:\"none\",stroke:\"#000\",path:a});return d},bI=function(a,b,c){var d=\"linear\",e=0.5,f=0.5,h=a.style;b=r(b)[Y](bd,function(a,b,c){d=\"radial\";if(b&&c){e=S(b);f=S(c);var g=(f>0.5)*2-1;C(e-0.5,2)+C(f-0.5,2)>0.25&&(f=y.sqrt(0.25-C(e-0.5,2))*g+0.5)&&f!=0.5&&(f=f.toFixed(5)-0.00001*g)}return p});b=b[s](/\\s*\\-\\s*/);if(d==\"linear\"){var i=b.shift();i=-S(i);if(isNaN(i))return null;var j=[0,0,y.cos(i*D/180),y.sin(i*D/180)],k=1/(z(B(j[2]),B(j[3]))||1);j[2]*=k;j[3]*=k;if(j[2]<0){j[0]=-j[2];j[2]=0}if(j[3]<0){j[1]=-j[3];j[3]=0}}var m=bx(b);if(!m)return null;var n=a.getAttribute(I);n=n.match(/^url\\(#(.*)\\)$/);n&&c.defs.removeChild(g.getElementById(n[1]));var o=bG(d+\"Gradient\");o.id=bh();bG(o,d==\"radial\"?{fx:e,fy:f}:{x1:j[0],y1:j[1],x2:j[2],y2:j[3]});c.defs[l](o);for(var q=0,t=m[w];q<t;q++){var u=bG(\"stop\");bG(u,{offset:m[q].offset?m[q].offset:q?\"100%\":\"0%\",\"stop-color\":m[q].color||\"#fff\"});o[l](u)}bG(a,{fill:\"url(#\"+o.id+\")\",opacity:1,\"fill-opacity\":1});h.fill=p;h.opacity=1;h.fillOpacity=1;return 1},bJ=function(b){var c=b.getBBox();bG(b.pattern,{patternTransform:a.format(\"translate({0},{1})\",c.x,c.y)})},bK=function(c,d){var e={\"\":[0],none:[0],\"-\":[3,1],\".\":[1,1],\"-.\":[3,1,1,1],\"-..\":[3,1,1,1,1,1],\". \":[1,3],\"- \":[4,3],\"--\":[8,3],\"- .\":[4,3,1,3],\"--.\":[8,3,1,3],\"--..\":[8,3,1,3,1,3]},h=c.node,i=c.attrs,j=c.rotate(),k=function(a,b){b=e[x.call(b)];if(b){var c=a.attrs[\"stroke-width\"]||\"1\",f=({round:c,square:c,butt:0})[a.attrs[\"stroke-linecap\"]||d[\"stroke-linecap\"]]||0,g=[],i=b[w];while(i--)g[i]=b[i]*c+(i%2?1:-1)*f;bG(h,{\"stroke-dasharray\":g[v](\",\")})}};d[f](\"rotation\")&&(j=d.rotation);var m=r(j)[s](b);if(m.length-1){m[1]=+m[1];m[2]=+m[2]}else m=null;S(j)&&c.rotate(0,true);for(var n in d){if(d[f](n)){if(!W[f](n))continue;var o=d[n];i[n]=o;switch(n){case\"blur\":c.blur(o);break;case\"rotation\":c.rotate(o,true);break;case\"href\":case\"title\":case\"target\":var t=h.parentNode;if(x.call(t.tagName)!=\"a\"){var u=bG(\"a\");t.insertBefore(u,h);u[l](h);t=u}n==\"target\"&&o==\"blank\"?t.setAttributeNS(c.paper.xlink,\"show\",\"new\"):t.setAttributeNS(c.paper.xlink,n,o);break;case\"cursor\":h.style.cursor=o;break;case\"clip-rect\":var y=r(o)[s](b);if(y[w]==4){c.clip&&c.clip.parentNode.parentNode.removeChild(c.clip.parentNode);var z=bG(\"clipPath\"),A=bG(\"rect\");z.id=bh();bG(A,{x:y[0],y:y[1],width:y[2],height:y[3]});z[l](A);c.paper.defs[l](z);bG(h,{\"clip-path\":\"url(#\"+z.id+\")\"});c.clip=A}if(!o){var B=g.getElementById(h.getAttribute(\"clip-path\")[Y](/(^url\\(#|\\)$)/g,p));B&&B.parentNode.removeChild(B);bG(h,{\"clip-path\":p});delete c.clip}break;case\"path\":c.type==\"path\"&&bG(h,{d:o?i.path=bq(o):\"M0,0\"});break;case\"width\":h[R](n,o);if(i.fx){n=\"x\";o=i.x}else break;case\"x\":i.fx&&(o=-i.x-(i.width||0));case\"rx\":if(n==\"rx\"&&c.type==\"rect\")break;case\"cx\":m&&(n==\"x\"||n==\"cx\")&&(m[1]+=o-i[n]);h[R](n,o);c.pattern&&bJ(c);break;case\"height\":h[R](n,o);if(i.fy){n=\"y\";o=i.y}else break;case\"y\":i.fy&&(o=-i.y-(i.height||0));case\"ry\":if(n==\"ry\"&&c.type==\"rect\")break;case\"cy\":m&&(n==\"y\"||n==\"cy\")&&(m[2]+=o-i[n]);h[R](n,o);c.pattern&&bJ(c);break;case\"r\":c.type==\"rect\"?bG(h,{rx:o,ry:o}):h[R](n,o);break;case\"src\":c.type==\"image\"&&h.setAttributeNS(c.paper.xlink,\"href\",o);break;case\"stroke-width\":h.style.strokeWidth=o;h[R](n,o);i[\"stroke-dasharray\"]&&k(c,i[\"stroke-dasharray\"]);break;case\"stroke-dasharray\":k(c,o);break;case\"translation\":var C=r(o)[s](b);C[0]=+C[0]||0;C[1]=+C[1]||0;if(m){m[1]+=C[0];m[2]+=C[1]}cz.call(c,C[0],C[1]);break;case\"scale\":C=r(o)[s](b);c.scale(+C[0]||1,+C[1]||+C[0]||1,isNaN(S(C[2]))?null:+C[2],isNaN(S(C[3]))?null:+C[3]);break;case I:var D=r(o).match(M);if(D){z=bG(\"pattern\");var E=bG(\"image\");z.id=bh();bG(z,{x:0,y:0,patternUnits:\"userSpaceOnUse\",height:1,width:1});bG(E,{x:0,y:0});E.setAttributeNS(c.paper.xlink,\"href\",D[1]);z[l](E);var F=g.createElement(\"img\");F.style.cssText=\"position:absolute;left:-9999em;top-9999em\";F.onload=function(){bG(z,{width:this.offsetWidth,height:this.offsetHeight});bG(E,{width:this.offsetWidth,height:this.offsetHeight});g.body.removeChild(this);c.paper.safari()};g.body[l](F);F.src=D[1];c.paper.defs[l](z);h.style.fill=\"url(#\"+z.id+\")\";bG(h,{fill:\"url(#\"+z.id+\")\"});c.pattern=z;c.pattern&&bJ(c);break}var G=a.getRGB(o);if(G.error)if((({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!=\"r\")&&bI(h,o,c.paper)){i.gradient=o;i.fill=\"none\";break}else{delete d.gradient;delete i.gradient;!a.is(i.opacity,\"undefined\")&&a.is(d.opacity,\"undefined\")&&bG(h,{opacity:i.opacity});!a.is(i[\"fill-opacity\"],\"undefined\")&&a.is(d[\"fill-opacity\"],\"undefined\")&&bG(h,{\"fill-opacity\":i[\"fill-opacity\"]})}G[f](\"opacity\")&&bG(h,{\"fill-opacity\":G.opacity>1?G.opacity/100:G.opacity});case\"stroke\":G=a.getRGB(o);h[R](n,G.hex);n==\"stroke\"&&G[f](\"opacity\")&&bG(h,{\"stroke-opacity\":G.opacity>1?G.opacity/100:G.opacity});break;case\"gradient\":(({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!=\"r\")&&bI(h,o,c.paper);break;case\"opacity\":i.gradient&&!i[f](\"stroke-opacity\")&&bG(h,{\"stroke-opacity\":o>1?o/100:o});case\"fill-opacity\":if(i.gradient){var H=g.getElementById(h.getAttribute(I)[Y](/^url\\(#|\\)$/g,p));if(H){var J=H.getElementsByTagName(\"stop\");J[J[w]-1][R](\"stop-opacity\",o)}break}default:n==\"font-size\"&&(o=T(o,10)+\"px\");var K=n[Y](/(\\-.)/g,function(a){return V.call(a.substring(1))});h.style[K]=o;h[R](n,o);break}}}bM(c,d);m?c.rotate(m.join(q)):S(j)&&c.rotate(j,true)},bL=1.2,bM=function(b,c){if(b.type!=\"text\"||!(c[f](\"text\")||c[f](\"font\")||c[f](\"font-size\")||c[f](\"x\")||c[f](\"y\")))return;var d=b.attrs,e=b.node,h=e.firstChild?T(g.defaultView.getComputedStyle(e.firstChild,p).getPropertyValue(\"font-size\"),10):10;if(c[f](\"text\")){d.text=c.text;while(e.firstChild)e.removeChild(e.firstChild);var i=r(c.text)[s](\"\\n\");for(var j=0,k=i[w];j<k;j++)if(i[j]){var m=bG(\"tspan\");j&&bG(m,{dy:h*bL,x:d.x});m[l](g.createTextNode(i[j]));e[l](m)}}else{i=e.getElementsByTagName(\"tspan\");for(j=0,k=i[w];j<k;j++)j&&bG(i[j],{dy:h*bL,x:d.x})}bG(e,{y:d.y});var n=b.getBBox(),o=d.y-(n.y+n.height/2);o&&a.is(o,\"finite\")&&bG(e,{y:d.y+o})},bN=function(b,c){var d=0,e=0;this[0]=b;this.id=a._oid++;this.node=b;b.raphael=this;this.paper=c;this.attrs=this.attrs||{};this.transformations=[];this._={tx:0,ty:0,rt:{deg:0,cx:0,cy:0},sx:1,sy:1};!c.bottom&&(c.bottom=this);this.prev=c.top;c.top&&(c.top.next=this);c.top=this;this.next=null},bO=bN[e];bN[e].rotate=function(c,d,e){if(this.removed)return this;if(c==null){if(this._.rt.cx)return[this._.rt.deg,this._.rt.cx,this._.rt.cy][v](q);return this._.rt.deg}var f=this.getBBox();c=r(c)[s](b);if(c[w]-1){d=S(c[1]);e=S(c[2])}c=S(c[0]);d!=null&&d!==false?this._.rt.deg=c:this._.rt.deg+=c;e==null&&(d=null);this._.rt.cx=d;this._.rt.cy=e;d=d==null?f.x+f.width/2:d;e=e==null?f.y+f.height/2:e;if(this._.rt.deg){this.transformations[0]=a.format(\"rotate({0} {1} {2})\",this._.rt.deg,d,e);this.clip&&bG(this.clip,{transform:a.format(\"rotate({0} {1} {2})\",-this._.rt.deg,d,e)})}else{this.transformations[0]=p;this.clip&&bG(this.clip,{transform:p})}bG(this.node,{transform:this.transformations[v](q)});return this};bN[e].hide=function(){!this.removed&&(this.node.style.display=\"none\");return this};bN[e].show=function(){!this.removed&&(this.node.style.display=\"\");return this};bN[e].remove=function(){if(this.removed)return;bA(this,this.paper);this.node.parentNode.removeChild(this.node);for(var a in this)delete this[a];this.removed=true};bN[e].getBBox=function(){if(this.removed)return this;if(this.type==\"path\")return bn(this.attrs.path);if(this.node.style.display==\"none\"){this.show();var a=true}var b={};try{b=this.node.getBBox()}catch(a){}finally{b=b||{}}if(this.type==\"text\"){b={x:b.x,y:Infinity,width:0,height:0};for(var c=0,d=this.node.getNumberOfChars();c<d;c++){var e=this.node.getExtentOfChar(c);e.y<b.y&&(b.y=e.y);e.y+e.height-b.y>b.height&&(b.height=e.y+e.height-b.y);e.x+e.width-b.x>b.width&&(b.width=e.x+e.width-b.x)}}a&&this.hide();return b};bN[e].attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale());d.gradient&&d.fill==\"none\"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,F)){if(b==\"translation\")return cz.call(this);if(b==\"rotation\")return this.rotate();if(b==\"scale\")return this.scale();if(b==I&&this.attrs.fill==\"none\"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(c==null&&a.is(b,G)){var g={};for(var h=0,i=b.length;h<i;h++)g[b[h]]=this.attr(b[h]);return g}if(c!=null){var j={};j[b]=c}else b!=null&&a.is(b,\"object\")&&(j=b);for(var k in this.paper.customAttributes)if(this.paper.customAttributes[f](k)&&j[f](k)&&a.is(this.paper.customAttributes[k],\"function\")){var l=this.paper.customAttributes[k].apply(this,[][n](j[k]));this.attrs[k]=j[k];for(var m in l)l[f](m)&&(j[m]=l[m])}bK(this,j);return this};bN[e].toFront=function(){if(this.removed)return this;this.node.parentNode[l](this.node);var a=this.paper;a.top!=this&&bB(this,a);return this};bN[e].toBack=function(){if(this.removed)return this;if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);bC(this,this.paper);var a=this.paper}return this};bN[e].insertAfter=function(a){if(this.removed)return this;var b=a.node||a[a.length-1].node;b.nextSibling?b.parentNode.insertBefore(this.node,b.nextSibling):b.parentNode[l](this.node);bD(this,a,this.paper);return this};bN[e].insertBefore=function(a){if(this.removed)return this;var b=a.node||a[0].node;b.parentNode.insertBefore(this.node,b);bE(this,a,this.paper);return this};bN[e].blur=function(a){var b=this;if(+a!==0){var c=bG(\"filter\"),d=bG(\"feGaussianBlur\");b.attrs.blur=a;c.id=bh();bG(d,{stdDeviation:+a||1.5});c.appendChild(d);b.paper.defs.appendChild(c);b._blur=c;bG(b.node,{filter:\"url(#\"+c.id+\")\"})}else{if(b._blur){b._blur.parentNode.removeChild(b._blur);delete b._blur;delete b.attrs.blur}b.node.removeAttribute(\"filter\")}};var bP=function(a,b,c,d){var e=bG(\"circle\");a.canvas&&a.canvas[l](e);var f=new bN(e,a);f.attrs={cx:b,cy:c,r:d,fill:\"none\",stroke:\"#000\"};f.type=\"circle\";bG(e,f.attrs);return f},bQ=function(a,b,c,d,e,f){var g=bG(\"rect\");a.canvas&&a.canvas[l](g);var h=new bN(g,a);h.attrs={x:b,y:c,width:d,height:e,r:f||0,rx:f||0,ry:f||0,fill:\"none\",stroke:\"#000\"};h.type=\"rect\";bG(g,h.attrs);return h},bR=function(a,b,c,d,e){var f=bG(\"ellipse\");a.canvas&&a.canvas[l](f);var g=new bN(f,a);g.attrs={cx:b,cy:c,rx:d,ry:e,fill:\"none\",stroke:\"#000\"};g.type=\"ellipse\";bG(f,g.attrs);return g},bS=function(a,b,c,d,e,f){var g=bG(\"image\");bG(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:\"none\"});g.setAttributeNS(a.xlink,\"href\",b);a.canvas&&a.canvas[l](g);var h=new bN(g,a);h.attrs={x:c,y:d,width:e,height:f,src:b};h.type=\"image\";return h},bT=function(a,b,c,d){var e=bG(\"text\");bG(e,{x:b,y:c,\"text-anchor\":\"middle\"});a.canvas&&a.canvas[l](e);var f=new bN(e,a);f.attrs={x:b,y:c,\"text-anchor\":\"middle\",text:d,font:W.font,stroke:\"none\",fill:\"#000\"};f.type=\"text\";bK(f,f.attrs);return f},bU=function(a,b){this.width=a||this.width;this.height=b||this.height;this.canvas[R](\"width\",this.width);this.canvas[R](\"height\",this.height);return this},bV=function(){var b=by[m](0,arguments),c=b&&b.container,d=b.x,e=b.y,f=b.width,h=b.height;if(!c)throw new Error(\"SVG container not found.\");var i=bG(\"svg\");d=d||0;e=e||0;f=f||512;h=h||342;bG(i,{xmlns:\"http://www.w3.org/2000/svg\",version:1.1,width:f,height:h});if(c==1){i.style.cssText=\"position:absolute;left:\"+d+\"px;top:\"+e+\"px\";g.body[l](i)}else c.firstChild?c.insertBefore(i,c.firstChild):c[l](i);c=new j;c.width=f;c.height=h;c.canvas=i;bz.call(c,c,a.fn);c.clear();return c};k.clear=function(){var a=this.canvas;while(a.firstChild)a.removeChild(a.firstChild);this.bottom=this.top=null;(this.desc=bG(\"desc\"))[l](g.createTextNode(\"Created with Raphaël\"));a[l](this.desc);a[l](this.defs=bG(\"defs\"))};k.remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a)}}if(a.vml){var bW={M:\"m\",L:\"l\",C:\"c\",Z:\"x\",m:\"t\",l:\"r\",c:\"v\",z:\"x\"},bX=/([clmz]),?([^clmz]*)/gi,bY=/ progid:\\S+Blur\\([^\\)]+\\)/g,bZ=/-?[^,\\s-]+/g,b$=1000+q+1000,b_=10,ca={path:1,rect:1},cb=function(a){var b=/[ahqstv]/ig,c=bq;r(a).match(b)&&(c=bw);b=/[clmz]/g;if(c==bq&&!r(a).match(b)){var d=r(a)[Y](bX,function(a,b,c){var d=[],e=x.call(b)==\"m\",f=bW[b];c[Y](bZ,function(a){if(e&&d[w]==2){f+=d+bW[b==\"m\"?\"l\":\"L\"];d=[]}d[L](Q(a*b_))});return f+d});return d}var e=c(a),f,g;d=[];for(var h=0,i=e[w];h<i;h++){f=e[h];g=x.call(e[h][0]);g==\"z\"&&(g=\"x\");for(var j=1,k=f[w];j<k;j++)g+=Q(f[j]*b_)+(j!=k-1?\",\":p);d[L](g)}return d[v](q)};a[H]=function(){return\"Your browser doesn’t support SVG. Falling down to VML.\\nYou are running Raphaël \"+this.version};bH=function(a,b){var c=cd(\"group\");c.style.cssText=\"position:absolute;left:0;top:0;width:\"+b.width+\"px;height:\"+b.height+\"px\";c.coordsize=b.coordsize;c.coordorigin=b.coordorigin;var d=cd(\"shape\"),e=d.style;e.width=b.width+\"px\";e.height=b.height+\"px\";d.coordsize=b$;d.coordorigin=b.coordorigin;c[l](d);var f=new bN(d,c,b),g={fill:\"none\",stroke:\"#000\"};a&&(g.path=a);f.type=\"path\";f.path=[];f.Path=p;bK(f,g);b.canvas[l](c);return f};bK=function(c,d){c.attrs=c.attrs||{};var e=c.node,h=c.attrs,i=e.style,j,k=(d.x!=h.x||d.y!=h.y||d.width!=h.width||d.height!=h.height||d.r!=h.r)&&c.type==\"rect\",m=c;for(var n in d)d[f](n)&&(h[n]=d[n]);if(k){h.path=cc(h.x,h.y,h.width,h.height,h.r);c.X=h.x;c.Y=h.y;c.W=h.width;c.H=h.height}d.href&&(e.href=d.href);d.title&&(e.title=d.title);d.target&&(e.target=d.target);d.cursor&&(i.cursor=d.cursor);\"blur\"in d&&c.blur(d.blur);if(d.path&&c.type==\"path\"||k)e.path=cb(h.path);d.rotation!=null&&c.rotate(d.rotation,true);if(d.translation){j=r(d.translation)[s](b);cz.call(c,j[0],j[1]);if(c._.rt.cx!=null){c._.rt.cx+=+j[0];c._.rt.cy+=+j[1];c.setBox(c.attrs,j[0],j[1])}}if(d.scale){j=r(d.scale)[s](b);c.scale(+j[0]||1,+j[1]||+j[0]||1,+j[2]||null,+j[3]||null)}if(\"clip-rect\"in d){var o=r(d[\"clip-rect\"])[s](b);if(o[w]==4){o[2]=+o[2]+ +o[0];o[3]=+o[3]+ +o[1];var q=e.clipRect||g.createElement(\"div\"),t=q.style,u=e.parentNode;t.clip=a.format(\"rect({1}px {2}px {3}px {0}px)\",o);if(!e.clipRect){t.position=\"absolute\";t.top=0;t.left=0;t.width=c.paper.width+\"px\";t.height=c.paper.height+\"px\";u.parentNode.insertBefore(q,u);q[l](u);e.clipRect=q}}d[\"clip-rect\"]||e.clipRect&&(e.clipRect.style.clip=p)}c.type==\"image\"&&d.src&&(e.src=d.src);if(c.type==\"image\"&&d.opacity){e.filterOpacity=U+\".Alpha(opacity=\"+d.opacity*100+\")\";i.filter=(e.filterMatrix||p)+(e.filterOpacity||p)}d.font&&(i.font=d.font);d[\"font-family\"]&&(i.fontFamily=\"\\\"\"+d[\"font-family\"][s](\",\")[0][Y](/^['\"]+|['\"]+$/g,p)+\"\\\"\");d[\"font-size\"]&&(i.fontSize=d[\"font-size\"]);d[\"font-weight\"]&&(i.fontWeight=d[\"font-weight\"]);d[\"font-style\"]&&(i.fontStyle=d[\"font-style\"]);if(d.opacity!=null||d[\"stroke-width\"]!=null||d.fill!=null||d.stroke!=null||d[\"stroke-width\"]!=null||d[\"stroke-opacity\"]!=null||d[\"fill-opacity\"]!=null||d[\"stroke-dasharray\"]!=null||d[\"stroke-miterlimit\"]!=null||d[\"stroke-linejoin\"]!=null||d[\"stroke-linecap\"]!=null){e=c.shape||e;var v=e.getElementsByTagName(I)&&e.getElementsByTagName(I)[0],x=false;!v&&(x=v=cd(I));if(\"fill-opacity\"in d||\"opacity\"in d){var y=((+h[\"fill-opacity\"]+1||2)-1)*((+h.opacity+1||2)-1)*((+a.getRGB(d.fill).o+1||2)-1);y=A(z(y,0),1);v.opacity=y}d.fill&&(v.on=true);if(v.on==null||d.fill==\"none\")v.on=false;if(v.on&&d.fill){var B=d.fill.match(M);if(B){v.src=B[1];v.type=\"tile\"}else{v.color=a.getRGB(d.fill).hex;v.src=p;v.type=\"solid\";if(a.getRGB(d.fill).error&&(m.type in{circle:1,ellipse:1}||r(d.fill).charAt()!=\"r\")&&bI(m,d.fill)){h.fill=\"none\";h.gradient=d.fill}}}x&&e[l](v);var C=e.getElementsByTagName(\"stroke\")&&e.getElementsByTagName(\"stroke\")[0],D=false;!C&&(D=C=cd(\"stroke\"));if(d.stroke&&d.stroke!=\"none\"||d[\"stroke-width\"]||d[\"stroke-opacity\"]!=null||d[\"stroke-dasharray\"]||d[\"stroke-miterlimit\"]||d[\"stroke-linejoin\"]||d[\"stroke-linecap\"])C.on=true;(d.stroke==\"none\"||C.on==null||d.stroke==0||d[\"stroke-width\"]==0)&&(C.on=false);var E=a.getRGB(d.stroke);C.on&&d.stroke&&(C.color=E.hex);y=((+h[\"stroke-opacity\"]+1||2)-1)*((+h.opacity+1||2)-1)*((+E.o+1||2)-1);var F=(S(d[\"stroke-width\"])||1)*0.75;y=A(z(y,0),1);d[\"stroke-width\"]==null&&(F=h[\"stroke-width\"]);d[\"stroke-width\"]&&(C.weight=F);F&&F<1&&(y*=F)&&(C.weight=1);C.opacity=y;d[\"stroke-linejoin\"]&&(C.joinstyle=d[\"stroke-linejoin\"]||\"miter\");C.miterlimit=d[\"stroke-miterlimit\"]||8;d[\"stroke-linecap\"]&&(C.endcap=d[\"stroke-linecap\"]==\"butt\"?\"flat\":d[\"stroke-linecap\"]==\"square\"?\"square\":\"round\");if(d[\"stroke-dasharray\"]){var G={\"-\":\"shortdash\",\".\":\"shortdot\",\"-.\":\"shortdashdot\",\"-..\":\"shortdashdotdot\",\". \":\"dot\",\"- \":\"dash\",\"--\":\"longdash\",\"- .\":\"dashdot\",\"--.\":\"longdashdot\",\"--..\":\"longdashdotdot\"};C.dashstyle=G[f](d[\"stroke-dasharray\"])?G[d[\"stroke-dasharray\"]]:p}D&&e[l](C)}if(m.type==\"text\"){i=m.paper.span.style;h.font&&(i.font=h.font);h[\"font-family\"]&&(i.fontFamily=h[\"font-family\"]);h[\"font-size\"]&&(i.fontSize=h[\"font-size\"]);h[\"font-weight\"]&&(i.fontWeight=h[\"font-weight\"]);h[\"font-style\"]&&(i.fontStyle=h[\"font-style\"]);m.node.string&&(m.paper.span.innerHTML=r(m.node.string)[Y](/</g,\"&#60;\")[Y](/&/g,\"&#38;\")[Y](/\\n/g,\"<br>\"));m.W=h.w=m.paper.span.offsetWidth;m.H=h.h=m.paper.span.offsetHeight;m.X=h.x;m.Y=h.y+Q(m.H/2);switch(h[\"text-anchor\"]){case\"start\":m.node.style[\"v-text-align\"]=\"left\";m.bbx=Q(m.W/2);break;case\"end\":m.node.style[\"v-text-align\"]=\"right\";m.bbx=-Q(m.W/2);break;default:m.node.style[\"v-text-align\"]=\"center\";break}}};bI=function(a,b){a.attrs=a.attrs||{};var c=a.attrs,d,e=\"linear\",f=\".5 .5\";a.attrs.gradient=b;b=r(b)[Y](bd,function(a,b,c){e=\"radial\";if(b&&c){b=S(b);c=S(c);C(b-0.5,2)+C(c-0.5,2)>0.25&&(c=y.sqrt(0.25-C(b-0.5,2))*((c>0.5)*2-1)+0.5);f=b+q+c}return p});b=b[s](/\\s*\\-\\s*/);if(e==\"linear\"){var g=b.shift();g=-S(g);if(isNaN(g))return null}var h=bx(b);if(!h)return null;a=a.shape||a.node;d=a.getElementsByTagName(I)[0]||cd(I);!d.parentNode&&a.appendChild(d);if(h[w]){d.on=true;d.method=\"none\";d.color=h[0].color;d.color2=h[h[w]-1].color;var i=[];for(var j=0,k=h[w];j<k;j++)h[j].offset&&i[L](h[j].offset+q+h[j].color);d.colors&&(d.colors.value=i[w]?i[v]():\"0% \"+d.color);if(e==\"radial\"){d.type=\"gradientradial\";d.focus=\"100%\";d.focussize=f;d.focusposition=f}else{d.type=\"gradient\";d.angle=(270-g)%360}}return 1};bN=function(b,c,d){var e=0,f=0,g=0,h=1;this[0]=b;this.id=a._oid++;this.node=b;b.raphael=this;this.X=0;this.Y=0;this.attrs={};this.Group=c;this.paper=d;this._={tx:0,ty:0,rt:{deg:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null};bO=bN[e];bO.rotate=function(a,c,d){if(this.removed)return this;if(a==null){if(this._.rt.cx)return[this._.rt.deg,this._.rt.cx,this._.rt.cy][v](q);return this._.rt.deg}a=r(a)[s](b);if(a[w]-1){c=S(a[1]);d=S(a[2])}a=S(a[0]);c!=null?this._.rt.deg=a:this._.rt.deg+=a;d==null&&(c=null);this._.rt.cx=c;this._.rt.cy=d;this.setBox(this.attrs,c,d);this.Group.style.rotation=this._.rt.deg;return this};bO.setBox=function(a,b,c){if(this.removed)return this;var d=this.Group.style,e=this.shape&&this.shape.style||this.node.style;a=a||{};for(var g in a)a[f](g)&&(this.attrs[g]=a[g]);b=b||this._.rt.cx;c=c||this._.rt.cy;var h=this.attrs,i,j,k,l;switch(this.type){case\"circle\":i=h.cx-h.r;j=h.cy-h.r;k=l=h.r*2;break;case\"ellipse\":i=h.cx-h.rx;j=h.cy-h.ry;k=h.rx*2;l=h.ry*2;break;case\"image\":i=+h.x;j=+h.y;k=h.width||0;l=h.height||0;break;case\"text\":this.textpath.v=[\"m\",Q(h.x),\", \",Q(h.y-2),\"l\",Q(h.x)+1,\", \",Q(h.y-2)][v](p);i=h.x-Q(this.W/2);j=h.y-this.H/2;k=this.W;l=this.H;break;case\"rect\":case\"path\":if(this.attrs.path){var m=bn(this.attrs.path);i=m.x;j=m.y;k=m.width;l=m.height}else{i=0;j=0;k=this.paper.width;l=this.paper.height}break;default:i=0;j=0;k=this.paper.width;l=this.paper.height;break}b=b==null?i+k/2:b;c=c==null?j+l/2:c;var n=b-this.paper.width/2,o=c-this.paper.height/2,q;d.left!=(q=n+\"px\")&&(d.left=q);d.top!=(q=o+\"px\")&&(d.top=q);this.X=ca[f](this.type)?-n:i;this.Y=ca[f](this.type)?-o:j;this.W=k;this.H=l;if(ca[f](this.type)){e.left!=(q=-n*b_+\"px\")&&(e.left=q);e.top!=(q=-o*b_+\"px\")&&(e.top=q)}else if(this.type==\"text\"){e.left!=(q=-n+\"px\")&&(e.left=q);e.top!=(q=-o+\"px\")&&(e.top=q)}else{d.width!=(q=this.paper.width+\"px\")&&(d.width=q);d.height!=(q=this.paper.height+\"px\")&&(d.height=q);e.left!=(q=i-n+\"px\")&&(e.left=q);e.top!=(q=j-o+\"px\")&&(e.top=q);e.width!=(q=k+\"px\")&&(e.width=q);e.height!=(q=l+\"px\")&&(e.height=q)}};bO.hide=function(){!this.removed&&(this.Group.style.display=\"none\");return this};bO.show=function(){!this.removed&&(this.Group.style.display=\"block\");return this};bO.getBBox=function(){if(this.removed)return this;if(ca[f](this.type))return bn(this.attrs.path);return{x:this.X+(this.bbx||0),y:this.Y,width:this.W,height:this.H}};bO.remove=function(){if(this.removed)return;bA(this,this.paper);this.node.parentNode.removeChild(this.node);this.Group.parentNode.removeChild(this.Group);this.shape&&this.shape.parentNode.removeChild(this.shape);for(var a in this)delete this[a];this.removed=true};bO.attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale());d.gradient&&d.fill==\"none\"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,\"string\")){if(b==\"translation\")return cz.call(this);if(b==\"rotation\")return this.rotate();if(b==\"scale\")return this.scale();if(b==I&&this.attrs.fill==\"none\"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(this.attrs&&c==null&&a.is(b,G)){var g,h={};for(e=0,g=b[w];e<g;e++)h[b[e]]=this.attr(b[e]);return h}var i;if(c!=null){i={};i[b]=c}c==null&&a.is(b,\"object\")&&(i=b);if(i){for(var j in this.paper.customAttributes)if(this.paper.customAttributes[f](j)&&i[f](j)&&a.is(this.paper.customAttributes[j],\"function\")){var k=this.paper.customAttributes[j].apply(this,[][n](i[j]));this.attrs[j]=i[j];for(var l in k)k[f](l)&&(i[l]=k[l])}i.text&&this.type==\"text\"&&(this.node.string=i.text);bK(this,i);i.gradient&&(({circle:1,ellipse:1})[f](this.type)||r(i.gradient).charAt()!=\"r\")&&bI(this,i.gradient);(!ca[f](this.type)||this._.rt.deg)&&this.setBox(this.attrs)}return this};bO.toFront=function(){!this.removed&&this.Group.parentNode[l](this.Group);this.paper.top!=this&&bB(this,this.paper);return this};bO.toBack=function(){if(this.removed)return this;if(this.Group.parentNode.firstChild!=this.Group){this.Group.parentNode.insertBefore(this.Group,this.Group.parentNode.firstChild);bC(this,this.paper)}return this};bO.insertAfter=function(a){if(this.removed)return this;a.constructor==cC&&(a=a[a.length-1]);a.Group.nextSibling?a.Group.parentNode.insertBefore(this.Group,a.Group.nextSibling):a.Group.parentNode[l](this.Group);bD(this,a,this.paper);return this};bO.insertBefore=function(a){if(this.removed)return this;a.constructor==cC&&(a=a[0]);a.Group.parentNode.insertBefore(this.Group,a.Group);bE(this,a,this.paper);return this};bO.blur=function(b){var c=this.node.runtimeStyle,d=c.filter;d=d.replace(bY,p);if(+b!==0){this.attrs.blur=b;c.filter=d+q+U+\".Blur(pixelradius=\"+(+b||1.5)+\")\";c.margin=a.format(\"-{0}px 0 0 -{0}px\",Q(+b||1.5))}else{c.filter=d;c.margin=0;delete this.attrs.blur}};bP=function(a,b,c,d){var e=cd(\"group\"),f=cd(\"oval\"),g=f.style;e.style.cssText=\"position:absolute;left:0;top:0;width:\"+a.width+\"px;height:\"+a.height+\"px\";e.coordsize=b$;e.coordorigin=a.coordorigin;e[l](f);var h=new bN(f,e,a);h.type=\"circle\";bK(h,{stroke:\"#000\",fill:\"none\"});h.attrs.cx=b;h.attrs.cy=c;h.attrs.r=d;h.setBox({x:b-d,y:c-d,width:d*2,height:d*2});a.canvas[l](e);return h};function cc(b,c,d,e,f){return f?a.format(\"M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z\",b+f,c,d-f*2,f,-f,e-f*2,f*2-d,f*2-e):a.format(\"M{0},{1}l{2},0,0,{3},{4},0z\",b,c,d,e,-d)}bQ=function(a,b,c,d,e,f){var g=cc(b,c,d,e,f),h=a.path(g),i=h.attrs;h.X=i.x=b;h.Y=i.y=c;h.W=i.width=d;h.H=i.height=e;i.r=f;i.path=g;h.type=\"rect\";return h};bR=function(a,b,c,d,e){var f=cd(\"group\"),g=cd(\"oval\"),h=g.style;f.style.cssText=\"position:absolute;left:0;top:0;width:\"+a.width+\"px;height:\"+a.height+\"px\";f.coordsize=b$;f.coordorigin=a.coordorigin;f[l](g);var i=new bN(g,f,a);i.type=\"ellipse\";bK(i,{stroke:\"#000\"});i.attrs.cx=b;i.attrs.cy=c;i.attrs.rx=d;i.attrs.ry=e;i.setBox({x:b-d,y:c-e,width:d*2,height:e*2});a.canvas[l](f);return i};bS=function(a,b,c,d,e,f){var g=cd(\"group\"),h=cd(\"image\");g.style.cssText=\"position:absolute;left:0;top:0;width:\"+a.width+\"px;height:\"+a.height+\"px\";g.coordsize=b$;g.coordorigin=a.coordorigin;h.src=b;g[l](h);var i=new bN(h,g,a);i.type=\"image\";i.attrs.src=b;i.attrs.x=c;i.attrs.y=d;i.attrs.w=e;i.attrs.h=f;i.setBox({x:c,y:d,width:e,height:f});a.canvas[l](g);return i};bT=function(b,c,d,e){var f=cd(\"group\"),g=cd(\"shape\"),h=g.style,i=cd(\"path\"),j=i.style,k=cd(\"textpath\");f.style.cssText=\"position:absolute;left:0;top:0;width:\"+b.width+\"px;height:\"+b.height+\"px\";f.coordsize=b$;f.coordorigin=b.coordorigin;i.v=a.format(\"m{0},{1}l{2},{1}\",Q(c*10),Q(d*10),Q(c*10)+1);i.textpathok=true;h.width=b.width;h.height=b.height;k.string=r(e);k.on=true;g[l](k);g[l](i);f[l](g);var m=new bN(k,f,b);m.shape=g;m.textpath=i;m.type=\"text\";m.attrs.text=e;m.attrs.x=c;m.attrs.y=d;m.attrs.w=1;m.attrs.h=1;bK(m,{font:W.font,stroke:\"none\",fill:\"#000\"});m.setBox();b.canvas[l](f);return m};bU=function(a,b){var c=this.canvas.style;a==+a&&(a+=\"px\");b==+b&&(b+=\"px\");c.width=a;c.height=b;c.clip=\"rect(0 \"+a+\" \"+b+\" 0)\";return this};var cd;g.createStyleSheet().addRule(\".rvml\",\"behavior:url(#default#VML)\");try{!g.namespaces.rvml&&g.namespaces.add(\"rvml\",\"urn:schemas-microsoft-com:vml\");cd=function(a){return g.createElement(\"<rvml:\"+a+\" class=\\\"rvml\\\">\")}}catch(a){cd=function(a){return g.createElement(\"<\"+a+\" xmlns=\\\"urn:schemas-microsoft.com:vml\\\" class=\\\"rvml\\\">\")}}bV=function(){var b=by[m](0,arguments),c=b.container,d=b.height,e,f=b.width,h=b.x,i=b.y;if(!c)throw new Error(\"VML container not found.\");var k=new j,n=k.canvas=g.createElement(\"div\"),o=n.style;h=h||0;i=i||0;f=f||512;d=d||342;f==+f&&(f+=\"px\");d==+d&&(d+=\"px\");k.width=1000;k.height=1000;k.coordsize=b_*1000+q+b_*1000;k.coordorigin=\"0 0\";k.span=g.createElement(\"span\");k.span.style.cssText=\"position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;\";n[l](k.span);o.cssText=a.format(\"top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden\",f,d);if(c==1){g.body[l](n);o.left=h+\"px\";o.top=i+\"px\";o.position=\"absolute\"}else c.firstChild?c.insertBefore(n,c.firstChild):c[l](n);bz.call(k,k,a.fn);return k};k.clear=function(){this.canvas.innerHTML=p;this.span=g.createElement(\"span\");this.span.style.cssText=\"position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;\";this.canvas[l](this.span);this.bottom=this.top=null};k.remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a);return true}}var ce=navigator.userAgent.match(/Version\\\\x2f(.*?)\\s/);navigator.vendor==\"Apple Computer, Inc.\"&&(ce&&ce[1]<4||navigator.platform.slice(0,2)==\"iP\")?k.safari=function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:\"none\"});h.setTimeout(function(){a.remove()})}:k.safari=function(){};var cf=function(){this.returnValue=false},cg=function(){return this.originalEvent.preventDefault()},ch=function(){this.cancelBubble=true},ci=function(){return this.originalEvent.stopPropagation()},cj=(function(){{if(g.addEventListener)return function(a,b,c,d){var e=o&&u[b]?u[b]:b,g=function(e){if(o&&u[f](b))for(var g=0,h=e.targetTouches&&e.targetTouches.length;g<h;g++){if(e.targetTouches[g].target==a){var i=e;e=e.targetTouches[g];e.originalEvent=i;e.preventDefault=cg;e.stopPropagation=ci;break}}return c.call(d,e)};a.addEventListener(e,g,false);return function(){a.removeEventListener(e,g,false);return true}};if(g.attachEvent)return function(a,b,c,d){var e=function(a){a=a||h.event;a.preventDefault=a.preventDefault||cf;a.stopPropagation=a.stopPropagation||ch;return c.call(d,a)};a.attachEvent(\"on\"+b,e);var f=function(){a.detachEvent(\"on\"+b,e);return true};return f}}})(),ck=[],cl=function(a){var b=a.clientX,c=a.clientY,d=g.documentElement.scrollTop||g.body.scrollTop,e=g.documentElement.scrollLeft||g.body.scrollLeft,f,h=ck.length;while(h--){f=ck[h];if(o){var i=a.touches.length,j;while(i--){j=a.touches[i];if(j.identifier==f.el._drag.id){b=j.clientX;c=j.clientY;(a.originalEvent?a.originalEvent:a).preventDefault();break}}}else a.preventDefault();b+=e;c+=d;f.move&&f.move.call(f.move_scope||f.el,b-f.el._drag.x,c-f.el._drag.y,b,c,a)}},cm=function(b){a.unmousemove(cl).unmouseup(cm);var c=ck.length,d;while(c--){d=ck[c];d.el._drag={};d.end&&d.end.call(d.end_scope||d.start_scope||d.move_scope||d.el,b)}ck=[]};for(var cn=t[w];cn--;)(function(b){a[b]=bN[e][b]=function(c,d){if(a.is(c,\"function\")){this.events=this.events||[];this.events.push({name:b,f:c,unbind:cj(this.shape||this.node||g,b,c,d||this)})}return this};a[\"un\"+b]=bN[e][\"un\"+b]=function(a){var c=this.events,d=c[w];while(d--)if(c[d].name==b&&c[d].f==a){c[d].unbind();c.splice(d,1);!c.length&&delete this.events;return this}return this}})(t[cn]);bO.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)};bO.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};bO.drag=function(b,c,d,e,f,h){this._drag={};this.mousedown(function(i){(i.originalEvent||i).preventDefault();var j=g.documentElement.scrollTop||g.body.scrollTop,k=g.documentElement.scrollLeft||g.body.scrollLeft;this._drag.x=i.clientX+k;this._drag.y=i.clientY+j;this._drag.id=i.identifier;c&&c.call(f||e||this,i.clientX+k,i.clientY+j,i);!ck.length&&a.mousemove(cl).mouseup(cm);ck.push({el:this,move:b,end:d,move_scope:e,start_scope:f,end_scope:h})});return this};bO.undrag=function(b,c,d){var e=ck.length;while(e--)ck[e].el==this&&(ck[e].move==b&&ck[e].end==d)&&ck.splice(e++,1);!ck.length&&a.unmousemove(cl).unmouseup(cm)};k.circle=function(a,b,c){return bP(this,a||0,b||0,c||0)};k.rect=function(a,b,c,d,e){return bQ(this,a||0,b||0,c||0,d||0,e||0)};k.ellipse=function(a,b,c,d){return bR(this,a||0,b||0,c||0,d||0)};k.path=function(b){b&&!a.is(b,F)&&!a.is(b[0],G)&&(b+=p);return bH(a.format[m](a,arguments),this)};k.image=function(a,b,c,d,e){return bS(this,a||\"about:blank\",b||0,c||0,d||0,e||0)};k.text=function(a,b,c){return bT(this,a||0,b||0,r(c))};k.set=function(a){arguments[w]>1&&(a=Array[e].splice.call(arguments,0,arguments[w]));return new cC(a)};k.setSize=bU;k.top=k.bottom=null;k.raphael=a;function co(){return this.x+q+this.y}bO.resetScale=function(){if(this.removed)return this;this._.sx=1;this._.sy=1;this.attrs.scale=\"1 1\"};bO.scale=function(a,b,c,d){if(this.removed)return this;if(a==null&&b==null)return{x:this._.sx,y:this._.sy,toString:co};b=b||a;!(+b)&&(b=a);var e,f,g,h,i=this.attrs;if(a!=0){var j=this.getBBox(),k=j.x+j.width/2,l=j.y+j.height/2,m=B(a/this._.sx),o=B(b/this._.sy);c=+c||c==0?c:k;d=+d||d==0?d:l;var r=this._.sx>0,s=this._.sy>0,t=~(~(a/B(a))),u=~(~(b/B(b))),x=m*t,y=o*u,z=this.node.style,A=c+B(k-c)*x*(k>c==r?1:-1),C=d+B(l-d)*y*(l>d==s?1:-1),D=a*t>b*u?o:m;switch(this.type){case\"rect\":case\"image\":var E=i.width*m,F=i.height*o;this.attr({height:F,r:i.r*D,width:E,x:A-E/2,y:C-F/2});break;case\"circle\":case\"ellipse\":this.attr({rx:i.rx*m,ry:i.ry*o,r:i.r*D,cx:A,cy:C});break;case\"text\":this.attr({x:A,y:C});break;case\"path\":var G=bp(i.path),H=true,I=r?x:m,J=s?y:o;for(var K=0,L=G[w];K<L;K++){var M=G[K],N=V.call(M[0]);{if(N==\"M\"&&H)continue;H=false}if(N==\"A\"){M[G[K][w]-2]*=I;M[G[K][w]-1]*=J;M[1]*=m;M[2]*=o;M[5]=+(t+u?!(!(+M[5])):!(+M[5]))}else if(N==\"H\")for(var O=1,P=M[w];O<P;O++)M[O]*=I;else if(N==\"V\")for(O=1,P=M[w];O<P;O++)M[O]*=J;else for(O=1,P=M[w];O<P;O++)M[O]*=O%2?I:J}var Q=bn(G);e=A-Q.x-Q.width/2;f=C-Q.y-Q.height/2;G[0][1]+=e;G[0][2]+=f;this.attr({path:G});break}if(this.type in{text:1,image:1}&&(t!=1||u!=1))if(this.transformations){this.transformations[2]=\"scale(\"[n](t,\",\",u,\")\");this.node[R](\"transform\",this.transformations[v](q));e=t==-1?-i.x-(E||0):i.x;f=u==-1?-i.y-(F||0):i.y;this.attr({x:e,y:f});i.fx=t-1;i.fy=u-1}else{this.node.filterMatrix=U+\".Matrix(M11=\"[n](t,\", M12=0, M21=0, M22=\",u,\", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')\");z.filter=(this.node.filterMatrix||p)+(this.node.filterOpacity||p)}else if(this.transformations){this.transformations[2]=p;this.node[R](\"transform\",this.transformations[v](q));i.fx=0;i.fy=0}else{this.node.filterMatrix=p;z.filter=(this.node.filterMatrix||p)+(this.node.filterOpacity||p)}i.scale=[a,b,c,d][v](q);this._.sx=a;this._.sy=b}return this};bO.clone=function(){if(this.removed)return null;var a=this.attr();delete a.scale;delete a.translation;return this.paper[this.type]().attr(a)};var cp={},cq=function(b,c,d,e,f,g,h,i,j){var k=0,l=100,m=[b,c,d,e,f,g,h,i].join(),n=cp[m],o,p;!n&&(cp[m]=n={data:[]});n.timer&&clearTimeout(n.timer);n.timer=setTimeout(function(){delete cp[m]},2000);if(j!=null){var q=cq(b,c,d,e,f,g,h,i);l=~(~q)*10}for(var r=0;r<l+1;r++){if(n.data[j]>r)p=n.data[r*l];else{p=a.findDotsAtSegment(b,c,d,e,f,g,h,i,r/l);n.data[r]=p}r&&(k+=C(C(o.x-p.x,2)+C(o.y-p.y,2),0.5));if(j!=null&&k>=j)return p;o=p}if(j==null)return k},cr=function(b,c){return function(d,e,f){d=bw(d);var g,h,i,j,k=\"\",l={},m,n=0;for(var o=0,p=d.length;o<p;o++){i=d[o];if(i[0]==\"M\"){g=+i[1];h=+i[2]}else{j=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6]);if(n+j>e){if(c&&!l.start){m=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);k+=[\"C\",m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k;k=[\"M\",m.x,m.y+\"C\",m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]][v]();n+=j;g=+i[5];h=+i[6];continue}if(!b&&!c){m=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j;g=+i[5];h=+i[6]}k+=i}l.end=k;m=b?n:c?l:a.findDotsAtSegment(g,h,i[1],i[2],i[3],i[4],i[5],i[6],1);m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cs=cr(1),ct=cr(),cu=cr(0,1);bO.getTotalLength=function(){if(this.type!=\"path\")return;if(this.node.getTotalLength)return this.node.getTotalLength();return cs(this.attrs.path)};bO.getPointAtLength=function(a){if(this.type!=\"path\")return;return ct(this.attrs.path,a)};bO.getSubpath=function(a,b){if(this.type!=\"path\")return;if(B(this.getTotalLength()-b)<\"1e-6\")return cu(this.attrs.path,a).end;var c=cu(this.attrs.path,b,1);return a?cu(c,a).end:c};a.easing_formulas={linear:function(a){return a},\"<\":function(a){return C(a,3)},\">\":function(a){return C(a-1,3)+1},\"<>\":function(a){a=a*2;if(a<1)return C(a,3)/2;a-=2;return(C(a,3)+2)/2},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==0||a==1)return a;var b=0.3,c=b/4;return C(2,-10*a)*y.sin((a-c)*(2*D)/b)+1},bounce:function(a){var b=7.5625,c=2.75,d;if(a<1/c)d=b*a*a;else if(a<2/c){a-=1.5/c;d=b*a*a+0.75}else if(a<2.5/c){a-=2.25/c;d=b*a*a+0.9375}else{a-=2.625/c;d=b*a*a+0.984375}return d}};var cv=[],cw=function(){var b=+(new Date);for(var c=0;c<cv[w];c++){var d=cv[c];if(d.stop||d.el.removed)continue;var e=b-d.start,g=d.ms,h=d.easing,i=d.from,j=d.diff,k=d.to,l=d.t,m=d.el,n={},o;if(e<g){var r=h(e/g);for(var s in i)if(i[f](s)){switch(X[s]){case\"along\":o=r*g*j[s];k.back&&(o=k.len-o);var t=ct(k[s],o);m.translate(j.sx-j.x||0,j.sy-j.y||0);j.x=t.x;j.y=t.y;m.translate(t.x-j.sx,t.y-j.sy);k.rot&&m.rotate(j.r+t.alpha,t.x,t.y);break;case E:o=+i[s]+r*g*j[s];break;case\"colour\":o=\"rgb(\"+[cy(Q(i[s].r+r*g*j[s].r)),cy(Q(i[s].g+r*g*j[s].g)),cy(Q(i[s].b+r*g*j[s].b))][v](\",\")+\")\";break;case\"path\":o=[];for(var u=0,x=i[s][w];u<x;u++){o[u]=[i[s][u][0]];for(var y=1,z=i[s][u][w];y<z;y++)o[u][y]=+i[s][u][y]+r*g*j[s][u][y];o[u]=o[u][v](q)}o=o[v](q);break;case\"csv\":switch(s){case\"translation\":var A=r*g*j[s][0]-l.x,B=r*g*j[s][1]-l.y;l.x+=A;l.y+=B;o=A+q+B;break;case\"rotation\":o=+i[s][0]+r*g*j[s][0];i[s][1]&&(o+=\",\"+i[s][1]+\",\"+i[s][2]);break;case\"scale\":o=[+i[s][0]+r*g*j[s][0],+i[s][1]+r*g*j[s][1],2 in k[s]?k[s][2]:p,3 in k[s]?k[s][3]:p][v](q);break;case\"clip-rect\":o=[];u=4;while(u--)o[u]=+i[s][u]+r*g*j[s][u];break}break;default:var C=[].concat(i[s]);o=[];u=m.paper.customAttributes[s].length;while(u--)o[u]=+C[u]+r*g*j[s][u];break}n[s]=o}m.attr(n);m._run&&m._run.call(m)}else{if(k.along){t=ct(k.along,k.len*!k.back);m.translate(j.sx-(j.x||0)+t.x-j.sx,j.sy-(j.y||0)+t.y-j.sy);k.rot&&m.rotate(j.r+t.alpha,t.x,t.y)}(l.x||l.y)&&m.translate(-l.x,-l.y);k.scale&&(k.scale+=p);m.attr(k);cv.splice(c--,1)}}a.svg&&m&&m.paper&&m.paper.safari();cv[w]&&setTimeout(cw)},cx=function(b,c,d,e,f){var g=d-e;c.timeouts.push(setTimeout(function(){a.is(f,\"function\")&&f.call(c);c.animate(b,g,b.easing)},e))},cy=function(a){return z(A(a,255),0)},cz=function(a,b){if(a==null)return{x:this._.tx,y:this._.ty,toString:co};this._.tx+=+a;this._.ty+=+b;switch(this.type){case\"circle\":case\"ellipse\":this.attr({cx:+a+this.attrs.cx,cy:+b+this.attrs.cy});break;case\"rect\":case\"image\":case\"text\":this.attr({x:+a+this.attrs.x,y:+b+this.attrs.y});break;case\"path\":var c=bp(this.attrs.path);c[0][1]+=+a;c[0][2]+=+b;this.attr({path:c});break}return this};bO.animateWith=function(a,b,c,d,e){for(var f=0,g=cv.length;f<g;f++)cv[f].el.id==a.id&&(b.start=cv[f].start);return this.animate(b,c,d,e)};bO.animateAlong=cA();bO.animateAlongBack=cA(1);function cA(b){return function(c,d,e,f){var g={back:b};a.is(e,\"function\")?f=e:g.rot=e;c&&c.constructor==bN&&(c=c.attrs.path);c&&(g.along=c);return this.animate(g,d,f)}}function cB(a,b,c,d,e,f){var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;function m(a){return((i*a+h)*a+g)*a}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function o(a,b){var c,d,e,f,j,k;for(e=a,k=0;k<8;k++){f=m(e)-a;if(B(f)<b)return e;j=(3*i*e+2*h)*e+g;if(B(j)<0.000001)break;e=e-f/j}c=0;d=1;e=a;if(e<c)return c;if(e>d)return d;while(c<d){f=m(e);if(B(f-a)<b)return e;a>f?c=e:d=e;e=(d-c)/2+c}return e}return n(a,1/(200*f))}bO.onAnimation=function(a){this._run=a||0;return this};bO.animate=function(c,d,e,g){var h=this;h.timeouts=h.timeouts||[];if(a.is(e,\"function\")||!e)g=e||null;if(h.removed){g&&g.call(h);return h}var i={},j={},k=false,l={};for(var m in c)if(c[f](m)){if(X[f](m)||h.paper.customAttributes[f](m)){k=true;i[m]=h.attr(m);i[m]==null&&(i[m]=W[m]);j[m]=c[m];switch(X[m]){case\"along\":var n=cs(c[m]),o=ct(c[m],n*!(!c.back)),p=h.getBBox();l[m]=n/d;l.tx=p.x;l.ty=p.y;l.sx=o.x;l.sy=o.y;j.rot=c.rot;j.back=c.back;j.len=n;c.rot&&(l.r=S(h.rotate())||0);break;case E:l[m]=(j[m]-i[m])/d;break;case\"colour\":i[m]=a.getRGB(i[m]);var q=a.getRGB(j[m]);l[m]={r:(q.r-i[m].r)/d,g:(q.g-i[m].g)/d,b:(q.b-i[m].b)/d};break;case\"path\":var t=bw(i[m],j[m]);i[m]=t[0];var u=t[1];l[m]=[];for(var v=0,x=i[m][w];v<x;v++){l[m][v]=[0];for(var y=1,z=i[m][v][w];y<z;y++)l[m][v][y]=(u[v][y]-i[m][v][y])/d}break;case\"csv\":var A=r(c[m])[s](b),B=r(i[m])[s](b);switch(m){case\"translation\":i[m]=[0,0];l[m]=[A[0]/d,A[1]/d];break;case\"rotation\":i[m]=B[1]==A[1]&&B[2]==A[2]?B:[0,A[1],A[2]];l[m]=[(A[0]-i[m][0])/d,0,0];break;case\"scale\":c[m]=A;i[m]=r(i[m])[s](b);l[m]=[(A[0]-i[m][0])/d,(A[1]-i[m][1])/d,0,0];break;case\"clip-rect\":i[m]=r(i[m])[s](b);l[m]=[];v=4;while(v--)l[m][v]=(A[v]-i[m][v])/d;break}j[m]=A;break;default:A=[].concat(c[m]);B=[].concat(i[m]);l[m]=[];v=h.paper.customAttributes[m][w];while(v--)l[m][v]=((A[v]||0)-(B[v]||0))/d;break}}}if(k){var G=a.easing_formulas[e];if(!G){G=r(e).match(P);if(G&&G[w]==5){var H=G;G=function(a){return cB(a,+H[1],+H[2],+H[3],+H[4],d)}}else G=function(a){return a}}cv.push({start:c.start||+(new Date),ms:d,easing:G,from:i,diff:l,to:j,el:h,t:{x:0,y:0}});a.is(g,\"function\")&&(h._ac=setTimeout(function(){g.call(h)},d));cv[w]==1&&setTimeout(cw)}else{var C=[],D;for(var F in c)if(c[f](F)&&Z.test(F)){m={value:c[F]};F==\"from\"&&(F=0);F==\"to\"&&(F=100);m.key=T(F,10);C.push(m)}C.sort(be);C[0].key&&C.unshift({key:0,value:h.attrs});for(v=0,x=C[w];v<x;v++)cx(C[v].value,h,d/100*C[v].key,d/100*(C[v-1]&&C[v-1].key||0),C[v-1]&&C[v-1].value.callback);D=C[C[w]-1].value.callback;D&&h.timeouts.push(setTimeout(function(){D.call(h)},d))}return this};bO.stop=function(){for(var a=0;a<cv.length;a++)cv[a].el.id==this.id&&cv.splice(a--,1);for(a=0,ii=this.timeouts&&this.timeouts.length;a<ii;a++)clearTimeout(this.timeouts[a]);this.timeouts=[];clearTimeout(this._ac);delete this._ac;return this};bO.translate=function(a,b){return this.attr({translation:a+\" \"+b})};bO[H]=function(){return\"Raphaël’s object\"};a.ae=cv;var cC=function(a){this.items=[];this[w]=0;this.type=\"set\";if(a)for(var b=0,c=a[w];b<c;b++){if(a[b]&&(a[b].constructor==bN||a[b].constructor==cC)){this[this.items[w]]=this.items[this.items[w]]=a[b];this[w]++}}};cC[e][L]=function(){var a,b;for(var c=0,d=arguments[w];c<d;c++){a=arguments[c];if(a&&(a.constructor==bN||a.constructor==cC)){b=this.items[w];this[b]=this.items[b]=a;this[w]++}}return this};cC[e].pop=function(){delete this[this[w]--];return this.items.pop()};for(var cD in bO)bO[f](cD)&&(cC[e][cD]=(function(a){return function(){for(var b=0,c=this.items[w];b<c;b++)this.items[b][a][m](this.items[b],arguments);return this}})(cD));cC[e].attr=function(b,c){if(b&&a.is(b,G)&&a.is(b[0],\"object\"))for(var d=0,e=b[w];d<e;d++)this.items[d].attr(b[d]);else for(var f=0,g=this.items[w];f<g;f++)this.items[f].attr(b,c);return this};cC[e].animate=function(b,c,d,e){(a.is(d,\"function\")||!d)&&(e=d||null);var f=this.items[w],g=f,h,i=this,j;e&&(j=function(){!(--f)&&e.call(i)});d=a.is(d,F)?d:j;h=this.items[--g].animate(b,c,d,j);while(g--)this.items[g]&&!this.items[g].removed&&this.items[g].animateWith(h,b,c,d,j);return this};cC[e].insertAfter=function(a){var b=this.items[w];while(b--)this.items[b].insertAfter(a);return this};cC[e].getBBox=function(){var a=[],b=[],c=[],d=[];for(var e=this.items[w];e--;){var f=this.items[e].getBBox();a[L](f.x);b[L](f.y);c[L](f.x+f.width);d[L](f.y+f.height)}a=A[m](0,a);b=A[m](0,b);return{x:a,y:b,width:z[m](0,c)-a,height:z[m](0,d)-b}};cC[e].clone=function(a){a=new cC;for(var b=0,c=this.items[w];b<c;b++)a[L](this.items[b].clone());return a};a.registerFont=function(a){if(!a.face)return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face[\"font-family\"];for(var d in a.face)a.face[f](d)&&(b.face[d]=a.face[d]);this.fonts[c]?this.fonts[c][L](b):this.fonts[c]=[b];if(!a.svg){b.face[\"units-per-em\"]=T(a.face[\"units-per-em\"],10);for(var e in a.glyphs)if(a.glyphs[f](e)){var g=a.glyphs[e];b.glyphs[e]={w:g.w,k:{},d:g.d&&\"M\"+g.d[Y](/[mlcxtrv]/g,function(a){return({l:\"L\",c:\"C\",x:\"z\",t:\"m\",r:\"l\",v:\"c\"})[a]||\"M\"})+\"z\"};if(g.k)for(var h in g.k)g[f](h)&&(b.glyphs[e].k[h]=g.k[h])}}return a};k.getFont=function(b,c,d,e){e=e||\"normal\";d=d||\"normal\";c=+c||({normal:400,bold:700,lighter:300,bolder:800})[c]||400;if(!a.fonts)return;var g=a.fonts[b];if(!g){var h=new RegExp(\"(^|\\\\s)\"+b[Y](/[^\\w\\d\\s+!~.:_-]/g,p)+\"(\\\\s|$)\",\"i\");for(var i in a.fonts)if(a.fonts[f](i)){if(h.test(i)){g=a.fonts[i];break}}}var j;if(g)for(var k=0,l=g[w];k<l;k++){j=g[k];if(j.face[\"font-weight\"]==c&&(j.face[\"font-style\"]==d||!j.face[\"font-style\"])&&j.face[\"font-stretch\"]==e)break}return j};k.print=function(c,d,e,f,g,h,i){h=h||\"middle\";i=z(A(i||0,1),-1);var j=this.set(),k=r(e)[s](p),l=0,m=p,n;a.is(f,e)&&(f=this.getFont(f));if(f){n=(g||16)/f.face[\"units-per-em\"];var o=f.face.bbox.split(b),q=+o[0],t=+o[1]+(h==\"baseline\"?o[3]-o[1]+ +f.face.descent:(o[3]-o[1])/2);for(var u=0,v=k[w];u<v;u++){var x=u&&f.glyphs[k[u-1]]||{},y=f.glyphs[k[u]];l+=u?(x.w||f.w)+(x.k&&x.k[k[u]]||0)+f.w*i:0;y&&y.d&&j[L](this.path(y.d).attr({fill:\"#000\",stroke:\"none\",translation:[l,0]}))}j.scale(n,n,q,t).translate(c-q,d-t)}return j};a.format=function(b,c){var e=a.is(c,G)?[0][n](c):arguments;b&&a.is(b,F)&&e[w]-1&&(b=b[Y](d,function(a,b){return e[++b]==null?p:e[b]}));return b||p};a.ninja=function(){i.was?h.Raphael=i.is:delete Raphael;return a};a.el=bO;a.st=cC[e];i.was?h.Raphael=a:Raphael=a})()"
  },
  {
    "path": "cpp/workbench/readme.txt",
    "content": "this folder is for holding experiments related to development. you can safely ignor them.\n"
  },
  {
    "path": "cpp/workbench/triangle_knife_cut.js",
    "content": "function triangle_knife_cut(p1,p2,p3,kn1,kn2,kn3)\n{\n\tvar poly = []; //the retained polygon\n\tvar poly_cut = []; //the cut away polygon\n\n\tvar std_sign = signed_area( kn1,kn2,kn3);\n\tvar s1 = signed_area( kn1,kn2,p1)>0 == std_sign>0; //true means this point should be cut\n\tvar s2 = signed_area( kn1,kn2,p2)>0 == std_sign>0;\n\tvar s3 = signed_area( kn1,kn2,p3)>0 == std_sign>0;\n\tvar sums = s1+s2+s3;\n\t\n\tif ( sums == 0)\n\t{\t//all 3 points are retained\n\t\tpoly.push(p1);\n\t\tpoly.push(p2);\n\t\tpoly.push(p3);\n\t\treturn { poly:poly, poly_cut:poly_cut};\n\t}\n\telse if ( sums == 3)\n\t{\t//all 3 are cut away\n\t\tpoly_cut.push(p1);\n\t\tpoly_cut.push(p2);\n\t\tpoly_cut.push(p3);\n\t\treturn { poly:poly, poly_cut:poly_cut};\n\t}\n\telse\n\t{\n\t\tif ( sums == 2) {\n\t\t\ts1 = !s1;\n\t\t\ts2 = !s2;\n\t\t\ts3 = !s3;\n\t\t}\n\t\t//\n\t\tvar ip1,ip2, outp;\n\t\tif ( s1) { //here assume one point is cut away\n\t\t\toutp= p1;\n\t\t\tip1 = p2;\n\t\t\tip2 = p3;\n\t\t} else if ( s2) {\n\t\t\toutp= p2;\n\t\t\tip1 = p1;\n\t\t\tip2 = p3;\n\t\t} else if ( s3) {\n\t\t\toutp= p3;\n\t\t\tip1 = p1;\n\t\t\tip2 = p2;\n\t\t}\n\t\t\n\t\tvar interP1 = intersect( kn1,kn2, ip2,outp);\n\t\tvar interP2 = intersect( kn1,kn2, ip1,outp);\n\t\t//ip2 first gives a polygon\n\t\t//ip1 first gives a triangle strip\n\t\t\n\t\tpoly.push(ip1);\n\t\tpoly.push(ip2);\n\t\tpoly.push(interP1);\n\t\tpoly.push(interP2);\n\t\t\n\t\tpoly_cut.push(outp);\n\t\tpoly_cut.push(interP1);\n\t\tpoly_cut.push(interP2);\n\t\t\n\t\tif ( sums == 1) {\n\t\t\treturn { poly:poly, poly_cut:poly_cut};\n\t\t} else if ( sums == 2) {\n\t\t\treturn { poly:poly_cut, poly_cut:poly};\n\t\t\t//^ we swap the result!\n\t\t}\n\t}\n}\nfunction signed_area(p1,p2,p3)\n{\n\tvar D = (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);\n\treturn D;\n}\nfunction intersect( P1,P2,  //line 1\n\t\t\tP3,P4) //line 2\n{ //Determine the intersection point of two line segments\n\tvar mua,mub;\n\tvar denom,numera,numerb;\n\n\tdenom  = (P4.y-P3.y) * (P2.x-P1.x) - (P4.x-P3.x) * (P2.y-P1.y);\n\tnumera = (P4.x-P3.x) * (P1.y-P3.y) - (P4.y-P3.y) * (P1.x-P3.x);\n\tnumerb = (P2.x-P1.x) * (P1.y-P3.y) - (P2.y-P1.y) * (P1.x-P3.x);\n\t\n\tfunction negligible(M)\n\t{\n\t\treturn -0.00000001 < M && M < 0.00000001;\n\t}\n\t\n\tif ( negligible(numera) && negligible(numerb) && negligible(denom)) {\n\t\t//meaning the lines coincide\n\t\treturn { x: (P1.x + P2.x) * 0.5,\n\t\t\ty:  (P1.y + P2.y) * 0.5 };\n\t}\n\n\tif ( negligible(denom)) {\n\t\t//meaning lines are parallel\n\t\treturn { x:0, y:0};\n\t}\n\n\tmua = numera / denom;\n\tmub = numerb / denom;\n\n\treturn { x: P1.x + mua * (P2.x - P1.x),\n\t\ty:  P1.y + mua * (P2.y - P1.y) };\n//http://paulbourke.net/geometry/lineline2d/\n}\n"
  },
  {
    "path": "cpp/workbench/triangles_dual_knife_cut.html",
    "content": "<body>\n<script src='raphael-min.js'></script>\n<script src='dragger.js'></script>\n<script src='drawer.js'></script>\n<script src='triangle_knife_cut.js'></script>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n<div class='side_pane'>\nThis is the \"dual knife cut\" method of a triangle.<br />\nThe green points define the sides to be cut away.<br />\nIt can operate on multiple triangles.<br />\nThe cut away part is shown in red and retained part in blue.<br />\nThe 2 parts are given offsets (depend on the green points) so they look separated, just for fun.<br />\nDrag the anchors to play. You can also drag the tips of the triangles.<br />\n</div>\n\n<script>\n//math are in 'triangle_knife_cut.js'\n\n//demo application\nvar app_count=0;\n\nfunction make_polygon(TP,P,color, dP)\n{\n\tif ( P.length>0)\n\t{\n\t\tTP.moveTo(P[0].x+dP.x,P[0].y+dP.y,color);\n\t\tfor ( var i=0; i<P.length; i++)\n\t\t{\n\t\t\tTP.lineTo(P[i].x+dP.x,P[i].y+dP.y,color);\n\t\t}\n\t\tTP.lineTo(P[0].x+dP.x,P[0].y+dP.y,color);\n\t}\n}\nfunction demo_application1()\n{\n\tvar i=this.i=app_count;\n\tvar parent=this;\n\tapp_count++;\n\tdocument.write(\"<div class='main_canvas' id='canvas\"+i+\"'></div>\");\n\t\n\tthis.TP = new drawer();\n\tthis.DG = new dragger(this.TP,'canvas'+i);\n\tthis.rl = Raphael('canvas'+i);\n\t//\n\tthis.TP.px=[650,400,400, 350,450,250, 350,450,550];\n\tthis.TP.py=[300,300,550, 200,400,400, 200,400,200];\n\n\tthis.TP.redraw=function()\n\t{\n\t\tvar rl=parent.rl;\n\t\tvar TP=parent.TP;\n\t\trl.clear();\n\t\trl.rect(0,0,800-1,600-1);\n\t\t\n\t\tfunction Point(i)\n\t\t{\n\t\t\treturn {x:TP.px[i],y:TP.py[i]};\n\t\t}\n\t\tvar kn1=0, kn2=1; kn3=2; //points that define a knife\n\t\t\n\t\t//draw knife\n\t\tTP.drawpoint(TP.px[kn1],TP.py[kn1], 6,2); //points\n\t\tTP.drawpoint(TP.px[kn2],TP.py[kn2], 6,0);\n\t\tTP.drawpoint(TP.px[kn3],TP.py[kn3], 6,2);\n\t\t\n\t\tTP.moveTo(TP.px[kn1],TP.py[kn1], 0); //lines\n\t\tTP.lineTo(TP.px[kn2],TP.py[kn2], 0);\n\t\tTP.moveTo(TP.px[kn2],TP.py[kn2], 0);\n\t\tTP.lineTo(TP.px[kn3],TP.py[kn3], 0);\n\t\tTP.drawpath(rl);\n\t\t\n\t\t//draw triangles\n\t\tfor ( var i=3; i<TP.px.length; i+=3)\n\t\t{\t//lines\n\t\t\tvar result1 = triangle_knife_cut( Point(i), Point(i+1), Point(i+2),\n\t\t\t\t\tPoint(kn1), Point(kn2), Point(kn3));\n\t\t\tvar result2 = triangle_knife_cut(\n\t\t\t\tresult1.poly_cut[0], result1.poly_cut[1], result1.poly_cut[2],\n\t\t\t\t\tPoint(kn3), Point(kn2), Point(kn1));\n\t\t\tvar result3 = 0;\n\t\t\tif ( result1.poly_cut.length > 3)\n\t\t\t{\n\t\t\tresult3 = triangle_knife_cut(\n\t\t\t\tresult1.poly_cut[0], result1.poly_cut[2], result1.poly_cut[3],\n\t\t\t\t\tPoint(kn3), Point(kn2), Point(kn1));\n\t\t\t}\n\t\t\t\n\t\t\tvar offset = { x: 0, y: 0};\n\t\t\t{\tvar Va = { x: TP.px[kn1]-TP.px[kn2],\n\t\t\t\t\ty: TP.py[kn1]-TP.py[kn2] };\n\t\t\t\tvar Vb = { x: TP.px[kn3]-TP.px[kn2],\n\t\t\t\t\ty: TP.py[kn3]-TP.py[kn2] };\n\t\t\t\tvar La = Math.sqrt(Va.x*Va.x + Va.y*Va.y);\n\t\t\t\tvar Lb = Math.sqrt(Vb.x*Vb.x + Vb.y*Vb.y);\n\t\t\t\tVa.x /= La;  Va.y /= La;\n\t\t\t\tVb.x /= Lb;  Vb.y /= Lb;\n\t\t\t\t\n\t\t\t\tvar Vs = { x: Va.x+Vb.x, y: Va.y+Vb.y};\n\t\t\t\toffset.x = Vs.x*10;\n\t\t\t\toffset.y = Vs.y*10;\n\t\t\t}\n\t\t\t\n\t\t\tmake_polygon(TP,result1.poly,    3, {x:-offset.x, y:-offset.y} );\n\t\t\tmake_polygon(TP,result2.poly,    3, {x:-offset.x, y:-offset.y} );\n\t\t\tmake_polygon(TP,result2.poly_cut,1, offset);\n\t\t\tif ( result3)\n\t\t\t{\n\t\t\tmake_polygon(TP,result3.poly,    3, {x:-offset.x, y:-offset.y} );\n\t\t\tmake_polygon(TP,result3.poly_cut,1, offset);\n\t\t\t}\n\t\t\t\n\t\t\tTP.fillpath(rl,3);\n\t\t\tTP.fillpath(rl,1);\n\t\t}\n\t}\n\tthis.TP.redraw();\n}\napp1 = new demo_application1();\n</script>\n</body>\n"
  },
  {
    "path": "cpp/workbench/triangles_knife_cut.html",
    "content": "<body>\n<script src='raphael-min.js'></script>\n<script src='dragger.js'></script>\n<script src='drawer.js'></script>\n<script src='triangle_knife_cut.js'></script>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n<div class='side_pane'>\nThis is the \"knife cut\" method of a triangle.<br />\nThe 2 black anchored points define the knife, while the green point define\nthe side to be cut away.<br />\nThe cut away part is shown in red and retained part in blue.<br />\nThe 2 parts are given offsets (depend on the green point) so they look separated, just for fun.<br />\nIt can operate on multiple triangles.<br />\nDrag the anchors to play. You can also drag the tips of the triangles.<br />\n</div>\n\n<script>\n//math are in 'triangle_knife_cut.js'\n\n//demo application\nvar app_count=0;\n\nfunction make_polygon(TP,P,color, dP)\n{\n\tif ( P.length>0)\n\t{\n\t\tTP.moveTo(P[0].x+dP.x,P[0].y+dP.y,color);\n\t\tfor ( var i=0; i<P.length; i++)\n\t\t{\n\t\t\tTP.lineTo(P[i].x+dP.x,P[i].y+dP.y,color);\n\t\t}\n\t\tTP.lineTo(P[0].x+dP.x,P[0].y+dP.y,color);\n\t}\n}\nfunction demo_application1()\n{\n\tvar i=this.i=app_count;\n\tvar parent=this;\n\tapp_count++;\n\tdocument.write(\"<div class='main_canvas' id='canvas\"+i+\"'></div>\");\n\t\n\tthis.TP = new drawer();\n\tthis.DG = new dragger(this.TP,'canvas'+i);\n\tthis.rl = Raphael('canvas'+i);\n\t//\n\tthis.TP.px=[700,100,100, 350,450,250, 350,450,550];\n\tthis.TP.py=[300,300,350, 200,400,400, 200,400,200];\n\n\tthis.TP.redraw=function()\n\t{\n\t\tvar rl=parent.rl;\n\t\tvar TP=parent.TP;\n\t\trl.clear();\n\t\trl.rect(0,0,800-1,600-1);\n\t\t\n\t\tfunction Point(i)\n\t\t{\n\t\t\treturn {x:TP.px[i],y:TP.py[i]};\n\t\t}\n\t\tvar kn1=0, kn2=1; kn3=2; //points that define a knife\n\t\t\n\t\t//draw knife\n\t\tTP.drawpoint(TP.px[kn1],TP.py[kn1], 6,0); //points\n\t\tTP.drawpoint(TP.px[kn2],TP.py[kn2], 6,0);\n\t\tTP.drawpoint(TP.px[kn3],TP.py[kn3], 4,2);\n\t\t\n\t\tTP.moveTo(TP.px[kn1],TP.py[kn1], 0); //lines\n\t\tTP.lineTo(TP.px[kn2],TP.py[kn2], 0);\n\t\tTP.moveTo(TP.px[kn2],TP.py[kn2], 4);\n\t\tTP.lineTo(TP.px[kn3],TP.py[kn3], 4);\n\t\tTP.drawpath(rl);\n\t\t\n\t\t//draw triangles\n\t\tfor ( var i=3; i<TP.px.length; i+=3)\n\t\t{\t//lines\t\t\t\n\t\t\tvar result = triangle_knife_cut( Point(i),Point(i+1),Point(i+2),\n\t\t\t\t\tPoint(kn1),Point(kn2),Point(kn3));\n\t\t\tvar offset = { x: (TP.px[kn3]-TP.px[kn2]) * 0.1,\n\t\t\t\ty: (TP.py[kn3]-TP.py[kn2]) * 0.1 };\n\t\t\t\n\t\t\tmake_polygon(TP,result.poly,3, {x:-offset.x, y:-offset.y} );\n\t\t\tmake_polygon(TP,result.poly_cut,1, offset);\n\t\t\tTP.fillpath(rl,3);\n\t\t\tTP.fillpath(rl,1);\n\t\t}\n\t}\n\tthis.TP.redraw();\n}\napp1 = new demo_application1();\n</script>\n</body>\n"
  },
  {
    "path": "cpp/workbench/vector_arc.html",
    "content": "<body>\n<script src='raphael-min.js'></script>\n<script src='dragger.js'></script>\n<script src='drawer.js'></script>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"layout.css\" />\n\n<div class='main_canvas' id='canvas1'></div>\n<div class='side_pane'>\n<button onclick='tooglebasic()'>toogle basic</button><br />\n<textarea id='txt'></textarea><br />\nNote that we always draw the arc from red to blue.<br />\nDrag control points to observe.<br />\nSource code in contained in this HTML file.\n</div>\n\n<script>\nTP = new drawer();\nDG = new dragger(TP,'canvas1');\nTP.px=[400,400-34.2,400+86.6];\nTP.py=[300,300-94.0,300-50.0];\n\nvar basic=0;\nfunction tooglebasic()\n{\n\tbasic = basic==0?1:0;\n\tTP.redraw();\n}\nwindow.onload = function()\n{\n\tvar location = window.location.href;\n\tif (location.match(/basic=1/))\n\t{\n\t\tbasic=1;\n\t\tTP.redraw();\n\t}\n}\n\nTP.redraw=function()\n{\n\trl.clear();\n\trl.rect(0,0,800-1,600-1);\n\tvar P={x:TP.px[0],y:TP.py[0]};\n\tvar A={x:TP.px[1]-TP.px[0], y:TP.py[1]-TP.py[0]};\n\tvar B={x:TP.px[2]-TP.px[0], y:TP.py[2]-TP.py[0]};\n\tif ( basic==0)\n\t\tvectors_to_arc( P,A,B);\n\telse\n\t\tbasic_vectors_arc( P,A,B,60);\n\tTP.drawpath(rl);\n}\nfunction roundc( c, d)\n{\n\tc*=Math.pow(10,d);\n\tc =Math.round(c);\n\tc/=Math.pow(10,d);\n\treturn c;\n}\n\nfunction vectors_to_arc( P, A,B)\n{\t//proper inner arc\n\tTP.drawarrow( P.x,P.y,P.x+A.x,P.y+A.y, null,1);\n\tTP.drawarrow( P.x,P.y,P.x+B.x,P.y+B.y, null,3);\n\tTP.drawpoint( P.x+A.x,P.y+A.y, 4,0);\n\tTP.drawpoint( P.x+B.x,P.y+B.y, 4,0);\n\trl.text(P.x+50,P.y,'proper inner arc');\n\t\n\tvar AL=Math.sqrt(A.x*A.x+A.y*A.y);\n\tvar BL=Math.sqrt(B.x*B.x+B.y*B.y);\n\tA.x/=AL;\n\tB.x/=BL;\n\tvar angle1=Math.acos(A.x);\n\tvar angle2=Math.acos(B.x);\n\tif ( A.y>0){angle1=2*Math.PI-angle1;}\n\tif ( B.y>0){angle2=2*Math.PI-angle2;}\n\n\tinner_arc( P,(AL+BL)/2,Math.PI/18, angle1,angle2);\n}\nfunction inner_arc( P,r,dangle, angle1,angle2)\n{\t//proper inner arc\n\tvar incremental=true;\n\t\n\tif ( angle2 > angle1)\n\t{\n\t\tif ( angle2-angle1>Math.PI)\n\t\t{\n\t\t\tangle2=angle2-2*Math.PI;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif ( angle1-angle2>Math.PI)\n\t\t{\n\t\t\tangle1=angle1-2*Math.PI;\n\t\t}\n\t}\n\tif ( angle1>angle2)\n\t{\n\t\tincremental = false; //means decremental\n\t}\n\n\tTXT = document.getElementById('txt');\n\tTXT.value=\"angle1: \"+roundc(angle1,2)+\"\\nangle2: \"+roundc(angle2,2);\n\n\tif ( incremental)\n\t{\n\t\tfor ( var a=angle1; a<angle2; a+=dangle)\n\t\t{\n\t\t\tvar x=Math.cos(a);\n\t\t\tvar y=Math.sin(a);\n\t\t\tTP.drawpoint( P.x+x*r,P.y-y*r, 2,0);\n\t\t}\n\t\tTP.drawarrow( P.x+Math.cos(angle1)*r, P.y-Math.sin(angle1)*r,\n\t\t\t\tP.x+Math.cos(angle2)*r, P.y-Math.sin(angle2)*r,\n\t\t\t\tnull,4);\n\t}\n\telse\n\t{\n\t\tfor ( var a=angle1; a>angle2; a-=dangle)\n\t\t{\n\t\t\tvar x=Math.cos(a);\n\t\t\tvar y=Math.sin(a);\n\t\t\tTP.drawpoint( P.x+x*r,P.y-y*r, 2,0);\n\t\t}\n\t\tTP.drawarrow( P.x+Math.cos(angle1)*r, P.y-Math.sin(angle1)*r,\n\t\t\t\tP.x+Math.cos(angle2)*r, P.y-Math.sin(angle2)*r,\n\t\t\t\tnull,4);\n\t}\n}\n\nfunction basic_vectors_arc( P, //origin\n         A, B,\n         r) //radius\n{\t//this is the demo of failed code\n\tTP.drawarrow( P.x,P.y,P.x+A.x,P.y+A.y, null,1);\n\tTP.drawarrow( P.x,P.y,P.x+B.x,P.y+B.y, null,3);\n\tTP.drawpoint( P.x+A.x,P.y+A.y, 4,0);\n\tTP.drawpoint( P.x+B.x,P.y+B.y, 4,0);\n\trl.text(P.x+30,P.y,'basic');\n\t\n\tvar AL=Math.sqrt(A.x*A.x+A.y*A.y);\n\tvar BL=Math.sqrt(B.x*B.x+B.y*B.y);\n\tA.x/=AL;\n\tB.x/=BL;\n\t\n\tvar angle1=Math.acos(A.x);    var angle2=Math.acos(B.x);\n\n\tbasic_arc( P,r,Math.PI/18, angle1,angle2);\n}\nfunction basic_arc( P, //origin\n         r,      //radius\n         dangle, //angle for each step\n         angle1, angle2)\n{\t//this is the demo of failed code\n\tvar incremental=true;\n\tif ( angle1>angle2) {\n\t\tincremental = false; //means decremental\n\t}\n\t\n\tif ( incremental) {\n\t\tfor ( var a=angle1; a < angle2; a+=dangle)\n\t\t{\n\t\t\tvar x=Math.cos(a);    var y=Math.sin(a);\n\t\t\tTP.drawpoint( P.x+x*r,P.y-y*r); //the current point on anchor\n\t\t}\n\t} else {\n\t\tfor ( var a=angle1; a > angle2; a-=dangle)\n\t\t{\n\t\t\tvar x=Math.cos(a);    var y=Math.sin(a);\n\t\t\tTP.drawpoint( P.x+x*r,P.y-y*r);\n\t\t}\n\t}\n}\n\n\nrl = Raphael('canvas1');\nTP.redraw();\n</script>\n</body>\n"
  },
  {
    "path": "csharp/Assets/Demo/Demo.cs",
    "content": "using Vaser;\nusing UnityEngine;\nusing System.Collections.Generic;\n\npublic class Demo : MonoBehaviour {\n\n    private int mode = 1;\n    private Polyline polyline = null;\n    private Polyline.Opt opt = new Polyline.Opt();\n    private GameObject myGameObject = null;\n\n    private void Start() {\n\n        opt.feather = false;\n        opt.feathering = 15f;\n        opt.joint = Polyline.Opt.PLJround;\n        opt.cap = Polyline.Opt.PLCround;\n        opt.triangulation = false;\n        {\n            Camera cam = Camera.main;\n            Vector3 a = cam.WorldToScreenPoint(new Vector3(0,0,0));\n            Vector3 b = cam.WorldToScreenPoint(new Vector3(1,1,0));\n            opt.worldToScreenRatio = Vector3.Distance(a,b) / new Vector3(1,1,0).magnitude;\n        }\n\n        VertexArrayHolder holder = null;\n        if (mode == 0) {\n            holder = new VertexArrayHolder();\n            holder.Push3(\n                new Vector2(-2, 0.5f),\n                new Vector2(-1, 0.5f),\n                new Vector2(-2, -0.5f),\n                Color.red,\n                Color.green,\n                Color.blue,\n                5, 0, 5\n            );\n            holder.Push3(\n                new Vector2(-2, -0.5f),\n                new Vector2(-1, -0.5f),\n                new Vector2(-2, -0.75f),\n                Color.red,\n                Color.green,\n                Color.blue,\n                1.25f, 0, 5\n            );\n            holder.Push3(\n                new Vector2(-0.6f, 0.5f),\n                new Vector2(-0.1f, 0.5f),\n                new Vector2(-0.6f, -0.5f),\n                Color.red,\n                Color.green,\n                Color.blue,\n                5, 0, 2.5f\n            );\n            holder.Push3(\n                new Vector2(-0.6f, -0.5f),\n                new Vector2(-0.1f, -0.5f),\n                new Vector2(-0.6f, -0.75f),\n                Color.red,\n                Color.green,\n                Color.blue,\n                1.25f, 0, 2.5f\n            );\n            holder.Push3(\n                new Vector2(0.25f, 0.5f),\n                new Vector2(2.25f, 0.5f),\n                new Vector2(0.25f, -0.5f),\n                Color.red,\n                Color.green,\n                Color.blue,\n                5, 0, 10\n            );\n            holder.Push3(\n                new Vector2(0.25f, -0.5f),\n                new Vector2(2f, -0.5f),\n                new Vector2(0.25f, -0.75f),\n                Color.red,\n                Color.green,\n                Color.blue,\n                1.25f, 0, 10\n            );\n        } else if (mode == 1) {\n            polyline = new Polyline(\n                new List<Vector2> {\n                    new Vector2(0, .75f),\n                    new Vector2(-.75f, -.1f),\n                    new Vector2(.75f, .1f),\n                    new Vector2(0, -.75f),\n                },\n                new List<Color> {\n                    Color.red,\n                    Color.green,\n                    Color.blue,\n                    Color.red,\n                },\n                new List<float> {\n                    0.05f, 0.15f, 0.15f, 0.05f,\n                },\n                opt\n            );\n        } else if (mode == 2) {\n            Polybezier polybezier = new Polybezier(\n                new List<Vector2> {\n                    new Vector2(0, .75f),\n                    new Vector2(-.75f, -.1f),\n                    new Vector2(.75f, .1f),\n                    new Vector2(0, -.75f),\n                },\n                new Vaser.Gradient (\n                    new List<Vaser.Gradient.Stop> {\n                        new Vaser.Gradient.Stop(0.0f,  Color.red),\n                        new Vaser.Gradient.Stop(0.0f,  0.01f),\n                        new Vaser.Gradient.Stop(0.25f, Color.green),\n                        new Vaser.Gradient.Stop(0.25f, 0.15f),\n                        new Vaser.Gradient.Stop(0.5f,  Color.blue),\n                        new Vaser.Gradient.Stop(0.5f,  0.15f),\n                        new Vaser.Gradient.Stop(1.0f,  Color.red),\n                        new Vaser.Gradient.Stop(1.0f,  0.01f),\n                    }\n                ),\n                new Polybezier.Opt {\n                    worldToScreenRatio = opt.worldToScreenRatio\n                }\n            );\n            polyline = polybezier.Render(opt);\n        }\n\n        myGameObject = new GameObject(\"Mesh\", typeof(MeshFilter), typeof(MeshRenderer));\n        myGameObject.transform.localScale = new Vector3(1,1,1);\n        myGameObject.GetComponent<MeshRenderer>().material = Resources.Load<Material>(\"Vaser/Fade\");\n\n        if (polyline != null) {\n            Mesh mesh = polyline.GetMesh();\n            myGameObject.GetComponent<MeshFilter>().mesh = mesh;\n        }\n\n        if (mode == 0) {\n            Mesh mesh = new Mesh();\n            mesh.SetVertices(holder.GetVertices());\n            mesh.SetUVs(0, holder.GetUVs());\n            mesh.SetColors(holder.GetColors());\n            mesh.SetTriangles(holder.GetTriangles(), 0);\n            myGameObject.GetComponent<MeshFilter>().mesh = mesh;\n        }\n    }\n\n    void Update()\n    {\n        float time = Time.fixedTime;\n        if (mode == 3) {\n            polyline = new Polyline();\n            polyline.holder.Push4(\n                new Vector2(-2.3f,  1),\n                new Vector2( 2.3f,  1),\n                new Vector2(-2.3f, -1),\n                new Vector2( 2.3f, -1),\n                new Color(0.365f, 0.808f, 0.910f, 1),\n                new Color(0.769f, 0.380f, 0.639f, 1),\n                new Color(0.365f, 0.808f, 0.910f, 1),\n                new Color(0.5f, 0.305f, 0.773f, 1),\n                0, 0, 0, 0\n            );\n            // pink new Color(0.769f, 0.380f, 0.639f, 1)\n            // cyan new Color(0.180f, 0.5f, 0.941f, 1)\n            for (int k=0; k<10; k++)\n            {\n                Polybezier.Buffer buffer = new Polybezier.Buffer();\n                buffer.AddPoints(ComputeSineCurve(time, 0.5f+0.035f*k));\n                buffer.Gradient(\n                    new Vaser.Gradient (\n                        new List<Vaser.Gradient.Stop> {\n                            new Vaser.Gradient.Stop(0.0f,  new Color(0.365f, 0.808f, 0.910f, 1)),\n                            new Vaser.Gradient.Stop(0.5f,  new Color(0.914f, 0.914f, 0.369f, 1)),\n                            new Vaser.Gradient.Stop(1.0f,  new Color(0.5f, 0.305f, 0.773f, 1)),\n                            new Vaser.Gradient.Stop(0.0f,  0.01f),\n                            new Vaser.Gradient.Stop(1.0f,  0.01f),\n                        }\n                    )\n                );\n                polyline.Append(buffer.Render(opt));\n            }\n            Mesh mesh = polyline.GetMesh();\n            myGameObject.GetComponent<MeshFilter>().mesh = mesh;\n        }\n    }\n\n    private List<Vector2> ComputeSineCurve(float time, float amplitude)\n    {\n        List<Vector2> curve = new List<Vector2> ();\n        for (float t=-1; t<1; t+=0.02f) {\n            float value = 0;\n            List<float> harmonics = new List<float> { 0.5f, 0.25f, 0.25f };\n            for (int j=0; j<harmonics.Count; j++) {\n                value += harmonics[j] * (float) System.Math.Sin(time - System.Math.PI * t * (j+1));\n            }\n            curve.Add(new Vector2(2.3f * t, -t + amplitude * value));\n        }\n        return curve;\n    }\n}"
  },
  {
    "path": "csharp/Assets/Demo/Demo.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 81156b434fc508ff78146c2e146a0bb4\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Demo/demo.unity",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!29 &1\nOcclusionCullingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_OcclusionBakeSettings:\n    smallestOccluder: 5\n    smallestHole: 0.25\n    backfaceThreshold: 100\n  m_SceneGUID: 00000000000000000000000000000000\n  m_OcclusionCullingData: {fileID: 0}\n--- !u!104 &2\nRenderSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 9\n  m_Fog: 0\n  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}\n  m_FogMode: 3\n  m_FogDensity: 0.01\n  m_LinearFogStart: 0\n  m_LinearFogEnd: 300\n  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}\n  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}\n  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}\n  m_AmbientIntensity: 1\n  m_AmbientMode: 0\n  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}\n  m_SkyboxMaterial: {fileID: 2100000, guid: 20c4b8489ffa46a4081a1c0319289aa1, type: 2}\n  m_HaloStrength: 0.5\n  m_FlareStrength: 1\n  m_FlareFadeSpeed: 3\n  m_HaloTexture: {fileID: 0}\n  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}\n  m_DefaultReflectionMode: 0\n  m_DefaultReflectionResolution: 128\n  m_ReflectionBounces: 1\n  m_ReflectionIntensity: 1\n  m_CustomReflection: {fileID: 0}\n  m_Sun: {fileID: 0}\n  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}\n  m_UseRadianceAmbientProbe: 0\n--- !u!157 &3\nLightmapSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 11\n  m_GIWorkflowMode: 0\n  m_GISettings:\n    serializedVersion: 2\n    m_BounceScale: 1\n    m_IndirectOutputScale: 1\n    m_AlbedoBoost: 1\n    m_EnvironmentLightingMode: 0\n    m_EnableBakedLightmaps: 1\n    m_EnableRealtimeLightmaps: 1\n  m_LightmapEditorSettings:\n    serializedVersion: 12\n    m_Resolution: 2\n    m_BakeResolution: 40\n    m_AtlasSize: 1024\n    m_AO: 0\n    m_AOMaxDistance: 1\n    m_CompAOExponent: 1\n    m_CompAOExponentDirect: 0\n    m_ExtractAmbientOcclusion: 0\n    m_Padding: 2\n    m_LightmapParameters: {fileID: 0}\n    m_LightmapsBakeMode: 1\n    m_TextureCompression: 1\n    m_FinalGather: 0\n    m_FinalGatherFiltering: 1\n    m_FinalGatherRayCount: 256\n    m_ReflectionCompression: 2\n    m_MixedBakeMode: 2\n    m_BakeBackend: 1\n    m_PVRSampling: 1\n    m_PVRDirectSampleCount: 32\n    m_PVRSampleCount: 500\n    m_PVRBounces: 2\n    m_PVREnvironmentSampleCount: 500\n    m_PVREnvironmentReferencePointCount: 2048\n    m_PVRFilteringMode: 2\n    m_PVRDenoiserTypeDirect: 0\n    m_PVRDenoiserTypeIndirect: 0\n    m_PVRDenoiserTypeAO: 0\n    m_PVRFilterTypeDirect: 0\n    m_PVRFilterTypeIndirect: 0\n    m_PVRFilterTypeAO: 0\n    m_PVREnvironmentMIS: 0\n    m_PVRCulling: 1\n    m_PVRFilteringGaussRadiusDirect: 1\n    m_PVRFilteringGaussRadiusIndirect: 5\n    m_PVRFilteringGaussRadiusAO: 2\n    m_PVRFilteringAtrousPositionSigmaDirect: 0.5\n    m_PVRFilteringAtrousPositionSigmaIndirect: 2\n    m_PVRFilteringAtrousPositionSigmaAO: 1\n    m_ExportTrainingData: 0\n    m_TrainingDataDestination: TrainingData\n  m_LightingDataAsset: {fileID: 0}\n  m_UseShadowmask: 1\n--- !u!196 &4\nNavMeshSettings:\n  serializedVersion: 2\n  m_ObjectHideFlags: 0\n  m_BuildSettings:\n    serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.4\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    debug:\n      m_Flags: 0\n  m_NavMeshData: {fileID: 0}\n--- !u!1 &362952764\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 362952766}\n  - component: {fileID: 362952765}\n  m_Layer: 0\n  m_Name: GameObject\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!114 &362952765\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 362952764}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 81156b434fc508ff78146c2e146a0bb4, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n--- !u!4 &362952766\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 362952764}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_RootOrder: 1\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!1 &1185959602\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1185959605}\n  - component: {fileID: 1185959604}\n  - component: {fileID: 1185959603}\n  m_Layer: 0\n  m_Name: Main Camera\n  m_TagString: MainCamera\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!81 &1185959603\nAudioListener:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1185959602}\n  m_Enabled: 1\n--- !u!20 &1185959604\nCamera:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1185959602}\n  m_Enabled: 1\n  serializedVersion: 2\n  m_ClearFlags: 1\n  m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0}\n  m_projectionMatrixMode: 1\n  m_GateFitMode: 2\n  m_FOVAxisMode: 0\n  m_SensorSize: {x: 36, y: 24}\n  m_LensShift: {x: 0, y: 0}\n  m_FocalLength: 50\n  m_NormalizedViewPortRect:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  near clip plane: 0.3\n  far clip plane: 1000\n  field of view: 60\n  orthographic: 1\n  orthographic size: 1\n  m_Depth: -1\n  m_CullingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n  m_RenderingPath: -1\n  m_TargetTexture: {fileID: 0}\n  m_TargetDisplay: 0\n  m_TargetEye: 3\n  m_HDR: 1\n  m_AllowMSAA: 1\n  m_AllowDynamicResolution: 0\n  m_ForceIntoRT: 0\n  m_OcclusionCulling: 1\n  m_StereoConvergence: 10\n  m_StereoSeparation: 0.022\n--- !u!4 &1185959605\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1185959602}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: -1}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_RootOrder: 0\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n"
  },
  {
    "path": "csharp/Assets/Demo/demo.unity.meta",
    "content": "fileFormatVersion: 2\nguid: b7a312166f95c074da8312847a33dbcd\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Demo.meta",
    "content": "fileFormatVersion: 2\nguid: 12e8321a818ec5ccb87d276a3bb47c22\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Resources/Vaser/Fade.mat",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!21 &2100000\nMaterial:\n  serializedVersion: 6\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_Name: Fade\n  m_Shader: {fileID: 4800000, guid: 98eb10bd2379fb34092e77f682e39b23, type: 3}\n  m_ShaderKeywords: \n  m_LightmapFlags: 4\n  m_EnableInstancingVariants: 0\n  m_DoubleSidedGI: 0\n  m_CustomRenderQueue: -1\n  stringTagMap: {}\n  disabledShaderPasses: []\n  m_SavedProperties:\n    serializedVersion: 3\n    m_TexEnvs:\n    - _BumpMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _DetailAlbedoMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _DetailMask:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _DetailNormalMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _EmissionMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _MainTex:\n        m_Texture: {fileID: 2800000, guid: 23dc4d4e95546b244854b9f4731755b7, type: 3}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _MetallicGlossMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _OcclusionMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _ParallaxMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    m_Floats:\n    - _BumpScale: 1\n    - _Cutoff: 0.5\n    - _DetailNormalMapScale: 1\n    - _DstBlend: 0\n    - _Feather: 1\n    - _GlossMapScale: 1\n    - _Glossiness: 0.5\n    - _GlossyReflections: 1\n    - _Metallic: 0\n    - _Mode: 0\n    - _OcclusionStrength: 1\n    - _Parallax: 0.02\n    - _SmoothnessTextureChannel: 0\n    - _SpecularHighlights: 1\n    - _SrcBlend: 1\n    - _UVSec: 0\n    - _ZWrite: 1\n    m_Colors:\n    - _Color: {r: 1, g: 1, b: 1, a: 1}\n    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}\n"
  },
  {
    "path": "csharp/Assets/Resources/Vaser/Fade.mat.meta",
    "content": "fileFormatVersion: 2\nguid: 38ae69e0a7563d741bb934ad6cf21933\nNativeFormatImporter:\n  externalObjects: {}\n  mainObjectFileID: 2100000\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Resources/Vaser/Fade.shader",
    "content": "﻿Shader \"Fade\" {\n\n    SubShader {\n        Tags { \"Queue\"=\"Transparent\" \"RenderType\"=\"Transparent\" \"IgnoreProjector\"=\"True\" }\n\n        Pass\n        {\n            ZWrite On\n            Blend SrcAlpha OneMinusSrcAlpha\n\n            CGPROGRAM\n            #pragma vertex vert\n            #pragma fragment frag\n\n            #include \"UnityCG.cginc\"\n\n            struct appdata {\n                float4 vertex : POSITION;\n                float4 uv : TEXCOORD0;\n                fixed4 color : COLOR;\n            };\n\n            struct v2f {\n                float4 uv : TEXCOORD0;\n                float4 vertex : SV_POSITION;\n                fixed4 color : COLOR;\n            };\n\n            v2f vert (appdata v) {\n                v2f o;\n                o.vertex = UnityObjectToClipPos(v.vertex);\n                o.uv = v.uv;\n                o.color = v.color;\n                return o;\n            }\n\n            fixed4 frag (v2f i) : SV_Target {\n                fixed4 col = i.color;\n                float factx = min((1 - abs(i.uv.x)) * i.uv.z, 1);\n                float facty = min((1 - abs(i.uv.y)) * i.uv.w, 1);\n                col.a *= min(factx, facty);\n                return col;\n            }\n            ENDCG\n        }\n    }\n}"
  },
  {
    "path": "csharp/Assets/Resources/Vaser/Fade.shader.meta",
    "content": "fileFormatVersion: 2\nguid: 98eb10bd2379fb34092e77f682e39b23\nShaderImporter:\n  externalObjects: {}\n  defaultTextures: []\n  nonModifiableTextures: []\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Resources/Vaser.meta",
    "content": "fileFormatVersion: 2\nguid: 12ce771d903fa393d85bff11f9ce9827\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Resources.meta",
    "content": "fileFormatVersion: 2\nguid: 16b03c580bf442779a7db0145e2aad4c\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Vaser/Gradient.cs",
    "content": "using UnityEngine;\nusing System.Collections.Generic;\n\nnamespace Vaser\n{\n    public class Gradient\n    {\n        public char unit = GD_ratio;\n        public List<Stop> stops; //array must be sorted in ascending order of t\n\n        public const char GD_ratio  = (char) 0;\n        public const char GD_length = (char) 1;\n\n        public const char GS_none   = (char) 0;\n        public const char GS_rgba   = (char) 1;\n        public const char GS_rgb    = (char) 2;\n        public const char GS_alpha  = (char) 3;\n        public const char GS_weight = (char) 4;\n\n        public Gradient()\n        {\n            stops = new List<Stop> ();\n        }\n\n        public Gradient(List<Stop> stopss)\n        {\n            stops = stopss;\n        }\n\n        public Gradient(Color cc, float ww)\n        {\n            stops = new List<Stop> ();\n            stops.Add(new Stop(0, cc));\n            stops.Add(new Stop(0, ww));\n        }\n\n        public struct Stop\n        {\n            public float t; //position\n            public char type;\n            public Color color;\n            public float weight;\n\n            public Stop(float tt, Color cc)\n            {\n                t = tt;\n                type = GS_rgba;\n                color = cc;\n                weight = 0;\n            }\n\n            public Stop(float tt, float ww)\n            {\n                t = tt;\n                type = GS_weight;\n                color = new Color(0,0,0,0);\n                weight = ww;\n            }\n        }\n\n        public void Apply(List<Color> C, List<float> W, List<float> L, int limit, float pathLength)\n        {\n            if (stops.Count == 0) {\n                return;\n            }\n\n            //current stops\n            int las_c = 0, las_a = 0, las_w = 0, //last\n                cur_c = 0, cur_a = 0, cur_w = 0, //current\n                nex_c = 0, nex_a = 0, nex_w = 0; //next\n\n            float lengthAlong = 0.0f;\n\n            if (stops.Count <= 1) {\n                return;\n            }\n            for (int i = 0; i < limit; i += 1) {\n                lengthAlong += L[i];\n                float p = 0.0f;\n                if (unit == GD_ratio) {\n                    p = lengthAlong / pathLength;\n                } else if (unit == GD_length) {\n                    p = lengthAlong;\n                } else {\n                    break;\n                }\n\n                //lookup for cur\n                for (nex_w = cur_w; nex_w < stops.Count; nex_w += 1) {\n                    if (stops[nex_w].type == GS_weight && p <= stops[nex_w].t) {\n                        cur_w = nex_w;\n                        break;\n                    }\n                }\n                for (nex_c = cur_c; nex_c < stops.Count; nex_c += 1) {\n                    if ((stops[nex_c].type == GS_rgba || stops[nex_c].type == GS_rgb) && p <= stops[nex_c].t) {\n                        cur_c = nex_c;\n                        break;\n                    }\n                }\n                for (nex_a = cur_a; nex_a < stops.Count; nex_a += 1) {\n                    if ((stops[nex_a].type == GS_rgba || stops[nex_a].type == GS_alpha) && p <= stops[nex_a].t) {\n                        cur_a = nex_a;\n                        break;\n                    }\n                }\n                //look for las\n                for (nex_w = cur_w; nex_w >= 0; nex_w -= 1) {\n                    if (stops[nex_w].type == GS_weight && p >= stops[nex_w].t) {\n                        las_w = nex_w;\n                        break;\n                    }\n                }\n                for (nex_c = cur_c; nex_c >= 0; nex_c -= 1) {\n                    if ((stops[nex_c].type == GS_rgba || stops[nex_c].type == GS_rgb) && p >= stops[nex_c].t) {\n                        las_c = nex_c;\n                        break;\n                    }\n                }\n                for (nex_a = cur_a; nex_a >= 0; nex_a -= 1) {\n                    if ((stops[nex_a].type == GS_rgba || stops[nex_a].type == GS_alpha) && p >= stops[nex_a].t) {\n                        las_a = nex_a;\n                        break;\n                    }\n                }\n\n                if (cur_c == las_c) {\n                    C[i] = Sc(cur_c);\n                } else {\n                    C[i] = ColorBetween(Sc(las_c), Sc(cur_c), (p - St(las_c)) / (St(cur_c) - St(las_c)));\n                }\n                if (cur_w == las_w) {\n                    W[i] = Sw(cur_w);\n                } else {\n                    W[i] = GetStep(Sw(las_w), Sw(cur_w), p - St(las_w), St(cur_w) - St(las_w));\n                }\n                if (cur_a == las_a) {\n                    C[i] = SaC(i, cur_a);\n                } else {\n                    C[i] = GetStepColor(i, Sa(las_a), Sa(cur_a), p - St(las_a), St(cur_a) - St(las_a));\n                }\n            }\n\n            Color Sc(int x) {\n                return stops[x].color;\n            }\n            float Sw(int x) {\n                return stops[x].weight;\n            }\n            float Sa(int x) {\n                return stops[x].color.a;\n            }\n            float St(int x) {\n                return stops[x].t;\n            }\n            Color SaC(int i, int x) {\n                Color c = new Color(C[i].r, C[i].g, C[i].b, C[i].a);\n                c.a = stops[x].color.a;\n                return c;\n            }\n            float GetStep(float A, float B, float t, float T) {\n                return ((T - t) * A + t * B) / T;\n            }\n            Color GetStepColor(int i, float A, float B, float t, float T) {\n                Color c = new Color(C[i].r, C[i].g, C[i].b, C[i].a);\n                c.a = GetStep(A, B, t, T);\n                return c;\n            }\n        }\n\n        public static Color ColorBetween(Color A, Color B, float t)\n        {\n            if (t < 0.0f) t = 0.0f;\n            if (t > 1.0f) t = 1.0f;\n\n            float kt = 1.0f - t;\n            return new Color(\n                A.r * kt + B.r * t,\n                A.g * kt + B.g * t,\n                A.b * kt + B.b * t,\n                A.a * kt + B.a * t\n            );\n        }\n    }\n}"
  },
  {
    "path": "csharp/Assets/Vaser/Gradient.cs.meta",
    "content": "fileFormatVersion: 2\nguid: cbc53ed91caad3e419e6ab5858f7e12b\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Vaser/Polybezier.cs",
    "content": "using UnityEngine;\nusing System.Collections.Generic;\n\nnamespace Vaser\n{\n    public class Polybezier\n    {\n        private Buffer buffer;\n\n        public class Opt\n        {\n            public float worldToScreenRatio = 1.0f;\n        }\n\n        public Polybezier(List<Vector2> P, Color cc, float ww, Opt opt)\n            : this(P, new Gradient(cc, ww), opt)\n        {\n            // empty\n        }\n\n        public Polybezier(List<Vector2> P, Gradient grad, Opt opt)\n        {\n            buffer = new Buffer();\n            if (opt == null) {\n                opt = new Opt();\n            }\n\n            double default_approximation_scale = (grad.stops.Count > 2 ? 2 : 1) * opt.worldToScreenRatio;\n            const double default_angle_tolerance = (15 / 180 * System.Math.PI);\n            const double default_cusp_limit = 5.0;\n\n            for (int i=0; i<P.Count-3; i+=3) {\n                Curve4Div(\n                    P[i+0].x,P[i+0].y,\n                    P[i+1].x,P[i+1].y,\n                    P[i+2].x,P[i+2].y,\n                    P[i+3].x,P[i+3].y,\n                    default_approximation_scale,\n                    default_angle_tolerance,\n                    default_cusp_limit,\n                    buffer);\n            }\n            if (grad != null) {\n                buffer.Gradient(grad);\n            }\n        }\n\n        public Polyline Render(Polyline.Opt opt)\n        {\n            return buffer.Render(opt);\n        }\n\n        public class Buffer\n        {\n            public List<Vector2> P;\n            public List<Color> C;\n            public List<float> W;\n            public List<float> L; //length along polyline\n            public float pathLength; //total segment length\n\n            public Buffer()\n            {\n                P = new List<Vector2>();\n                C = new List<Color>();\n                W = new List<float>();\n                L = new List<float>();\n                pathLength = 0.0f;\n                L.Add(0.0f);\n            }\n\n            public void Gradient(Gradient grad)\n            {\n                grad.Apply(C, W, L, C.Count, pathLength);\n            }\n\n            public Polyline Render(Polyline.Opt opt)\n            {\n                if (P.Count == 0) {\n                    return new Polyline();\n                }\n                Polyline.Inopt inopt = new Polyline.Inopt();\n                inopt.segmentLength = L;\n                return new Polyline(P, C, W, opt, inopt);\n            }\n\n            public void AddPoint(float x, float y)\n            {\n                AddPoint(new Vector2(x, y));\n            }\n\n            public void AddPoint(double x, double y)\n            {\n                AddPoint(new Vector2((float) x, (float) y));\n            }\n\n            public void AddPoint(Vector2 V)\n            {\n                AddVertex(V, new Color(0,0,0,1), 1);\n            }\n\n            public void AddPoints(List<Vector2> points)\n            {\n                for (int i=0; i<points.Count; i++)\n                {\n                    AddPoint(points[i]);\n                }\n            }\n\n            private bool AddVertex(Vector2 V, Color cc, float ww)\n            {\n                int N = P.Count;\n                if (N > 0 && P[N-1].x == V.x && P[N-1].y == V.y) {\n                    return false; //duplicate\n                } else {\n                    //point\n                    P.Add(V);\n                    if (N > 0) {\n                        float len = (V - P[N-1]).Length();\n                        pathLength += len;\n                        L.Add(len);\n                    }\n                    C.Add(cc);\n                    W.Add(ww);\n                    return true;\n                }\n            }\n        }\n\n        //-----------------------------------------------------------------------\n        // The Anti-Grain Geometry Project\n        // A high quality rendering engine for C++\n        // http://antigrain.com\n        // \n        // Anti-Grain Geometry has dual licensing model. The Modified BSD \n        // License was first added in version v2.4 just for convenience.\n        // It is a simple, permissive non-copyleft free software license, \n        // compatible with the GNU GPL. It's well proven and recognizable.\n        // See http://www.fsf.org/licensing/licenses/index_html#ModifiedBSD\n        // for details.\n        // \n        // Note that the Modified BSD license DOES NOT restrict your rights \n        // if you choose the Anti-Grain Geometry Public License.\n        // \n        // Anti-Grain Geometry Public License\n        // ====================================================\n        // \n        // Anti-Grain Geometry - Version 2.4 \n        // Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) \n        // \n        // Permission to copy, use, modify, sell and distribute this software \n        // is granted provided this copyright notice appears in all copies. \n        // This software is provided \"as is\" without express or implied\n        // warranty, and with no claim as to its suitability for any purpose.\n        // \n        // \n        // \n        // Modified BSD License\n        // ====================================================\n        // Anti-Grain Geometry - Version 2.4 \n        // Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) \n        // \n        // Redistribution and use in source and binary forms, with or without \n        // modification, are permitted provided that the following conditions \n        // are met:\n        // \n        //   1. Redistributions of source code must retain the above copyright \n        //      notice, this list of conditions and the following disclaimer. \n        // \n        //   2. Redistributions in binary form must reproduce the above copyright\n        //      notice, this list of conditions and the following disclaimer in \n        //      the documentation and/or other materials provided with the \n        //      distribution. \n        // \n        //   3. The name of the author may not be used to endorse or promote \n        //      products derived from this software without specific prior \n        //      written permission. \n        // \n        // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR \n        // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \n        // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE \n        // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, \n        // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \n        // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR \n        // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) \n        // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, \n        // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING \n        // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \n        // POSSIBILITY OF SUCH DAMAGE.\n        //\n        //-----------------------------------------------------------------------\n        // Contact: mcseem@antigrain.com\n        //          mcseemagg@yahoo.com\n        //          http://www.antigrain.com\n        //-----------------------------------------------------------------------\n\n        private void RecursiveBezier(\n            double x1, double y1, \n            double x2, double y2, \n            double x3, double y3, \n            double x4, double y4,\n            int level,\n            double m_angle_tolerance,\n            double m_cusp_limit,\n            double m_distance_tolerance_square,\n            Buffer obj)\n        {\n            const double M_PI = System.Math.PI;\n            //const double curve_distance_epsilon                  = 1e-30;\n            const double curve_collinearity_epsilon              = 1e-30;\n            const double curve_angle_tolerance_epsilon           = 0.01;\n            const int curve_recursion_limit = 32;\n\n            if (level > curve_recursion_limit) {\n                return;\n            }\n\n            // Calculate all the mid-points of the line segments\n            //----------------------\n            double x12   = (x1 + x2) / 2;\n            double y12   = (y1 + y2) / 2;\n            double x23   = (x2 + x3) / 2;\n            double y23   = (y2 + y3) / 2;\n            double x34   = (x3 + x4) / 2;\n            double y34   = (y3 + y4) / 2;\n            double x123  = (x12 + x23) / 2;\n            double y123  = (y12 + y23) / 2;\n            double x234  = (x23 + x34) / 2;\n            double y234  = (y23 + y34) / 2;\n            double x1234 = (x123 + x234) / 2;\n            double y1234 = (y123 + y234) / 2;\n\n            // Try to approximate the full cubic curve by a single straight line\n            //------------------\n            double dx = x4-x1;\n            double dy = y4-y1;\n\n            double d2 = System.Math.Abs(((x2 - x4) * dy - (y2 - y4) * dx));\n            double d3 = System.Math.Abs(((x3 - x4) * dy - (y3 - y4) * dx));\n            double da1, da2, k;\n\n            switch (((d2 > curve_collinearity_epsilon ? 1 : 0) << 1) + (d3 > curve_collinearity_epsilon ? 1 : 0))\n            {\n                case 0:\n                    // All collinear OR p1==p4\n                    //----------------------\n                    k = dx*dx + dy*dy;\n                    if (k == 0) {\n                        d2 = calc_sq_distance(x1, y1, x2, y2);\n                        d3 = calc_sq_distance(x4, y4, x3, y3);\n                    } else {\n                        k   = 1 / k;\n                        da1 = x2 - x1;\n                        da2 = y2 - y1;\n                        d2  = k * (da1*dx + da2*dy);\n                        da1 = x3 - x1;\n                        da2 = y3 - y1;\n                        d3  = k * (da1*dx + da2*dy);\n\n                        if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) {\n                            // Simple collinear case, 1---2---3---4\n                            // We can leave just two endpoints\n                            return;\n                        }\n                        if (d2 <= 0) {\n                            d2 = calc_sq_distance(x2, y2, x1, y1);\n                        } else if (d2 >= 1) {\n                            d2 = calc_sq_distance(x2, y2, x4, y4);\n                        } else {\n                            d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);\n                        }\n                        if (d3 <= 0) {\n                            d3 = calc_sq_distance(x3, y3, x1, y1);\n                        } else if(d3 >= 1) {\n                            d3 = calc_sq_distance(x3, y3, x4, y4);\n                        } else {\n                            d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);\n                        }\n                    }\n                    if (d2 > d3) {\n                        if (d2 < m_distance_tolerance_square) {\n                            obj.AddPoint(x2, y2);\n                            return;\n                        }\n                    } else {\n                        if (d3 < m_distance_tolerance_square) {\n                            obj.AddPoint(x3, y3);\n                            return;\n                        }\n                    }\n                break;\n\n                case 1:\n                    // p1,p2,p4 are collinear, p3 is significant\n                    //----------------------\n                    if (d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) {\n                        if (m_angle_tolerance < curve_angle_tolerance_epsilon) {\n                            obj.AddPoint(x23, y23);\n                            return;\n                        }\n\n                        // Angle Condition\n                        //----------------------\n                        da1 = System.Math.Abs(System.Math.Atan2(y4 - y3, x4 - x3) - System.Math.Atan2(y3 - y2, x3 - x2));\n                        if (da1 >= M_PI) da1 = 2*M_PI - da1;\n\n                        if (da1 < m_angle_tolerance) {\n                            obj.AddPoint(x2, y2);\n                            obj.AddPoint(x3, y3);\n                            return;\n                        }\n\n                        if (m_cusp_limit != 0.0) {\n                            if (da1 > m_cusp_limit) {\n                                obj.AddPoint(x3, y3);\n                                return;\n                            }\n                        }\n                    }\n                break;\n\n                case 2:\n                    // p1,p3,p4 are collinear, p2 is significant\n                    //----------------------\n                    if (d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) {\n                        if (m_angle_tolerance < curve_angle_tolerance_epsilon) {\n                            obj.AddPoint(x23, y23);\n                            return;\n                        }\n\n                        // Angle Condition\n                        //----------------------\n                        da1 = System.Math.Abs(System.Math.Atan2(y3 - y2, x3 - x2) - System.Math.Atan2(y2 - y1, x2 - x1));\n                        if (da1 >= M_PI) {\n                            da1 = 2*M_PI - da1;\n                        }\n                        if (da1 < m_angle_tolerance) {\n                            obj.AddPoint(x2, y2);\n                            obj.AddPoint(x3, y3);\n                            return;\n                        }\n                        if (m_cusp_limit != 0.0) {\n                            if (da1 > m_cusp_limit) {\n                                obj.AddPoint(x2, y2);\n                                return;\n                            }\n                        }\n                    }\n                break;\n\n                case 3: \n                    // Regular case\n                    //-----------------\n                    if ((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy)) {\n                        // If the curvature doesn't exceed the distance_tolerance value\n                        // we tend to finish subdivisions.\n                        //----------------------\n                        if (m_angle_tolerance < curve_angle_tolerance_epsilon) {\n                            obj.AddPoint(x23, y23);\n                            return;\n                        }\n\n                        // Angle & Cusp Condition\n                        //----------------------\n                        k   = System.Math.Atan2(y3 - y2, x3 - x2);\n                        da1 = System.Math.Abs(k - System.Math.Atan2(y2 - y1, x2 - x1));\n                        da2 = System.Math.Abs(System.Math.Atan2(y4 - y3, x4 - x3) - k);\n                        if (da1 >= M_PI) da1 = 2*M_PI - da1;\n                        if (da2 >= M_PI) da2 = 2*M_PI - da2;\n\n                        if (da1 + da2 < m_angle_tolerance) {\n                            // Finally we can stop the recursion\n                            //----------------------\n                            obj.AddPoint(x23, y23);\n                            return;\n                        }\n\n                        if (m_cusp_limit != 0.0) {\n                            if (da1 > m_cusp_limit) {\n                                obj.AddPoint(x2, y2);\n                                return;\n                            }\n                            if (da2 > m_cusp_limit) {\n                                obj.AddPoint(x3, y3);\n                                return;\n                            }\n                        }\n                    }\n                break;\n            }\n\n            // Continue subdivision\n            //----------------------\n            RecursiveBezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1,\n                m_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,\n                obj); \n            RecursiveBezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1,\n                m_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,\n                obj);\n\n            double calc_sq_distance(double xx1, double yy1, double xx2, double yy2)\n            {\n                double ddx = xx2-xx1;\n                double ddy = yy2-yy1;\n                return ddx * ddx + ddy * ddy;\n            }\n        }\n\n        private void Curve4Div(\n            double x1, double y1, \n            double x2, double y2, \n            double x3, double y3,\n            double x4, double y4,\n            double m_approximation_scale,\n            double m_angle_tolerance,\n            double m_cusp_limit,\n            Buffer obj)\n        {\n            double m_distance_tolerance_square = 0.5 / m_approximation_scale;\n            m_distance_tolerance_square *= m_distance_tolerance_square;\n            obj.AddPoint(x1, y1);\n            RecursiveBezier(\n                x1, y1, x2, y2, x3, y3, x4, y4, 0,\n                m_angle_tolerance, m_cusp_limit, m_distance_tolerance_square,\n                obj);\n            obj.AddPoint(x4, y4);\n        }\n        //                             end of AGG\n        //-----------------------------------------------------------------------\n    }\n}"
  },
  {
    "path": "csharp/Assets/Vaser/Polybezier.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5ac57cfb8ec56bd489a88a68ee553086\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Vaser/Polyline.cs",
    "content": "using UnityEngine;\nusing System.Collections.Generic;\n\nnamespace Vaser\n{\n    public class Polyline\n    {\n        public VertexArrayHolder holder;\n\n        public class Opt\n        {\n            public char joint = PLJmiter;\n            public char cap = PLCbutt;\n            public bool feather = false;\n            public float feathering = 0.0f;\n            public bool noFeatherAtCap = false;\n            public bool noFeatherAtCore = false;\n            public float worldToScreenRatio = 1.0f;\n            public bool triangulation = false;\n\n            //for Opt.joint\n            public const char PLJmiter = (char) 0; //default\n            public const char PLJbevel = (char) 1;\n            public const char PLJround = (char) 2;\n            //for Opt.cap\n            public const char PLCbutt   = (char) 0; //default\n            public const char PLCround  = (char) 1;\n            public const char PLCsquare = (char) 2;\n            public const char PLCrect   = (char) 3;\n            public const char PLCboth   = (char) 0; //default\n            public const char PLCfirst  = (char) 10;\n            public const char PLClast   = (char) 20;\n            public const char PLCnone   = (char) 30;\n        }\n\n        public class Inopt\n        {\n            public bool constColor = false;\n            public bool constWeight = false;\n            public bool noCapFirst = false;\n            public bool noCapLast = false;\n            public bool joinFirst = false;\n            public bool joinLast = false;\n            public List<float> segmentLength = null; // length of each segment; optional\n            public VertexArrayHolder holder = new VertexArrayHolder();\n\n            public Inopt ShallowCopy()\n            {\n                return (Inopt) this.MemberwiseClone();\n            }\n        }\n\n        public Polyline()\n        {\n            holder = new VertexArrayHolder();\n        }\n\n        public Polyline(\n            List<Vector2> P, List<Color> C, List<float> W, Opt opt)\n            : this(P, C, W, opt, null)\n        {\n            // empty\n        }\n\n        public Polyline(\n            List<Vector2> P, Color C, float W, Opt opt)\n            : this(P, new List<Color> { C }, new List<float> { W }, opt, new Inopt {\n                constColor = true, constWeight = true,\n            })\n        {\n            // empty\n        }\n\n        public Polyline(\n            List<Vector2> P, List<Color> C, List<float> W,\n            Opt opt, Inopt inopt)\n        {\n            int length = P.Count;\n            if (opt == null) {\n                opt = new Opt();\n            }\n            if (inopt == null) {\n                inopt = new Inopt();\n            }\n\n            if (opt.cap >= 10) {\n                char dec = (char)((int) opt.cap - ((int) opt.cap % 10));\n                if (dec == Opt.PLCfirst || dec == Opt.PLCnone) {\n                    inopt.noCapLast = true;\n                }\n                if (dec == Opt.PLClast || dec == Opt.PLCnone) {\n                    inopt.noCapFirst = true;\n                }\n                opt.cap -= dec;\n            }\n\n            int A = 0, B = 0;\n            bool on = false;\n            for (int i = 1; i < length - 1; i++) {\n                Vector2 V1 = P[i] - P[i - 1];\n                Vector2 V2 = P[i + 1] - P[i];\n                float len = 0;\n                if (inopt.segmentLength != null) {\n                    V1 *= 1 / inopt.segmentLength[i];\n                    V2 *= 1 / inopt.segmentLength[i + 1];\n                    len += (inopt.segmentLength[i] + inopt.segmentLength[i + 1]) * 0.5f;\n                } else {\n                    len += Vec2Ext.Normalize(ref V1) * 0.5f;\n                    len += Vec2Ext.Normalize(ref V2) * 0.5f;\n                }\n                float costho = V1.x * V2.x + V1.y * V2.y;\n                const float m_pi = (float) System.Math.PI;\n                //float angle = acos(costho)*180/m_pi;\n                float cos_a = (float) System.Math.Cos(15 * m_pi / 180);\n                float cos_b = (float) System.Math.Cos(10 * m_pi / 180);\n                float cos_c = (float) System.Math.Cos(25 * m_pi / 180);\n                float weight = W[inopt.constWeight ? 0 : i];\n                bool approx = false;\n                if ((weight * opt.worldToScreenRatio < 7 && costho > cos_a) || (costho > cos_b) || //when the angle difference at an anchor is smaller than a critical degree, do polyline approximation\n                    (len < weight && costho > cos_c)) { //when vector length is smaller than weight, do approximation\n                    approx = true;\n                }\n                if (approx && !on) {\n                    A = i;\n                    on = true;\n                    if (A == 1) {\n                        A = 0;\n                    }\n                    if (A > 1) {\n                        PolylineRange(P, C, W, opt, inopt, B, A, false);\n                    }\n                } else if (!approx && on) {\n                    B = i;\n                    on = false;\n                    PolylineRange(P, C, W, opt, inopt, A, B, true);\n                }\n            }\n            if (on && B < length - 1) {\n                B = length - 1;\n                PolylineRange(P, C, W, opt, inopt, A, B, true);\n            } else if (!on && A < length - 1) {\n                A = length - 1;\n                PolylineRange(P, C, W, opt, inopt, B, A, false);\n            }\n            holder = inopt.holder;\n            inopt.holder = null;\n        }\n\n        public Mesh GetMesh()\n        {\n            Mesh mesh = new Mesh();\n            mesh.SetVertices(holder.GetVertices());\n            mesh.SetUVs(0, holder.GetUVs());\n            mesh.SetColors(holder.GetColors());\n            mesh.SetTriangles(holder.GetTriangles(), 0);\n            return mesh;\n        }\n\n        public void Append(Polyline polyline)\n        {\n            holder.Push(polyline.holder);\n        }\n\n        private struct StPolyline\n        // to hold info for AnchorLate() to perform triangulation\n        {\n            //for all joints\n            public Vector2 vP; //vector to intersection point; outward\n\n            //for djoint==PLJbevel\n            public Vector2 T; //core thickness of a line\n            public Vector2 R; //fading edge of a line\n            public Vector2 bR; //out stepping vector, same direction as cap\n            public Vector2 T1; //alternate vectors, same direction as T21\n            // all T,R,T1 are outward\n\n            //for djoint==PLJround\n            public float t,r;\n\n            //for degeneration case\n            public bool degenT; //core degenerated\n            public bool degenR; //fade degenerated\n            public bool preFull; //draw the preceding segment in full\n            public Vector2 PT;\n\n            public char djoint; //determined joint\n            // e.g. originally a joint is PLJmiter. but it is smaller than critical angle, should then set djoint to PLJbevel\n        }\n\n        private class StAnchor\n        // to hold memory for the working of anchor()\n        {\n            public Vector2[] P = new Vector2[3]; //point\n            public Color[] C = new Color[3]; //color\n            public float[] W = new float[3]; //weight\n\n            public StPolyline[] SL = new StPolyline[3];\n            public VertexArrayHolder vah = new VertexArrayHolder();\n        }\n\n        private static void PolyPointInter(\n            List<Vector2> P, List<Color> C, List<float> W,\n            Inopt inopt, ref Vector2 p, ref Color c, ref float w, int at, float t)\n        {\n            Color color(int I) {\n                return C[inopt != null && inopt.constColor ? 0 : I];\n            }\n            float weight(int I) {\n                return W[inopt != null && inopt.constWeight ? 0 : I];\n            }\n\n            if (t == 0.0) {\n                p = P[at];\n                c = color(at);\n                w = weight(at);\n            } else if (t == 1.0) {\n                p = P[at + 1];\n                c = color(at + 1);\n                w = weight(at + 1);\n            } else {\n                p = (P[at] + P[at + 1]) * t;\n                c = Gradient.ColorBetween(color(at), color(at + 1), t);\n                w = (weight(at) + weight(at + 1)) * t;\n            }\n        }\n\n        private static void PolylineApprox(\n            List<Vector2> P, List<Color> C, List<float> W,\n            Opt opt, Inopt inopt, int from, int to)\n        {\n            if (to - from + 1 < 2) {\n                return;\n            }\n            bool capFirst = !(inopt != null && inopt.noCapFirst);\n            bool capLast = !(inopt != null && inopt.noCapLast);\n            bool joinFirst = inopt != null && inopt.joinFirst;\n            bool joinLast = inopt != null && inopt.joinLast;\n\n            VertexArrayHolder vcore = new VertexArrayHolder(); //curve core\n            vcore.SetGlDrawMode(VertexArrayHolder.GL_TRIANGLE_STRIP);\n\n            Color color(int I) {\n                return C[inopt != null && inopt.constColor ? 0: I];\n            }\n            float weight(int I) {\n                return W[inopt != null && inopt.constWeight ? 0: I];\n            }\n            void poly_step(int i, Vector2 pp, float ww, Color cc) {\n                float t = 0, r = 0;\n                DetermineTr(ww, ref t, ref r, opt.worldToScreenRatio);\n                if (opt.feather && !opt.noFeatherAtCore) {\n                    r *= opt.feathering;\n                }\n                float rr = (t + r) / r;\n                Vector2 V = P[i] - P[i - 1];\n                Vec2Ext.Perpen(ref V);\n                Vec2Ext.Normalize(ref V);\n                V *= (t + r);\n                vcore.Push(pp - V, cc, rr);\n                vcore.Push(pp + V, cc, 0);\n            }\n\n            for (int i = from + 1; i < to; i++) {\n                poly_step(i, P[i], weight(i), color(i));\n            }\n            Vector2 P_las = new Vector2(), P_fir = new Vector2();\n            Color C_las = new Color(), C_fir = new Color();\n            float W_las = 0, W_fir = 0;\n            PolyPointInter(P, C, W, inopt, ref P_las, ref C_las, ref W_las, to - 1, 0.5f);\n            poly_step(to, P_las, W_las, C_las);\n\n            StAnchor SA = new StAnchor();\n            {\n                PolyPointInter(P, C, W, inopt, ref P_fir, ref C_fir, ref W_fir, from, joinFirst ? 0.5f : 0.0f);\n                SA.P[0] = P_fir;\n                SA.P[1] = P[from + 1];\n                SA.C[0] = C_fir;\n                SA.C[1] = color(from + 1);\n                SA.W[0] = W_fir;\n                SA.W[1] = weight(from + 1);\n                Segment(SA, opt, capFirst, false, true);\n            }\n            if (!joinLast) {\n                SA.P[0] = P_las;\n                SA.P[1] = P[to];\n                SA.C[0] = C_las;\n                SA.C[1] = color(to);\n                SA.W[0] = W_las;\n                SA.W[1] = weight(to);\n                Segment(SA, opt, false, capLast, true);\n            }\n\n            inopt.holder.Push(vcore);\n            inopt.holder.Push(SA.vah);\n\n            if (opt.triangulation) {\n                DrawTriangles(vcore, inopt.holder, opt.worldToScreenRatio);\n                DrawTriangles(SA.vah, inopt.holder, opt.worldToScreenRatio);\n            }\n        }\n\n        private static void PolylineExact(\n            List<Vector2> P, List<Color> C, List<float> W,\n            Opt opt, Inopt inopt, int from, int to)\n        {\n            bool capFirst = !(inopt != null && inopt.noCapFirst);\n            bool capLast = !(inopt != null && inopt.noCapLast);\n            bool joinFirst = inopt != null && inopt.joinFirst;\n            bool joinLast = inopt != null && inopt.joinLast;\n\n            Color color(int I) {\n                return C[inopt != null && inopt.constColor ? 0: I];\n            }\n            float weight(int I) {\n                return W[inopt != null && inopt.constWeight ? 0: I];\n            }\n\n            Vector2 mid_l = new Vector2(), mid_n = new Vector2(); //the last and the next mid point\n            Color c_l = new Color(), c_n = new Color();\n            float w_l = 0, w_n = 0;\n\n            //init for the first anchor\n            PolyPointInter(P, C, W, inopt, ref mid_l, ref c_l, ref w_l, from, joinFirst ? 0.5f : 0);\n\n            StAnchor SA = new StAnchor();\n            if (to - from + 1 == 2) {\n                SA.P[0] = P[from];\n                SA.P[1] = P[from + 1];\n                SA.C[0] = color(from);\n                SA.C[1] = color(from + 1);\n                SA.W[0] = weight(from);\n                SA.W[1] = weight(from + 1);\n                Segment(SA, opt, capFirst, capLast, true);\n            } else {\n                for (int i = from + 1; i < to; i++) {\n                    if (i == to - 1 && !joinLast) {\n                        PolyPointInter(P, C, W, inopt, ref mid_n, ref c_n, ref w_n, i, 1.0f);\n                    } else {\n                        PolyPointInter(P, C, W, inopt, ref mid_n, ref c_n, ref w_n, i, 0.5f);\n                    }\n\n                    SA.P[0] = mid_l;\n                    SA.C[0] = c_l;\n                    SA.W[0] = w_l;\n                    SA.P[2] = mid_n;\n                    SA.C[2] = c_n;\n                    SA.W[2] = w_n;\n\n                    SA.P[1] = P[i];\n                    SA.C[1] = color(i);\n                    SA.W[1] = weight(i);\n\n                    Anchor(SA, opt, (i == 1) && capFirst, (i == to - 1) && capLast);\n\n                    mid_l = mid_n;\n                    c_l = c_n;\n                    w_l = w_n;\n                }\n            }\n\n            inopt.holder.Push(SA.vah);\n            if (opt.triangulation) {\n                DrawTriangles(SA.vah, inopt.holder, opt.worldToScreenRatio);\n            }\n        }\n\n        private static void PolylineRange(\n            List<Vector2> P, List<Color> C, List<float> W,\n            Opt opt, Inopt ininopt, int from, int to, bool approx)\n        {\n            Inopt inopt;\n            if (ininopt == null) {\n                inopt = new Inopt();\n            } else {\n                inopt = ininopt.ShallowCopy();\n            }\n            if (from > 0) {\n                from -= 1;\n            }\n\n            inopt.joinFirst = from != 0;\n            inopt.joinLast = to != (P.Count - 1);\n            inopt.noCapFirst = ininopt.noCapFirst || inopt.joinFirst;\n            inopt.noCapLast = ininopt.noCapLast || inopt.joinLast;\n\n            if (approx) {\n                PolylineApprox(P, C, W, opt, inopt, from, to);\n            } else {\n                PolylineExact(P, C, W, opt, inopt, from, to);\n            }\n        }\n\n        private static void DrawTriangles(VertexArrayHolder triangles, VertexArrayHolder holder, float scale)\n        {\n            Opt opt = new Opt();\n            opt.cap = Opt.PLCnone;\n            opt.joint = Opt.PLJbevel;\n            opt.worldToScreenRatio = scale;\n            if (triangles.glmode == VertexArrayHolder.GL_TRIANGLES) {\n                for (int i = 0; i < triangles.GetCount(); i++) {\n                    List<Vector2> P = new List<Vector2> ();\n                    P.Add(triangles.Get(i));\n                    i += 1;\n                    P.Add(triangles.Get(i));\n                    i += 1;\n                    P.Add(triangles.Get(i));\n                    P.Add(P[0]);\n                    Polyline polyline = new Polyline(P, Color.red, 1 / scale, opt);\n                    holder.Push(polyline.holder);\n                }\n            } else if (triangles.glmode == VertexArrayHolder.GL_TRIANGLE_STRIP) {\n                for (int i = 2; i < triangles.GetCount(); i++) {\n                    List<Vector2> P = new List<Vector2> ();\n                    P.Add(triangles.Get(i - 2));\n                    P.Add(triangles.Get(i));\n                    P.Add(triangles.Get(i - 1));\n                    Polyline polyline = new Polyline(P, Color.red, 1 / scale, opt);\n                    holder.Push(polyline.holder);\n                }\n            }\n        }\n\n        private static void DrawDigit(VertexArrayHolder holder, Vector2 pp, float scale, int digit)\n        {\n            Opt opt = new Opt();\n            opt.cap = Opt.PLCnone;\n            opt.joint = Opt.PLJbevel;\n            opt.worldToScreenRatio = scale;\n            List<Vector2> P;\n            float size = 5 / scale;\n            float halfSize = size / 2;\n            switch (digit)\n            {\n                case 0:\n                    P = new List<Vector2> {\n                        new Vector2(halfSize, size) + pp,\n                        new Vector2(halfSize, -size) + pp,\n                        new Vector2(-halfSize, -size) + pp,\n                        new Vector2(-halfSize, size) + pp,\n                        new Vector2(halfSize, size) + pp,\n                        new Vector2(-halfSize, -size) + pp,\n                    };\n                break;\n                case 1:\n                    P = new List<Vector2> {\n                        new Vector2(-halfSize, 0) + pp,\n                        new Vector2(0, size) + pp,\n                        new Vector2(0, -size) + pp,\n                    };\n                break;\n                case 2:\n                    P = new List<Vector2> {\n                        new Vector2(-halfSize, size) + pp,\n                        new Vector2(halfSize, size) + pp,\n                        new Vector2(halfSize, 0) + pp,\n                        new Vector2(-halfSize, 0) + pp,\n                        new Vector2(-halfSize, -size) + pp,\n                        new Vector2(halfSize, -size) + pp,\n                    };\n                break;\n                case 3:\n                    P = new List<Vector2> {\n                        new Vector2(-halfSize, size) + pp,\n                        new Vector2(halfSize, size) + pp,\n                        new Vector2(halfSize, 0) + pp,\n                        new Vector2(-halfSize, 0) + pp,\n                        new Vector2(halfSize, 0) + pp,\n                        new Vector2(halfSize, -size) + pp,\n                        new Vector2(-halfSize, -size) + pp,\n                    };\n                break;\n                case 4:\n                    P = new List<Vector2> {\n                        new Vector2(halfSize, -size) + pp,\n                        new Vector2(halfSize, size) + pp,\n                        new Vector2(-halfSize, 0) + pp,\n                        new Vector2(halfSize, 0) + pp,\n                    };\n                break;\n                default: return;\n            }\n            Polyline polyline = new Polyline(P, Color.red, 1 / scale, opt);\n            holder.Push(polyline.holder);\n        }\n\n        private static void DetermineTr(float w, ref float t, ref float R, float scale)\n        {\n            //efficiency: can cache one set of w,t,R values\n            // i.e. when a polyline is of uniform thickness, the same w is passed in repeatedly\n            w *= scale;\n            float f = w - (float) System.Math.Floor(w);\n            // resolution dependent\n            if (w >= 0.0 && w < 1.0) {\n                t = 0.05f;\n                R = 0.768f;\n            } else if (w >= 1.0 && w < 2.0) {\n                t = 0.05f + f * 0.33f;\n                R = 0.768f + 0.312f * f;\n            } else if (w >= 2.0 && w < 3.0) {\n                t = 0.38f + f * 0.58f;\n                R = 1.08f;\n            } else if (w >= 3.0 && w < 4.0) {\n                t = 0.96f + f * 0.48f;\n                R = 1.08f;\n            } else if (w >= 4.0 && w < 5.0) {\n                t = 1.44f + f * 0.46f;\n                R = 1.08f;\n            } else if (w >= 5.0 && w < 6.0) {\n                t = 1.9f + f * 0.6f;\n                R = 1.08f;\n            } else if (w >= 6.0) {\n                t = 2.5f + (w - 6.0f) * 0.50f;\n                R = 1.08f;\n            }\n            t /= scale;\n            R /= scale;\n        }\n\n        private static void MakeTrc(\n            ref Vector2 P1, ref Vector2 P2, ref Vector2 T, ref Vector2 R, ref Vector2 C,\n            float w, Opt opt, ref float rr, ref float tt, ref float dist, bool anchorMode)\n        {\n            float t = 1.0f, r = 0.0f;\n\n            //calculate t,r\n            DetermineTr(w, ref t, ref r, opt.worldToScreenRatio);\n            if (opt.feather && !opt.noFeatherAtCore) {\n                r *= opt.feathering;\n            }\n            if (anchorMode) {\n                t += r;\n            }\n\n            //output\n            tt = t;\n            rr = r;\n            Vector2 DP = P2 - P1;\n            dist = Vec2Ext.Normalize(ref DP);\n            C = DP * (1 / opt.worldToScreenRatio);\n            Vec2Ext.Perpen(ref DP);\n            T = DP * t;\n            R = DP * r;\n        }\n\n        private static void Segment(StAnchor SA, Opt opt, bool capFirst, bool capLast, bool core)\n        {\n            float[] weight = SA.W;\n\n            Vector2[] P = new Vector2[2];\n            P[0] = SA.P[0];\n            P[1] = SA.P[1];\n            Color[] C = new Color[3];\n            C[0] = SA.C[0];\n            C[1] = SA.C[1];\n\n            Vector2 T2 = new Vector2();\n            Vector2 R2 = new Vector2();\n            Vector2 bR = new Vector2();\n            float t = 0, r = 0;\n\n            bool varying_weight = weight[0] != weight[1];\n\n            Vector2 capStart = new Vector2(), capEnd = new Vector2();\n            StPolyline[] SL = new StPolyline[2];\n\n            /*for (int i = 0; i < 2; i++) {\n                //lower the transparency for weight < 1.0\n                float actualWeight = weight[i] * opt.worldToScreenRatio;\n                if (actualWeight >= 0.0 && actualWeight < 1.0) {\n                    C[i].a *= actualWeight;\n                }\n            }*/\n\n            {\n                int i = 0;\n                float dd = 0f;\n                MakeTrc(ref P[i], ref P[i + 1], ref T2, ref R2, ref bR, weight[i], opt, ref r, ref t, ref dd, false);\n\n                if (capFirst) {\n                    if (opt.cap == Opt.PLCsquare) {\n                        P[0] -= bR * (t + r);\n                    }\n                    capStart = bR;\n                    Vec2Ext.Opposite(ref capStart);\n                    if (opt.feather && !opt.noFeatherAtCap) {\n                        capStart *= opt.feathering;\n                    }\n                }\n\n                SL[i].djoint = opt.cap;\n                SL[i].t = t;\n                SL[i].r = r;\n                SL[i].T = T2;\n                SL[i].R = R2;\n                SL[i].bR = bR;\n                SL[i].degenT = false;\n                SL[i].degenR = false;\n            }\n\n            {\n                int i = 1;\n                float dd = 0f;\n                if (varying_weight) {\n                    MakeTrc(ref P[i - 1], ref P[i], ref T2, ref R2, ref bR, weight[i], opt, ref r, ref t, ref dd, false);\n                }\n\n                if (capLast) {\n                    if (opt.cap == Opt.PLCsquare) {\n                        P[1] += bR * (t + r);\n                    }\n                    capEnd = bR;\n                    if (opt.feather && !opt.noFeatherAtCap) {\n                        capEnd *= opt.feathering;\n                    }\n                }\n\n                SL[i].djoint = opt.cap;\n                SL[i].t = t;\n                SL[i].r = r;\n                SL[i].T = T2;\n                SL[i].R = R2;\n                SL[i].bR = bR;\n                SL[i].degenT = false;\n                SL[i].degenR = false;\n            }\n\n            SegmentLate(opt, P, C, SL, SA.vah, capStart, capEnd, core);\n        }\n\n        private static void SegmentLate(\n            Opt opt, Vector2[] P, Color[] C, StPolyline[] SL,\n            VertexArrayHolder tris, Vector2 cap1, Vector2 cap2, bool core)\n        {\n            tris.SetGlDrawMode(VertexArrayHolder.GL_TRIANGLES);\n\n            Vector2 P_0, P_1;\n            P_0 = P[0];\n            P_1 = P[1];\n\n            Vector2 P1, P2, P3, P4; //core\n            Vector2 P1c, P2c, P3c, P4c; //cap\n            Vector2 P1r, P2r, P3r, P4r; //fade\n\n            float s0 = 1, s1 = 1;\n            if (SL[0].t < SL[1].t) {\n                s0 = (SL[0].t + SL[0].r) / (SL[1].t + SL[1].r);\n            }\n            if (SL[1].t < SL[0].t) {\n                s1 = (SL[1].t + SL[1].r) / (SL[0].t + SL[0].r);\n            }\n            P1 = P_0 + SL[0].T + SL[0].R;\n            P1r = P1 - SL[0].R * s0;\n            P1c = P1 + cap1 * s0;\n            P2 = P_0 - SL[0].T - SL[0].R;\n            P2r = P2 + SL[0].R * s0;\n            P2c = P2 + cap1 * s0;\n            P3 = P_1 + SL[1].T + SL[1].R;\n            P3r = P3 - SL[1].R * s1;\n            P3c = P3 + cap2 * s1;\n            P4 = P_1 - SL[1].T - SL[1].R;\n            P4r = P4 + SL[1].R * s1;\n            P4c = P4 + cap2 * s1;\n            float rr = System.Math.Max((SL[0].t + SL[0].r) / SL[0].r, (SL[1].t + SL[1].r) / SL[1].r);\n            float rc = (P_1 - P_0).Length() / System.Math.Max(SL[0].t + SL[0].r, SL[1].t + SL[1].r);\n\n            //core\n            if (core) {\n                float rc0 = 0, rc1 = 0;\n                if (SL[0].djoint == Opt.PLCbutt && !cap1.IsZero()) {\n                    rc0 = rc;\n                }\n                if (SL[1].djoint == Opt.PLCbutt && !cap2.IsZero()) {\n                    rc1 = rc;\n                }\n                tris.Push3(P1, P3, P2, C[0], C[1], C[0], rr,  0, rc0);\n                tris.Push3(P2, P3, P4, C[0], C[1], C[1], 0, rc1,  rr);\n            }\n\n            //caps\n            for (int j = 0; j < 2; j++) {\n                VertexArrayHolder cap = new VertexArrayHolder();\n                cap.SetGlDrawMode(VertexArrayHolder.GL_TRIANGLE_STRIP);\n                Vector2 cur_cap = j == 0 ? cap1 : cap2;\n                if (cur_cap.IsZero()) {\n                    continue;\n                }\n\n                if (SL[j].djoint == Opt.PLCround) {\n                    //round cap\n                    Vector2 O = P[j];\n                    float dangle = GetPljRoundDangle(SL[j].t, SL[j].r, opt.worldToScreenRatio);\n\n                    VectorsToArc(\n                        cap, O, C[j], C[j], SL[j].T + SL[j].R, -SL[j].T - SL[j].R,\n                        dangle, SL[j].t + SL[j].r, 0.0f, false, O, j == 0 ? cap1 : cap2, rr, false);\n                    //cap.Push(O-SL[j].T-SL[j].R, C[j]);\n                    //cap.Push(O, C[j]);\n                    tris.Push(cap);\n                } else if (SL[j].djoint == Opt.PLCrect || SL[j].djoint == Opt.PLCsquare) {\n                    //rectangular cap\n                    Vector2 Pj, Pjr, Pjc, Pk, Pkr, Pkc;\n                    if (j == 0) {\n                        Pj = P1;\n                        Pjr = P1r;\n                        Pjc = P1c;\n\n                        Pk = P2;\n                        Pkr = P2r;\n                        Pkc = P2c;\n                    }\n                    else {\n                        Pj = P4;\n                        Pjr = P4r;\n                        Pjc = P4c;\n\n                        Pk = P3;\n                        Pkr = P3r;\n                        Pkc = P3c;\n                    }\n\n                    tris.PushF(Pk, C[j]);\n                    tris.PushF(Pkc, C[j]);\n                    tris.Push(Pkr, C[j]);\n                    tris.Push(Pkr, C[j]);\n                    tris.PushF(Pkc, C[j]);\n                    tris.Push(Pjr, C[j]);\n                    tris.Push(Pjr, C[j]);\n                    tris.PushF(Pkc, C[j]);\n                    tris.PushF(Pjc, C[j]);\n                    tris.Push(Pjr, C[j]);\n                    tris.PushF(Pjc, C[j]);\n                    tris.PushF(Pj, C[j]);\n                }\n            }\n        }\n\n        private static void Anchor(StAnchor SA, Opt opt, bool capFirst, bool capLast)\n        {\n            Vector2[] P = SA.P;\n            Color[] C = SA.C;\n            float[] weight = SA.W;\n            StPolyline[] SL = SA.SL;\n            if (Vec2Ext.SignedArea(P[0], P[1], P[2]) > 0) {\n                // rectify clockwise\n                Vector2 P0 = P[0];\n                P[0] = P[2];\n                P[2] = P0;\n                Color C0 = C[0];\n                C[0] = C[2];\n                C[2] = C0;\n                float weight0 = weight[0];\n                weight[0] = weight[2];\n                weight[2] = weight0;\n                bool capCap = capFirst;\n                capFirst = capLast;\n                capLast = capCap;\n            }\n\n            //const float critical_angle=11.6538;\n            //  critical angle in degrees where a miter is force into bevel\n            //  it is _similar_ to cairo_set_miter_limit () but cairo works with ratio while VASEr works with included angle\n            const float cos_cri_angle = 0.979386f; //cos(critical_angle)\n\n            bool varying_weight = !(weight[0] == weight[1] & weight[1] == weight[2]);\n\n            Vector2 T1 = new Vector2(), T2 = new Vector2(), T21 = new Vector2(), T31 = new Vector2(), RR = new Vector2();\n\n            /*for (int i = 0; i < 3; i++) {\n                //lower the transparency for weight < 1.0\n                float actualWeight = weight[i] * opt.worldToScreenRatio;\n                if (actualWeight >= 0.0 && actualWeight < 1.0) {\n                    C[i].a *= actualWeight;\n                }\n            }*/\n\n            {\n                int i = 0;\n\n                Vector2 cap0 = new Vector2(), cap1 = new Vector2();\n                float r = 0f, t = 0f, d = 0f;\n                MakeTrc(ref P[i], ref P[i + 1], ref T2, ref RR, ref cap1, weight[i], opt, ref r, ref t, ref d, true);\n                if (varying_weight) {\n                    MakeTrc(ref P[i], ref P[i + 1], ref T31, ref RR, ref cap0, weight[i + 1], opt, ref d, ref d, ref d, true);\n                } else {\n                    T31 = T2;\n                }\n                SL[i].djoint = opt.cap;\n                SL[i].T = T2;\n                SL[i].t = t;\n                SL[i].r = r;\n                SL[i].degenT = false;\n\n                SL[i + 1].T1 = T31;\n            }\n\n            {\n                int i = 1;\n\n                float r = 0f, t = 0f;\n                Vector2 P_cur = P[i]; //current point\n                Vector2 P_nxt = P[i + 1]; //next point\n                Vector2 P_las = P[i - 1]; //last point\n\n                {\n                    Vector2 cap0 = new Vector2(), bR = new Vector2();\n                    float length_cur = 0f, length_nxt = 0f, d = 0f;\n                    MakeTrc(ref P_las, ref P_cur, ref T1, ref RR, ref cap0, weight[i - 1], opt, ref d, ref d, ref length_cur, true);\n                    if (varying_weight) {\n                        MakeTrc(ref P_las, ref P_cur, ref T21, ref RR, ref cap0, weight[i], opt, ref d, ref d, ref d, true);\n                    } else {\n                        T21 = T1;\n                    }\n\n                    MakeTrc(ref P_cur, ref P_nxt, ref T2, ref RR, ref bR, weight[i], opt, ref r, ref t, ref length_nxt, true);\n                    if (varying_weight) {\n                        MakeTrc(ref P_cur, ref P_nxt, ref T31, ref RR, ref cap0, weight[i + 1], opt, ref d, ref d, ref d, true);\n                    } else {\n                        T31 = T2;\n                    }\n\n                    SL[i].T = T2;\n                    SL[i].bR = bR;\n                    SL[i].t = t;\n                    SL[i].r = r;\n                    SL[i].degenT = false;\n\n                    SL[i + 1].T1 = T31;\n                }\n\n                {   //2nd point\n                    //find the angle between the 2 line segments\n                    Vector2 ln1 = new Vector2(), ln2 = new Vector2(), V = new Vector2();\n                    ln1 = P_cur - P_las;\n                    ln2 = P_nxt - P_cur;\n                    Vec2Ext.Normalize(ref ln1);\n                    Vec2Ext.Normalize(ref ln2);\n                    Vec2Ext.Dot(ln1, ln2, ref V);\n                    float cos_tho = V.x + V.y;\n                    bool zero_degree = System.Math.Abs(cos_tho - 1.0f) < 0.0000001f;\n                    bool d180_degree = cos_tho < -1.0f + 0.0001f;\n                    bool intersection_fail = false;\n                    int result3 = 1;\n\n                    SL[i].djoint = opt.joint;\n                    if (SL[i].djoint == Opt.PLJmiter) {\n                        if (System.Math.Abs(cos_tho) >= cos_cri_angle) {\n                            SL[i].djoint = Opt.PLJbevel;\n                        }\n                    }\n\n                    Vec2Ext.AnchorOutward(ref T1, P_cur, P_nxt);\n                    Vec2Ext.AnchorOutward(ref T21, P_cur, P_nxt);\n                    Vec2Ext.FollowSigns(ref SL[i].T1, T21);\n                    Vec2Ext.AnchorOutward(ref T2, P_cur, P_las);\n                    Vec2Ext.FollowSigns(ref SL[i].T, T2);\n                    Vec2Ext.AnchorOutward(ref T31, P_cur, P_las);\n\n                    {   //must do intersection\n                        Vector2 interP = new Vector2(), vP = new Vector2();\n                        float[] pts = new float[2];\n                        result3 = Vec2Ext.Intersect(\n                            P_las + T1, P_cur + T21, P_nxt + T31, P_cur + T2, ref interP, pts);\n\n                        if (result3 != 0) {\n                            vP = interP - P_cur;\n                            SL[i].vP = vP;\n                            if (SL[i].djoint == Opt.PLJmiter) {\n                                if (pts[0] > 2 || pts[1] > 2 || vP.Length() > 2 * SL[i].t) {\n                                    SL[i].djoint = Opt.PLJbevel;\n                                }\n                            }\n                        } else {\n                            intersection_fail = true;\n                            //Debug.Log(System.String.Format(\"intersection failed: cos(angle)={0}, angle={1} (degree)\", cos_tho, System.Math.Acos(cos_tho) * 180 / 3.14159));\n                        }\n                    }\n\n                    if (zero_degree || intersection_fail) {\n                        Segment(SA, opt, capFirst, false, true);\n                        SA.P[0] = SA.P[1];\n                        SA.P[1] = SA.P[2];\n                        SA.C[0] = SA.C[1];\n                        SA.C[1] = SA.C[2];\n                        SA.W[0] = SA.W[1];\n                        SA.W[1] = SA.W[2];\n                        Segment(SA, opt, false, capLast, true);\n                        return;\n                    }\n\n                    Vec2Ext.Opposite(ref T1);\n                    Vec2Ext.Opposite(ref T21);\n                    Vec2Ext.Opposite(ref T2);\n                    Vec2Ext.Opposite(ref T31);\n\n                    //make intersections\n                    Vector2 PT1 = new Vector2(), PT2 = new Vector2();\n                    int result1t, result2t;\n                    {\n                        result1t = Vec2Ext.Intersect(\n                            P_nxt - T31, P_nxt + T31, P_las + T1, P_cur + T21, //knife1_a\n                            ref PT1); //core\n                        result2t = Vec2Ext.Intersect(\n                            P_las - T1, P_las + T1, P_nxt + T31, P_cur + T2, //knife2_a\n                            ref PT2);\n                    }\n                    bool is_result1t = result1t == 1;\n                    bool is_result2t = result2t == 1;\n\n                    if (is_result1t | is_result2t) { //core degeneration\n                        SL[i].degenT = true;\n                        SL[i].preFull = is_result1t;\n                        SL[i].PT = is_result1t ? PT1: PT2;\n                    } else {\n                        int result4;\n                        result4 = Vec2Ext.Intersect(\n                            P_nxt - T31, P_nxt + T31, P_las, P_cur,\n                            ref PT1);\n                        if (result4 == 1) {\n                            SL[i].degenT = true;\n                            SL[i].preFull = true;\n                            SL[i].PT = PT1;\n                        } else {\n                            result4 = Vec2Ext.Intersect(\n                                P_las - T1, P_las + T1, P_cur, P_nxt,\n                                ref PT2);\n                            if (result4 == 1) {\n                                SL[i].degenT = true;\n                                SL[i].preFull = false;\n                                SL[i].PT = PT2;\n                            }\n                        }\n                    }\n\n                    if (d180_degree | result3 == 0) { //to solve visual bugs 3 and 1.1\n                        SL[i].vP = SL[i].T;\n                        Vec2Ext.FollowSigns(ref SL[i].T1, SL[i].T);\n                        SL[i].djoint = Opt.PLJmiter;\n                    }\n                }\n            }\n\n            {\n                int i = 2;\n\n                Vector2 cap0 = new Vector2();\n                float r = 0f, t = 0f, d = 0f;\n                MakeTrc(ref P[i - 1], ref P[i], ref T2, ref RR, ref cap0, weight[i], opt, ref r, ref t, ref d, true);\n\n                SL[i].djoint = opt.cap;\n                SL[i].T = T2;\n                SL[i].t = t;\n                SL[i].r = r;\n                SL[i].degenT = false;\n            }\n\n            AnchorLate(opt, P, C, SA.SL, SA.vah, capFirst, capLast);\n            if (capFirst && SL[0].djoint != Opt.PLCbutt && SL[0].djoint != Opt.PLCnone) {\n                Segment(SA, opt, true, false, false);\n            }\n            if (capLast && SL[1].djoint != Opt.PLCbutt && SL[1].djoint != Opt.PLCnone) {\n                SA.P[0] = SA.P[1];\n                SA.P[1] = SA.P[2];\n                SA.C[0] = SA.C[1];\n                SA.C[1] = SA.C[2];\n                SA.W[0] = SA.W[1];\n                SA.W[1] = SA.W[2];\n                Segment(SA, opt, false, true, false);\n            }\n        } //Anchor\n\n        private static void AnchorLate(\n            Opt opt, Vector2[] P, Color[] C, StPolyline[] SL,\n            VertexArrayHolder tris, bool capFirst, bool capLast)\n        {\n            Vector2 P_0 = P[0], P_1 = P[1], P_2 = P[2];\n            Vector2 P0, P1, P2, P3, P4, P5, P6, P7;\n\n            P0 = P_1 + SL[1].vP;\n            P1 = P_1 - SL[1].vP;\n\n            P2 = P_1 + SL[1].T1;\n            P3 = P_0 + SL[0].T;\n            P4 = P_0 - SL[0].T;\n\n            P5 = P_1 + SL[1].T;\n            P6 = P_2 + SL[2].T;\n            P7 = P_2 - SL[2].T;\n\n            int normal_line_core_joint = 1; //0:dont draw, 1:draw, 2:outer only\n            float rr = SL[1].t / SL[1].r;\n            float rc1 = 0, rc2 = 0;\n            if (capFirst && SL[0].djoint == Opt.PLCbutt) {\n                rc1 = (P_1 - P_0).Length() / (SL[0].r) / 2;\n            }\n            if (capLast && SL[2].djoint == Opt.PLCbutt) {\n                rc2 = (P_2 - P_1).Length() / (SL[2].r) / 2;\n            }\n\n            if (SL[1].degenT) {\n                P1 = SL[1].PT;\n                tris.Push3(P1, P3, P2, C[1], C[0], C[1], 0, rr, 0); //fir seg\n                tris.Push3(P1, P5, P6, C[1], C[1], C[2], 0, rr, 0); //las seg\n\n                if (SL[1].preFull) {\n                    tris.Push3(P3, P1, P4, C[0], C[1], C[0], 0, rr, 0);\n                } else {\n                    tris.Push3(P1, P6, P7, C[1], C[2], C[2], 0, 0, rr);\n                }\n            } else {\n                // normal first segment\n                tris.Push3(P2, P4, P3, C[1], C[0], C[0], 0, rc1, rr);\n                tris.Push3(P4, P2, P1, C[0], C[1], C[1], 0, 0, rr);\n                // normal last segment\n                tris.Push3(P5, P7, P1, C[1], C[2], C[1], 0, rr, 0);\n                tris.Push3(P7, P5, P6, C[2], C[1], C[2], 0, rr, rc2);\n            }\n\n            if (normal_line_core_joint != 0) {\n                switch (SL[1].djoint) {\n                case Opt.PLJmiter:\n                    tris.Push3(P2, P0, P1, C[1], C[1], C[1], rr, 0, 0);\n                    tris.Push3(P1, P0, P5, C[1], C[1], C[1], 0, rr, 0);\n                    break;\n\n                case Opt.PLJbevel:\n                    if (normal_line_core_joint == 1) tris.Push3(P2, P5, P1, C[1], C[1], C[1], rr, 0, 0);\n                    break;\n\n                case Opt.PLJround:\n                    {\n                        VertexArrayHolder strip = new VertexArrayHolder();\n                        strip.SetGlDrawMode(VertexArrayHolder.GL_TRIANGLE_STRIP);\n\n                        if (normal_line_core_joint == 1) {\n                            VectorsToArc(strip, P_1, C[1], C[1], SL[1].T1, SL[1].T, GetPljRoundDangle(SL[1].t, SL[1].r, opt.worldToScreenRatio), SL[1].t, 0.0f, false, P1, new Vector2(), rr, true);\n                        } else if (normal_line_core_joint == 2) {\n                            VectorsToArc(strip, P_1, C[1], C[1], SL[1].T1, SL[1].T, GetPljRoundDangle(SL[1].t, SL[1].r, opt.worldToScreenRatio), SL[1].t, 0.0f, false, P5, new Vector2(), rr, true);\n                        }\n                        tris.Push(strip);\n                    }\n                    break;\n                }\n            }\n        } //AnchorLate\n\n        private static void VectorsToArc(\n            VertexArrayHolder hold, Vector2 P, Color C, Color C2,\n            Vector2 PA, Vector2 PB, float dangle, float r, float r2,\n            bool ignorEnds, Vector2 apparentP, Vector2 hint, float rr, bool innerFade)\n        {\n            // triangulate an inner arc between vectors A and B,\n            // A and B are position vectors relative to P\n            const float m_pi = (float) System.Math.PI;\n            Vector2 A = PA * (1 / r);\n            Vector2 B = PB * (1 / r);\n            float rrr;\n            if (innerFade) {\n                rrr = 0;\n            } else {\n                rrr = -1;\n            }\n\n            float angle1 = (float) System.Math.Acos(A.x);\n            float angle2 = (float) System.Math.Acos(B.x);\n            if (A.y > 0) {\n                angle1 = 2 * m_pi - angle1;\n            }\n            if (B.y > 0) {\n                angle2 = 2 * m_pi - angle2;\n            }\n            if (!hint.IsZero()) {\n                // special case when angle1 == angle2,\n                //   have to determine which side by hint\n                if (hint.x > 0 && hint.y == 0) {\n                    angle1 -= (angle1 < angle2 ? 1 : -1) * 0.00001f;\n                } else if (hint.x == 0 && hint.y > 0) {\n                    angle1 -= (angle1 < angle2 ? 1 : -1) * 0.00001f;\n                } else if (hint.x > 0 && hint.y > 0) {\n                    angle1 -= (angle1 < angle2 ? 1 : -1) * 0.00001f;\n                } else if (hint.x > 0 && hint.y < 0) {\n                    angle1 -= (angle1 < angle2 ? 1 : -1) * 0.00001f;\n                } else if (hint.x < 0 && hint.y > 0) {\n                    angle1 += (angle1 < angle2 ? 1 : -1) * 0.00001f;\n                } else if (hint.x < 0 && hint.y < 0) {\n                    angle1 += (angle1 < angle2 ? 1 : -1) * 0.00001f;\n                }\n            }\n\n            // (apparent center) center of fan\n            //draw the inner arc between angle1 and angle2 with dangle at each step.\n            // -the arc has thickness, r is the outer radius and r2 is the inner radius,\n            //    with color C and C2 respectively.\n            //    in case when inner radius r2=0.0f, it gives a pie.\n            // -when ignorEnds=false, the two edges of the arc lie exactly on angle1\n            //    and angle2. when ignorEnds=true, the two edges of the arc do not touch\n            //    angle1 or angle2.\n            // -P is the mathematical center of the arc.\n            // -when use_apparent_P is true, r2 is ignored,\n            //    apparentP is then the apparent origin of the pie.\n            // -the result is pushed to hold, in form of a triangle strip\n            // -an inner arc is an arc which is always shorter than or equal to a half circumference\n\n            if (angle2 > angle1) {\n                if (angle2 - angle1 > m_pi) {\n                    angle2 = angle2 - 2 * m_pi;\n                }\n            }\n            else {\n                if (angle1 - angle2 > m_pi) {\n                    angle1 = angle1 - 2 * m_pi;\n                }\n            }\n\n            bool incremental = true;\n            if (angle1 > angle2) {\n                incremental = false;\n            }\n\n            if (incremental) {\n                if (!ignorEnds) {\n                    hold.Push(new Vector2(P.x + PB.x, P.y + PB.y), C, rr);\n                    hold.Push(apparentP, C2, rrr);\n                }\n                for (float a = angle2 - dangle; a > angle1; a -= dangle) {\n                    inner_arc_push(System.Math.Cos(a), System.Math.Sin(a));\n                }\n                if (!ignorEnds) {\n                    hold.Push(new Vector2(P.x + PA.x, P.y + PA.y), C, rr);\n                    hold.Push(apparentP, C2, rrr);\n                }\n            }\n            else //decremental\n            {\n                if (!ignorEnds) {\n                    hold.Push(apparentP, C2, rr);\n                    hold.Push(new Vector2(P.x + PB.x, P.y + PB.y), C, rrr);\n                }\n                for (float a = angle2 + dangle; a < angle1; a += dangle) {\n                    inner_arc_push(System.Math.Cos(a), System.Math.Sin(a), true);\n                }\n                if (!ignorEnds) {\n                    hold.Push(apparentP, C2, rr);\n                    hold.Push(new Vector2(P.x + PA.x, P.y + PA.y), C, rrr);\n                }\n            }\n\n            void inner_arc_push(double x, double y, bool reverse = false) {\n                Vector2 PP = new Vector2(P.x + (float) x * r, P.y - (float) y * r);\n                //hold.Dot(PP, 0.05f); return;\n                if (!reverse) {\n                    hold.Push(PP, C, rr);\n                    hold.Push(apparentP, C2, rrr);\n                } else {\n                    hold.Push(apparentP, C2, rr);\n                    hold.Push(PP, C, rrr);\n                }\n            }\n        }\n\n        private static float GetPljRoundDangle(float t, float r, float scale)\n        {\n            float dangle;\n            float sum = (t + r) * scale;\n            if (sum <= 1.44f + 1.08f) { //w<=4.0, feathering=1.0\n                dangle = 0.6f / sum;\n            } else if (sum <= 3.25f + 1.08f) { //w<=6.5, feathering=1.0\n                dangle = 2.8f / sum;\n            } else {\n                dangle = 4.2f / sum;\n            }\n            return dangle;\n        }\n    }\n}"
  },
  {
    "path": "csharp/Assets/Vaser/Polyline.cs.meta",
    "content": "fileFormatVersion: 2\nguid: f3267f6b485b83291a8297963816eb95\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Vaser/Vec2Ext.cs",
    "content": "using UnityEngine;\n\nnamespace Vaser\n{\n    public static class Vec2Ext\n    {\n        public static float Length(this Vector2 a)\n        {\n            return (float) System.Math.Sqrt(a.x*a.x+a.y*a.y);\n        }\n        public static float SignedArea(Vector2 P1, Vector2 P2, Vector2 P3)\n        {\n            return (P2.x-P1.x)*(P3.y-P1.y) - (P3.x-P1.x)*(P2.y-P1.y);\n        }\n        public static void Dot(Vector2 a, Vector2 b, ref Vector2 o) //dot product: o = a dot b\n        {\n            o.x = a.x * b.x;\n            o.y = a.y * b.y;\n        }\n        public static void Opposite(ref Vector2 a)\n        {\n            a.x = -a.x;\n            a.y = -a.y;\n        }\n        public static float Normalize(ref Vector2 a)\n        {\n            float L = a.Length();\n            if (L > 0.0000001f)\n            {\n                a.x /= L;\n                a.y /= L;\n            }\n            return L;\n        }\n        public static void Perpen(ref Vector2 a) //perpendicular: anti-clockwise 90 degrees\n        {\n            float y_value=a.y;\n            a.y=a.x;\n            a.x=-y_value;\n        }\n        public static void FollowSigns(ref Vector2 a, Vector2 b)\n        {\n            if ((a.x>0) != (b.x>0)) a.x = -a.x;\n            if ((a.y>0) != (b.y>0)) a.y = -a.y;\n        }\n\n        //judgements\n        public static bool Negligible(float M)\n        {\n            const float vaser_min_alw = 0.000000001f;\n            return -vaser_min_alw < M && M < vaser_min_alw;\n        }\n        public static bool Negligible(double M)\n        {\n            const double vaser_min_alw = 0.0000000001;\n            return -vaser_min_alw < M && M < vaser_min_alw;\n        }\n        public static bool Negligible(this Vector2 a)\n        {\n            return Negligible(a.x) && Negligible(a.y);\n        }\n        public static bool IsZero(this Vector2 a)\n        {\n            return a.x==0.0 && a.y==0.0;\n        }\n        public static bool Intersecting(Vector2 A, Vector2 B, Vector2 C, Vector2 D)\n        {   //return true if AB intersects CD\n            return SignedArea(A,B,C)>0 != SignedArea(A,B,D)>0;\n        }\n        public static bool OppositeQuadrant(Vector2 P1, Vector2 P2)\n        {\n            int P1x = P1.x>0? 1:(P1.x<0?-1:0);\n            int P1y = P1.y>0? 1:(P1.y<0?-1:0);\n            int P2x = P2.x>0? 1:(P2.x<0?-1:0);\n            int P2y = P2.y>0? 1:(P2.y<0?-1:0);\n\n            if (P1x != P2x) {\n                if (P1y != P2y)\n                    return true;\n                if (P1y == 0 || P2y == 0) {\n                    return true;\n                }\n            }\n            if (P1y != P2y) {\n                if (P1x == 0 || P2x == 0) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        public static bool AnchorOutward(ref Vector2 V, Vector2 b, Vector2 c, bool reverse=false)\n        {   //put the correct outward vector at V, with V placed on b, comparing distances from c\n            bool determinant = (b.x*V.x - c.x*V.x + b.y*V.y - c.y*V.y) > 0;\n            if (determinant == (!reverse)) {\n                //when reverse==true, it means inward\n                //positive V is the outward vector\n                return false;\n            } else {\n                //negative V is the outward vector\n                V.x=-V.x;\n                V.y=-V.y;\n                return true; //return whether V is changed\n            }\n        }\n\n        public static int Intersect(\n                Vector2 P1, Vector2 P2, //line 1\n                Vector2 P3, Vector2 P4, //line 2\n                ref Vector2 Pout,     //the output point\n                float[] u_out = null)\n        {   //Determine the intersection point of two line segments\n            float mua, mub;\n            double denom, numera, numerb;\n\n            denom  = (P4.y-P3.y) * (P2.x-P1.x) - (P4.x-P3.x) * (P2.y-P1.y);\n            numera = (P4.x-P3.x) * (P1.y-P3.y) - (P4.y-P3.y) * (P1.x-P3.x);\n            numerb = (P2.x-P1.x) * (P1.y-P3.y) - (P2.y-P1.y) * (P1.x-P3.x);\n\n            if (Negligible(numera) &&\n                Negligible(numerb) &&\n                Negligible(denom)) {\n                Pout.x = (P1.x + P2.x) * 0.5f;\n                Pout.y = (P1.y + P2.y) * 0.5f;\n                return 2; //meaning the lines coincide\n            }\n\n            if (Negligible(denom)) {\n                Pout.x = 0;\n                Pout.y = 0;\n                return 0; //meaning lines are parallel\n            }\n\n            mua = (float) (numera / denom);\n            mub = (float) (numerb / denom);\n            if (u_out != null) {\n                u_out[0] = mua;\n                u_out[1] = mub;\n            }\n\n            Pout.x = P1.x + mua * (P2.x - P1.x);\n            Pout.y = P1.y + mua * (P2.y - P1.y);\n\n            bool out1 = mua < 0 || mua > 1;\n            bool out2 = mub < 0 || mub > 1;\n\n            if (out1 & out2) {\n                return 5; //the intersection lies outside both segments\n            } else if (out1) {\n                return 3; //the intersection lies outside segment 1\n            } else if (out2) {\n                return 4; //the intersection lies outside segment 2\n            } else {\n                return 1; //great\n            }\n            //http://paulbourke.net/geometry/lineline2d/\n        }\n\n        public static int Octant(this Vector2 a)\n        {\n            if (a.x == 0 && a.y == 0) {\n                return 0;\n            }\n            if (a.x == 0) {\n                return a.y > 0 ? 3 : 7;\n            }\n            if (a.y == 0) {\n                return a.x > 0 ? 1 : 5;\n            }\n            if (a.x > 0 && a.y > 0) {\n                if (a.y/a.x < 0.5f) {\n                    return 1;\n                } else if (a.y/a.x > 2f) {\n                    return 3;\n                } else {\n                    return 2;\n                }\n            } else if (a.x < 0 && a.y > 0) {\n                if (a.y/a.x < -2f) {\n                    return 3;\n                } else if (a.y/a.x > -0.5f) {\n                    return 5;\n                } else {\n                    return 4;\n                }\n            } else if (a.x < 0 && a.y < 0) {\n                if (a.y/a.x < 0.5f) {\n                    return 5;\n                } else if (a.y/a.x > 2f) {\n                    return 7;\n                } else {\n                    return 6;\n                }\n            } else if (a.x > 0 && a.y < 0) {\n                if (a.y/a.x < -2f) {\n                    return 7;\n                } else if (a.y/a.x > -0.5f) {\n                    return 1;\n                } else {\n                    return 8;\n                }\n            }\n            return 0;\n        }\n    }\n}"
  },
  {
    "path": "csharp/Assets/Vaser/Vec2Ext.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1169d245ab3670b4685e0ceeafcdde52\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Vaser/VertexArrayHolder.cs",
    "content": "using UnityEngine;\nusing System.Collections.Generic;\n\nnamespace Vaser\n{\n    public class VertexArrayHolder\n    {\n        public const int GL_POINTS = 1;\n        public const int GL_LINES = 2;\n        public const int GL_TRIANGLES = 3;\n        public const int GL_LINE_STRIP = 4;\n        public const int GL_LINE_LOOP = 5;\n        public const int GL_TRIANGLE_STRIP = 6;\n        public const int GL_TRIANGLE_FAN = 7;\n\n        public int glmode = GL_TRIANGLES; //drawing mode in opengl\n        private bool jumping = false;\n        private List<Vector3> vert = new List<Vector3>();\n        private List<Color> color = new List<Color>();\n        private List<float> fade = new List<float>();\n\n        public int GetCount()\n        {\n            return vert.Count;\n        }\n\n        public void SetGlDrawMode(int gl_draw_mode)\n        {\n            glmode = gl_draw_mode;\n        }\n\n        public int Push(Vector2 P, Color cc, float fade0=0)\n        {\n            int cur = vert.Count;\n            vert.Add(P); // A Vector2 can be implicitly converted into a Vector3. (The z is set to zero in the result).\n            color.Add(cc);\n            fade.Add(fade0);\n\n            if (jumping)\n            {\n                jumping = false;\n                RepeatLastPush();\n            }\n            return cur;\n        }\n\n        public int PushF(Vector2 P, Color C)\n        {\n            C.a = 0;\n            return Push(P, C);\n        }\n\n        public void Push3(\n                Vector2 P1, Vector2 P2, Vector2 P3,\n                Color C1, Color C2, Color C3,\n                float fade1=0, float fade2=0, float fade3=0)\n        {\n            Push(P1, C1, fade1);\n            Push(P2, C2, fade2);\n            Push(P3, C3, fade3);\n        }\n\n        public void Push4(\n            Vector2 P1, Vector2 P2, Vector2 P3, Vector2 P4, \n            Color C1, Color C2, Color C3, Color C4,\n            float r1, float r2, float r3, float r4)\n        {\n            //interpret P0 to P3 as triangle strip\n            Push3(P1, P2, P3, C1, C2, C3, r1, r2, r3);\n            Push3(P3, P2, P4, C3, C2, C4, r3, r2, r4);\n        }\n\n        public void Dot(Vector2 P, float size)\n        {\n            size /= 2;\n            if (glmode == GL_TRIANGLES) {\n            } else if (glmode == GL_TRIANGLE_STRIP) {\n                Push(new Vector2(P.x-size, P.y), Color.red);\n                Push(new Vector2(P.x, P.y+size), Color.red);\n                Push(new Vector2(P.x, P.y-size), Color.red);\n                Push(new Vector2(P.x+size, P.y), Color.red);\n                Jump();\n            }\n        }\n\n        public void Push(VertexArrayHolder hold)\n        {\n            if (glmode == hold.glmode)\n            {\n                vert.AddRange(hold.vert);\n                color.AddRange(hold.color);\n                fade.AddRange(hold.fade);\n            }\n            else if (glmode == GL_TRIANGLES &&\n                hold.glmode == GL_TRIANGLE_STRIP)\n            {\n                for (int b=3; b < hold.vert.Count; b+=2)\n                {\n                    vert.Add(hold.vert[b-3]); color.Add(hold.color[b-3]); fade.Add(hold.fade[b-0]);\n                    vert.Add(hold.vert[b-2]); color.Add(hold.color[b-2]); fade.Add(hold.fade[b-0]);\n                    vert.Add(hold.vert[b-1]); color.Add(hold.color[b-1]); fade.Add(hold.fade[b-1]);\n                    vert.Add(hold.vert[b-1]); color.Add(hold.color[b-1]); fade.Add(hold.fade[b-0]);\n                    vert.Add(hold.vert[b-2]); color.Add(hold.color[b-2]); fade.Add(hold.fade[b-1]);\n                    vert.Add(hold.vert[b-0]); color.Add(hold.color[b-0]); fade.Add(hold.fade[b-0]);\n                }\n            }\n        }\n\n        public Vector2 Get(int i)\n        {\n            Vector2 P = new Vector2();\n            P.x = vert[i].x;\n            P.y = vert[i].y;\n            return P;\n        }\n\n        public Color GetColor(int b)\n        {\n            return color[b];\n        }\n\n        public Vector2 GetRelativeEnd(int di = -1)\n        {\n            //di=-1 is the last one\n            int count = vert.Count;\n            int i = count+di;\n            if (i<0) i=0;\n            if (i>=count) i=count-1;\n            return Get(i);\n        }\n\n        public void RepeatLastPush()\n        {\n            int i = vert.Count-1;\n            Push(Get(i), GetColor(i));\n        }\n\n        public void Jump() //to make a jump in triangle strip by degenerated triangles\n        {\n            if (glmode == GL_TRIANGLE_STRIP)\n            {\n                RepeatLastPush();\n                jumping = true;\n            }\n        }\n\n        public List<Vector3> GetVertices()\n        {\n            return vert;\n        }\n\n        public List<int> GetTriangles()\n        {\n            List<int> indices = new List<int>();\n            if (glmode == GL_TRIANGLES)\n            {\n                for (int i=0; i < vert.Count; i++)\n                {\n                    indices.Add(i);\n                }\n            }\n            return indices;\n        }\n\n        public List<Vector4> GetUVs()\n        {\n            List<Vector4> uvs = new List<Vector4>();\n            Vector4[] UVS = new Vector4[]\n            {\n                new Vector4(0, 0, 0, 0),\n                new Vector4(1, 0, 0, 0),\n                new Vector4(1, 1, 0, 0),\n                new Vector4(0, 1, 0, 0),\n                new Vector4(-1, 1, 0, 0),\n                new Vector4(-1, 0, 0, 0),\n                new Vector4(-1, -1, 0, 0),\n                new Vector4(0, -1, 0, 0),\n                new Vector4(1, -1, 0, 0),\n                new Vector4(-1, 0, 0, 0),\n            };\n\n            if (glmode == GL_TRIANGLES)\n            {\n                for (int i=2; i < fade.Count; i+=3)\n                {\n                    int count = 0;\n                    if (fade[i-2] > 0) count++;\n                    if (fade[i-1] > 0) count++;\n                    if (fade[i-0] > 0) count++;\n                    if (count == 0) {\n                        addUV(0, 1, 1);\n                        addUV(0, 1, 1);\n                        addUV(0, 1, 1);\n                    } else if (count == 3) {\n                        addUV(1, fade[i-2], fade[i-2]);\n                        addUV(5, fade[i-1], fade[i-1]);\n                        addUV(3, fade[i-0], fade[i-0]);\n                    } else if (count == 2) {\n                        float fadeX = 0, fadeY = 0;\n                        for (int j=0; j<3; j++)\n                        {\n                            float fadeU = 0, fadeV = 0;\n                            if (j == 0) {\n                                fadeU = fade[i-2];\n                                fadeV = fade[i-0];\n                            } else if (j == 1) {\n                                fadeU = fade[i-1];\n                                fadeV = fade[i-2];\n                            } else if (j == 2) {\n                                fadeU = fade[i-0];\n                                fadeV = fade[i-1];\n                            }\n                            if (fadeU > 0 && fadeV > 0) {\n                                fadeX = fadeV;\n                                fadeY = fadeU;\n                            }\n                        }\n                        for (int j=0; j<3; j++)\n                        {\n                            float fadeU = 0, fadeV = 0;\n                            if (j == 0) {\n                                fadeU = fade[i-2];\n                                fadeV = fade[i-0];\n                            } else if (j == 1) {\n                                fadeU = fade[i-1];\n                                fadeV = fade[i-2];\n                            } else if (j == 2) {\n                                fadeU = fade[i-0];\n                                fadeV = fade[i-1];\n                            }\n                            if (fadeU > 0 && fadeV > 0) {\n                                //Debug.Log(\"fade both\");\n                                addUV(8, fadeX, fadeY);\n                            } else if (fadeU > 0) {\n                                //Debug.Log(\"fadeU\");\n                                addUV(2, fadeX, fadeY);\n                            } else if (fadeV > 0) {\n                                //Debug.Log(\"fadeV\");\n                                addUV(6, fadeX, fadeY);\n                            }\n                        }\n                    } else if (count == 1 &&\n                        fade[i-2] >= 0 && fade[i-1] >= 0 && fade[i-0] >= 0) {\n                        float fadeU = 0;\n                        float fadeV = 0;\n                        for (int j=0; j<3; j++)\n                        {\n                            if (j == 0) {\n                                fadeU = fade[i-2];\n                                fadeV = fade[i-0];\n                            } else if (j == 1) {\n                                fadeU = fade[i-1];\n                                fadeV = fade[i-2];\n                            } else if (j == 2) {\n                                fadeV = fade[i-1];\n                                fadeU = fade[i-0];\n                            }\n                            if (fadeU > 0 || fadeV > 0) {\n                                //Debug.Log(\"fade side\");\n                                float fader = fadeU > 0 ? fadeU : fadeV;\n                                addUV(1, fader, fader);\n                            } else {\n                                //Debug.Log(\"corner fade\");\n                                float fader = fade[i-2] > 0 ? fade[i-2] : fade[i-1] > 0 ? fade[i-1] : fade[i-0];\n                                addUV(5, fader, fader);\n                            }\n                        }\n                    } else if (count == 1) {\n                        // may be fade is < 0\n                        float fadeU = 0;\n                        float fadeV = 0;\n                        for (int j=0; j<3; j++)\n                        {\n                            if (j == 0) {\n                                fadeU = fade[i-2];\n                                fadeV = fade[i-0];\n                            } else if (j == 1) {\n                                fadeU = fade[i-1];\n                                fadeV = fade[i-2];\n                            } else if (j == 2) {\n                                fadeV = fade[i-1];\n                                fadeU = fade[i-0];\n                            }\n                            if (fadeU > 0 || fadeV > 0) {\n                                //Debug.Log(\"fan fade\");\n                                float fader = fadeU > 0 ? fadeU : fadeV;\n                                addUV(9, fader, fader);\n                            } else {\n                                //Debug.Log(\"zero fade\");\n                                float fader = fade[i-2] > 0 ? fade[i-2] : fade[i-1] > 0 ? fade[i-1] : fade[i-0];\n                                addUV(0, fader, fader);\n                            }\n                        }\n                    }\n                }\n            }\n            return uvs;\n\n            void addUV(int i, float rx, float ry)\n            {\n                uvs.Add(new Vector4(UVS[i].x, UVS[i].y, rx, ry));\n            }\n        }\n\n        public List<Color> GetColors()\n        {\n            return color;\n        }\n    }\n}\n"
  },
  {
    "path": "csharp/Assets/Vaser/VertexArrayHolder.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 0321073adc558cd47964c01d4ded4443\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Assets/Vaser.meta",
    "content": "fileFormatVersion: 2\nguid: 89409ccbf42597ee79d84df5e334ee24\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "csharp/Docs/README.md",
    "content": "![Screenshot](Screenshot%20at%202019-10-23%2021-04-21.png)\n![Screenshot](Screenshot%20at%202019-10-29%2015-48-55.png)\n![Screenshot](Screenshot%20at%202019-10-30%2000-13-05.png)\n![Screenshot](Screenshot%20at%202019-11-10%2002-48-59.png)\n![Screenshot](Screenshot%20at%202019-12-02%2000-44-00.png)\n![Screenshot](Screenshot%20at%202019-12-02%2004-27-00.png)\n![Screenshot](Screenshot%20at%202019-12-07%2023-56-00.png)\n![Screenshot](Screenshot%20at%202019-12-08%2022-44-00.png)\n![Screenshot](Screenshot%20at%202019-12-11%2001-11-10.png)\n![Screenshot](Screenshot%20at%202019-12-15%2003-31-00.png)\n![Screenshot](Screenshot%20at%202019-12-15%2003-57-00.png)"
  },
  {
    "path": "csharp/Packages/manifest.json",
    "content": "{\n  \"dependencies\": {\n    \"com.unity.collab-proxy\": \"1.2.16\",\n    \"com.unity.ext.nunit\": \"1.0.0\",\n    \"com.unity.ide.rider\": \"1.1.0\",\n    \"com.unity.ide.vscode\": \"1.1.2\",\n    \"com.unity.package-manager-ui\": \"2.2.0\",\n    \"com.unity.test-framework\": \"1.0.13\",\n    \"com.unity.textmeshpro\": \"2.0.1\",\n    \"com.unity.timeline\": \"1.1.0\",\n    \"com.unity.ugui\": \"1.0.0\",\n    \"com.unity.modules.ai\": \"1.0.0\",\n    \"com.unity.modules.androidjni\": \"1.0.0\",\n    \"com.unity.modules.animation\": \"1.0.0\",\n    \"com.unity.modules.assetbundle\": \"1.0.0\",\n    \"com.unity.modules.audio\": \"1.0.0\",\n    \"com.unity.modules.cloth\": \"1.0.0\",\n    \"com.unity.modules.director\": \"1.0.0\",\n    \"com.unity.modules.imageconversion\": \"1.0.0\",\n    \"com.unity.modules.imgui\": \"1.0.0\",\n    \"com.unity.modules.jsonserialize\": \"1.0.0\",\n    \"com.unity.modules.particlesystem\": \"1.0.0\",\n    \"com.unity.modules.physics\": \"1.0.0\",\n    \"com.unity.modules.physics2d\": \"1.0.0\",\n    \"com.unity.modules.screencapture\": \"1.0.0\",\n    \"com.unity.modules.terrain\": \"1.0.0\",\n    \"com.unity.modules.terrainphysics\": \"1.0.0\",\n    \"com.unity.modules.tilemap\": \"1.0.0\",\n    \"com.unity.modules.ui\": \"1.0.0\",\n    \"com.unity.modules.uielements\": \"1.0.0\",\n    \"com.unity.modules.umbra\": \"1.0.0\",\n    \"com.unity.modules.unityanalytics\": \"1.0.0\",\n    \"com.unity.modules.unitywebrequest\": \"1.0.0\",\n    \"com.unity.modules.unitywebrequestassetbundle\": \"1.0.0\",\n    \"com.unity.modules.unitywebrequestaudio\": \"1.0.0\",\n    \"com.unity.modules.unitywebrequesttexture\": \"1.0.0\",\n    \"com.unity.modules.unitywebrequestwww\": \"1.0.0\",\n    \"com.unity.modules.vehicles\": \"1.0.0\",\n    \"com.unity.modules.video\": \"1.0.0\",\n    \"com.unity.modules.vr\": \"1.0.0\",\n    \"com.unity.modules.wind\": \"1.0.0\",\n    \"com.unity.modules.xr\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "csharp/ProjectSettings/AudioManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!11 &1\nAudioManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Volume: 1\n  Rolloff Scale: 1\n  Doppler Factor: 1\n  Default Speaker Mode: 2\n  m_SampleRate: 0\n  m_DSPBufferSize: 1024\n  m_VirtualVoiceCount: 512\n  m_RealVoiceCount: 32\n  m_SpatializerPlugin: \n  m_AmbisonicDecoderPlugin: \n  m_DisableAudio: 0\n  m_VirtualizeEffects: 1\n  m_RequestedDSPBufferSize: 1024\n"
  },
  {
    "path": "csharp/ProjectSettings/ClusterInputManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!236 &1\nClusterInputManager:\n  m_ObjectHideFlags: 0\n  m_Inputs: []\n"
  },
  {
    "path": "csharp/ProjectSettings/DynamicsManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!55 &1\nPhysicsManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 11\n  m_Gravity: {x: 0, y: -9.81, z: 0}\n  m_DefaultMaterial: {fileID: 0}\n  m_BounceThreshold: 2\n  m_SleepThreshold: 0.005\n  m_DefaultContactOffset: 0.01\n  m_DefaultSolverIterations: 6\n  m_DefaultSolverVelocityIterations: 1\n  m_QueriesHitBackfaces: 0\n  m_QueriesHitTriggers: 1\n  m_EnableAdaptiveForce: 0\n  m_ClothInterCollisionDistance: 0\n  m_ClothInterCollisionStiffness: 0\n  m_ContactsGeneration: 1\n  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n  m_AutoSimulation: 1\n  m_AutoSyncTransforms: 0\n  m_ReuseCollisionCallbacks: 1\n  m_ClothInterCollisionSettingsToggle: 0\n  m_ContactPairsMode: 0\n  m_BroadphaseType: 0\n  m_WorldBounds:\n    m_Center: {x: 0, y: 0, z: 0}\n    m_Extent: {x: 250, y: 250, z: 250}\n  m_WorldSubdivisions: 8\n  m_FrictionType: 0\n  m_EnableEnhancedDeterminism: 0\n  m_EnableUnifiedHeightmaps: 1\n  m_DefaultMaxAngluarSpeed: 7\n"
  },
  {
    "path": "csharp/ProjectSettings/EditorBuildSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1045 &1\nEditorBuildSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Scenes: []\n  m_configObjects: {}\n"
  },
  {
    "path": "csharp/ProjectSettings/EditorSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!159 &1\nEditorSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 8\n  m_ExternalVersionControlSupport: Hidden Meta Files\n  m_SerializationMode: 2\n  m_LineEndingsForNewScripts: 2\n  m_DefaultBehaviorMode: 0\n  m_PrefabRegularEnvironment: {fileID: 0}\n  m_PrefabUIEnvironment: {fileID: 0}\n  m_SpritePackerMode: 0\n  m_SpritePackerPaddingPower: 1\n  m_EtcTextureCompressorBehavior: 1\n  m_EtcTextureFastCompressor: 1\n  m_EtcTextureNormalCompressor: 2\n  m_EtcTextureBestCompressor: 4\n  m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref\n  m_ProjectGenerationRootNamespace: \n  m_CollabEditorSettings:\n    inProgressEnabled: 1\n  m_EnableTextureStreamingInEditMode: 1\n  m_EnableTextureStreamingInPlayMode: 1\n  m_AsyncShaderCompilation: 1\n  m_ShowLightmapResolutionOverlay: 1\n"
  },
  {
    "path": "csharp/ProjectSettings/GraphicsSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!30 &1\nGraphicsSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_Deferred:\n    m_Mode: 1\n    m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}\n  m_DeferredReflections:\n    m_Mode: 1\n    m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}\n  m_ScreenSpaceShadows:\n    m_Mode: 1\n    m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}\n  m_LegacyDeferred:\n    m_Mode: 1\n    m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}\n  m_DepthNormals:\n    m_Mode: 1\n    m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}\n  m_MotionVectors:\n    m_Mode: 1\n    m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}\n  m_LightHalo:\n    m_Mode: 1\n    m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}\n  m_LensFlare:\n    m_Mode: 1\n    m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}\n  m_AlwaysIncludedShaders:\n  - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}\n  m_PreloadedShaders: []\n  m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,\n    type: 0}\n  m_CustomRenderPipeline: {fileID: 0}\n  m_TransparencySortMode: 0\n  m_TransparencySortAxis: {x: 0, y: 0, z: 1}\n  m_DefaultRenderingPath: 1\n  m_DefaultMobileRenderingPath: 1\n  m_TierSettings: []\n  m_LightmapStripping: 0\n  m_FogStripping: 0\n  m_InstancingStripping: 0\n  m_LightmapKeepPlain: 1\n  m_LightmapKeepDirCombined: 1\n  m_LightmapKeepDynamicPlain: 1\n  m_LightmapKeepDynamicDirCombined: 1\n  m_LightmapKeepShadowMask: 1\n  m_LightmapKeepSubtractive: 1\n  m_FogKeepLinear: 1\n  m_FogKeepExp: 1\n  m_FogKeepExp2: 1\n  m_AlbedoSwatchInfos: []\n  m_LightsUseLinearIntensity: 0\n  m_LightsUseColorTemperature: 0\n  m_LogWhenShaderIsCompiled: 0\n"
  },
  {
    "path": "csharp/ProjectSettings/InputManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!13 &1\nInputManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Axes:\n  - serializedVersion: 3\n    m_Name: Horizontal\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: left\n    positiveButton: right\n    altNegativeButton: a\n    altPositiveButton: d\n    gravity: 3\n    dead: 0.001\n    sensitivity: 3\n    snap: 1\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Vertical\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: down\n    positiveButton: up\n    altNegativeButton: s\n    altPositiveButton: w\n    gravity: 3\n    dead: 0.001\n    sensitivity: 3\n    snap: 1\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire1\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left ctrl\n    altNegativeButton: \n    altPositiveButton: mouse 0\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire2\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left alt\n    altNegativeButton: \n    altPositiveButton: mouse 1\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire3\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left shift\n    altNegativeButton: \n    altPositiveButton: mouse 2\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Jump\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: space\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse X\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse Y\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 1\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse ScrollWheel\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 2\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Horizontal\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0.19\n    sensitivity: 1\n    snap: 0\n    invert: 0\n    type: 2\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Vertical\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0.19\n    sensitivity: 1\n    snap: 0\n    invert: 1\n    type: 2\n    axis: 1\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire1\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 0\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire2\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 1\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire3\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 2\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Jump\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 3\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Submit\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: return\n    altNegativeButton: \n    altPositiveButton: joystick button 0\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Submit\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: enter\n    altNegativeButton: \n    altPositiveButton: space\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Cancel\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: escape\n    altNegativeButton: \n    altPositiveButton: joystick button 1\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n"
  },
  {
    "path": "csharp/ProjectSettings/NavMeshAreas.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!126 &1\nNavMeshProjectSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  areas:\n  - name: Walkable\n    cost: 1\n  - name: Not Walkable\n    cost: 1\n  - name: Jump\n    cost: 2\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  m_LastAgentTypeID: -887442657\n  m_Settings:\n  - serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.75\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    debug:\n      m_Flags: 0\n  m_SettingNames:\n  - Humanoid\n"
  },
  {
    "path": "csharp/ProjectSettings/Physics2DSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!19 &1\nPhysics2DSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 4\n  m_Gravity: {x: 0, y: -9.81}\n  m_DefaultMaterial: {fileID: 0}\n  m_VelocityIterations: 8\n  m_PositionIterations: 3\n  m_VelocityThreshold: 1\n  m_MaxLinearCorrection: 0.2\n  m_MaxAngularCorrection: 8\n  m_MaxTranslationSpeed: 100\n  m_MaxRotationSpeed: 360\n  m_BaumgarteScale: 0.2\n  m_BaumgarteTimeOfImpactScale: 0.75\n  m_TimeToSleep: 0.5\n  m_LinearSleepTolerance: 0.01\n  m_AngularSleepTolerance: 2\n  m_DefaultContactOffset: 0.01\n  m_JobOptions:\n    serializedVersion: 2\n    useMultithreading: 0\n    useConsistencySorting: 0\n    m_InterpolationPosesPerJob: 100\n    m_NewContactsPerJob: 30\n    m_CollideContactsPerJob: 100\n    m_ClearFlagsPerJob: 200\n    m_ClearBodyForcesPerJob: 200\n    m_SyncDiscreteFixturesPerJob: 50\n    m_SyncContinuousFixturesPerJob: 50\n    m_FindNearestContactsPerJob: 100\n    m_UpdateTriggerContactsPerJob: 100\n    m_IslandSolverCostThreshold: 100\n    m_IslandSolverBodyCostScale: 1\n    m_IslandSolverContactCostScale: 10\n    m_IslandSolverJointCostScale: 10\n    m_IslandSolverBodiesPerJob: 50\n    m_IslandSolverContactsPerJob: 50\n  m_AutoSimulation: 1\n  m_QueriesHitTriggers: 1\n  m_QueriesStartInColliders: 1\n  m_CallbacksOnDisable: 1\n  m_ReuseCollisionCallbacks: 1\n  m_AutoSyncTransforms: 0\n  m_AlwaysShowColliders: 0\n  m_ShowColliderSleep: 1\n  m_ShowColliderContacts: 0\n  m_ShowColliderAABB: 0\n  m_ContactArrowScale: 0.2\n  m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}\n  m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}\n  m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}\n  m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}\n  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n"
  },
  {
    "path": "csharp/ProjectSettings/PresetManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1386491679 &1\nPresetManager:\n  m_ObjectHideFlags: 0\n  m_DefaultList: []\n"
  },
  {
    "path": "csharp/ProjectSettings/ProjectSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!129 &1\nPlayerSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 18\n  productGUID: 91caf70474c988447b5a55aeda9bd15b\n  AndroidProfiler: 0\n  AndroidFilterTouchesWhenObscured: 0\n  AndroidEnableSustainedPerformanceMode: 0\n  defaultScreenOrientation: 4\n  targetDevice: 2\n  useOnDemandResources: 0\n  accelerometerFrequency: 60\n  companyName: DefaultCompany\n  productName: vaser-unity\n  defaultCursor: {fileID: 0}\n  cursorHotspot: {x: 0, y: 0}\n  m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}\n  m_ShowUnitySplashScreen: 1\n  m_ShowUnitySplashLogo: 1\n  m_SplashScreenOverlayOpacity: 1\n  m_SplashScreenAnimation: 1\n  m_SplashScreenLogoStyle: 1\n  m_SplashScreenDrawMode: 0\n  m_SplashScreenBackgroundAnimationZoom: 1\n  m_SplashScreenLogoAnimationZoom: 1\n  m_SplashScreenBackgroundLandscapeAspect: 1\n  m_SplashScreenBackgroundPortraitAspect: 1\n  m_SplashScreenBackgroundLandscapeUvs:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  m_SplashScreenBackgroundPortraitUvs:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  m_SplashScreenLogos: []\n  m_VirtualRealitySplashScreen: {fileID: 0}\n  m_HolographicTrackingLossScreen: {fileID: 0}\n  defaultScreenWidth: 1024\n  defaultScreenHeight: 768\n  defaultScreenWidthWeb: 960\n  defaultScreenHeightWeb: 600\n  m_StereoRenderingPath: 0\n  m_ActiveColorSpace: 0\n  m_MTRendering: 1\n  m_StackTraceTypes: 010000000100000001000000010000000100000001000000\n  iosShowActivityIndicatorOnLoading: -1\n  androidShowActivityIndicatorOnLoading: -1\n  displayResolutionDialog: 0\n  iosUseCustomAppBackgroundBehavior: 0\n  iosAllowHTTPDownload: 1\n  allowedAutorotateToPortrait: 1\n  allowedAutorotateToPortraitUpsideDown: 1\n  allowedAutorotateToLandscapeRight: 1\n  allowedAutorotateToLandscapeLeft: 1\n  useOSAutorotation: 1\n  use32BitDisplayBuffer: 1\n  preserveFramebufferAlpha: 0\n  disableDepthAndStencilBuffers: 0\n  androidStartInFullscreen: 1\n  androidRenderOutsideSafeArea: 1\n  androidUseSwappy: 0\n  androidBlitType: 0\n  defaultIsNativeResolution: 1\n  macRetinaSupport: 1\n  runInBackground: 1\n  captureSingleScreen: 0\n  muteOtherAudioSources: 0\n  Prepare IOS For Recording: 0\n  Force IOS Speakers When Recording: 0\n  deferSystemGesturesMode: 0\n  hideHomeButton: 0\n  submitAnalytics: 1\n  usePlayerLog: 1\n  bakeCollisionMeshes: 0\n  forceSingleInstance: 0\n  useFlipModelSwapchain: 1\n  resizableWindow: 0\n  useMacAppStoreValidation: 0\n  macAppStoreCategory: public.app-category.games\n  gpuSkinning: 1\n  graphicsJobs: 0\n  xboxPIXTextureCapture: 0\n  xboxEnableAvatar: 0\n  xboxEnableKinect: 0\n  xboxEnableKinectAutoTracking: 0\n  xboxEnableFitness: 0\n  visibleInBackground: 1\n  allowFullscreenSwitch: 1\n  graphicsJobMode: 0\n  fullscreenMode: 1\n  xboxSpeechDB: 0\n  xboxEnableHeadOrientation: 0\n  xboxEnableGuest: 0\n  xboxEnablePIXSampling: 0\n  metalFramebufferOnly: 0\n  xboxOneResolution: 0\n  xboxOneSResolution: 0\n  xboxOneXResolution: 3\n  xboxOneMonoLoggingLevel: 0\n  xboxOneLoggingLevel: 1\n  xboxOneDisableEsram: 0\n  xboxOnePresentImmediateThreshold: 0\n  switchQueueCommandMemory: 0\n  switchQueueControlMemory: 16384\n  switchQueueComputeMemory: 262144\n  switchNVNShaderPoolsGranularity: 33554432\n  switchNVNDefaultPoolsGranularity: 16777216\n  switchNVNOtherPoolsGranularity: 16777216\n  vulkanEnableSetSRGBWrite: 0\n  m_SupportedAspectRatios:\n    4:3: 1\n    5:4: 1\n    16:10: 1\n    16:9: 1\n    Others: 1\n  bundleVersion: 0.1\n  preloadedAssets: []\n  metroInputSource: 0\n  wsaTransparentSwapchain: 0\n  m_HolographicPauseOnTrackingLoss: 1\n  xboxOneDisableKinectGpuReservation: 1\n  xboxOneEnable7thCore: 1\n  vrSettings:\n    cardboard:\n      depthFormat: 0\n      enableTransitionView: 0\n    daydream:\n      depthFormat: 0\n      useSustainedPerformanceMode: 0\n      enableVideoLayer: 0\n      useProtectedVideoMemory: 0\n      minimumSupportedHeadTracking: 0\n      maximumSupportedHeadTracking: 1\n    hololens:\n      depthFormat: 1\n      depthBufferSharingEnabled: 1\n    lumin:\n      depthFormat: 0\n      frameTiming: 2\n      enableGLCache: 0\n      glCacheMaxBlobSize: 524288\n      glCacheMaxFileSize: 8388608\n    oculus:\n      sharedDepthBuffer: 1\n      dashSupport: 1\n      lowOverheadMode: 0\n      protectedContext: 0\n      v2Signing: 0\n    enable360StereoCapture: 0\n  isWsaHolographicRemotingEnabled: 0\n  protectGraphicsMemory: 0\n  enableFrameTimingStats: 0\n  useHDRDisplay: 0\n  m_ColorGamuts: 00000000\n  targetPixelDensity: 30\n  resolutionScalingMode: 0\n  androidSupportedAspectRatio: 1\n  androidMaxAspectRatio: 2.1\n  applicationIdentifier: {}\n  buildNumber: {}\n  AndroidBundleVersionCode: 1\n  AndroidMinSdkVersion: 16\n  AndroidTargetSdkVersion: 0\n  AndroidPreferredInstallLocation: 1\n  aotOptions: \n  stripEngineCode: 1\n  iPhoneStrippingLevel: 0\n  iPhoneScriptCallOptimization: 0\n  ForceInternetPermission: 0\n  ForceSDCardPermission: 0\n  CreateWallpaper: 0\n  APKExpansionFiles: 0\n  keepLoadedShadersAlive: 0\n  StripUnusedMeshComponents: 1\n  VertexChannelCompressionMask: 4054\n  iPhoneSdkVersion: 988\n  iOSTargetOSVersionString: 9.0\n  tvOSSdkVersion: 0\n  tvOSRequireExtendedGameController: 0\n  tvOSTargetOSVersionString: 9.0\n  uIPrerenderedIcon: 0\n  uIRequiresPersistentWiFi: 0\n  uIRequiresFullScreen: 1\n  uIStatusBarHidden: 1\n  uIExitOnSuspend: 0\n  uIStatusBarStyle: 0\n  iPhoneSplashScreen: {fileID: 0}\n  iPhoneHighResSplashScreen: {fileID: 0}\n  iPhoneTallHighResSplashScreen: {fileID: 0}\n  iPhone47inSplashScreen: {fileID: 0}\n  iPhone55inPortraitSplashScreen: {fileID: 0}\n  iPhone55inLandscapeSplashScreen: {fileID: 0}\n  iPhone58inPortraitSplashScreen: {fileID: 0}\n  iPhone58inLandscapeSplashScreen: {fileID: 0}\n  iPadPortraitSplashScreen: {fileID: 0}\n  iPadHighResPortraitSplashScreen: {fileID: 0}\n  iPadLandscapeSplashScreen: {fileID: 0}\n  iPadHighResLandscapeSplashScreen: {fileID: 0}\n  iPhone65inPortraitSplashScreen: {fileID: 0}\n  iPhone65inLandscapeSplashScreen: {fileID: 0}\n  iPhone61inPortraitSplashScreen: {fileID: 0}\n  iPhone61inLandscapeSplashScreen: {fileID: 0}\n  appleTVSplashScreen: {fileID: 0}\n  appleTVSplashScreen2x: {fileID: 0}\n  tvOSSmallIconLayers: []\n  tvOSSmallIconLayers2x: []\n  tvOSLargeIconLayers: []\n  tvOSLargeIconLayers2x: []\n  tvOSTopShelfImageLayers: []\n  tvOSTopShelfImageLayers2x: []\n  tvOSTopShelfImageWideLayers: []\n  tvOSTopShelfImageWideLayers2x: []\n  iOSLaunchScreenType: 0\n  iOSLaunchScreenPortrait: {fileID: 0}\n  iOSLaunchScreenLandscape: {fileID: 0}\n  iOSLaunchScreenBackgroundColor:\n    serializedVersion: 2\n    rgba: 0\n  iOSLaunchScreenFillPct: 100\n  iOSLaunchScreenSize: 100\n  iOSLaunchScreenCustomXibPath: \n  iOSLaunchScreeniPadType: 0\n  iOSLaunchScreeniPadImage: {fileID: 0}\n  iOSLaunchScreeniPadBackgroundColor:\n    serializedVersion: 2\n    rgba: 0\n  iOSLaunchScreeniPadFillPct: 100\n  iOSLaunchScreeniPadSize: 100\n  iOSLaunchScreeniPadCustomXibPath: \n  iOSUseLaunchScreenStoryboard: 0\n  iOSLaunchScreenCustomStoryboardPath: \n  iOSDeviceRequirements: []\n  iOSURLSchemes: []\n  iOSBackgroundModes: 0\n  iOSMetalForceHardShadows: 0\n  metalEditorSupport: 1\n  metalAPIValidation: 1\n  iOSRenderExtraFrameOnPause: 0\n  appleDeveloperTeamID: \n  iOSManualSigningProvisioningProfileID: \n  tvOSManualSigningProvisioningProfileID: \n  iOSManualSigningProvisioningProfileType: 0\n  tvOSManualSigningProvisioningProfileType: 0\n  appleEnableAutomaticSigning: 0\n  iOSRequireARKit: 0\n  iOSAutomaticallyDetectAndAddCapabilities: 1\n  appleEnableProMotion: 0\n  clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea\n  templatePackageId: com.unity.template.3d@3.1.2\n  templateDefaultScene: Assets/Scenes/SampleScene.unity\n  AndroidTargetArchitectures: 1\n  AndroidSplashScreenScale: 0\n  androidSplashScreen: {fileID: 0}\n  AndroidKeystoreName: '{inproject}: '\n  AndroidKeyaliasName: \n  AndroidBuildApkPerCpuArchitecture: 0\n  AndroidTVCompatibility: 0\n  AndroidIsGame: 1\n  AndroidEnableTango: 0\n  androidEnableBanner: 1\n  androidUseLowAccuracyLocation: 0\n  androidUseCustomKeystore: 0\n  m_AndroidBanners:\n  - width: 320\n    height: 180\n    banner: {fileID: 0}\n  androidGamepadSupportLevel: 0\n  AndroidValidateAppBundleSize: 1\n  AndroidAppBundleSizeToValidate: 150\n  resolutionDialogBanner: {fileID: 0}\n  m_BuildTargetIcons: []\n  m_BuildTargetPlatformIcons: []\n  m_BuildTargetBatching:\n  - m_BuildTarget: Standalone\n    m_StaticBatching: 1\n    m_DynamicBatching: 0\n  - m_BuildTarget: tvOS\n    m_StaticBatching: 1\n    m_DynamicBatching: 0\n  - m_BuildTarget: Android\n    m_StaticBatching: 1\n    m_DynamicBatching: 0\n  - m_BuildTarget: iPhone\n    m_StaticBatching: 1\n    m_DynamicBatching: 0\n  - m_BuildTarget: WebGL\n    m_StaticBatching: 0\n    m_DynamicBatching: 0\n  m_BuildTargetGraphicsAPIs:\n  - m_BuildTarget: AndroidPlayer\n    m_APIs: 150000000b000000\n    m_Automatic: 0\n  - m_BuildTarget: iOSSupport\n    m_APIs: 10000000\n    m_Automatic: 1\n  - m_BuildTarget: AppleTVSupport\n    m_APIs: 10000000\n    m_Automatic: 0\n  - m_BuildTarget: WebGLSupport\n    m_APIs: 0b000000\n    m_Automatic: 1\n  m_BuildTargetVRSettings:\n  - m_BuildTarget: Standalone\n    m_Enabled: 0\n    m_Devices:\n    - Oculus\n    - OpenVR\n  openGLRequireES31: 0\n  openGLRequireES31AEP: 0\n  openGLRequireES32: 0\n  vuforiaEnabled: 0\n  m_TemplateCustomTags: {}\n  mobileMTRendering:\n    Android: 1\n    iPhone: 1\n    tvOS: 1\n  m_BuildTargetGroupLightmapEncodingQuality: []\n  m_BuildTargetGroupLightmapSettings: []\n  playModeTestRunnerEnabled: 0\n  runPlayModeTestAsEditModeTest: 0\n  actionOnDotNetUnhandledException: 1\n  enableInternalProfiler: 0\n  logObjCUncaughtExceptions: 1\n  enableCrashReportAPI: 0\n  cameraUsageDescription: \n  locationUsageDescription: \n  microphoneUsageDescription: \n  switchNetLibKey: \n  switchSocketMemoryPoolSize: 6144\n  switchSocketAllocatorPoolSize: 128\n  switchSocketConcurrencyLimit: 14\n  switchScreenResolutionBehavior: 2\n  switchUseCPUProfiler: 0\n  switchApplicationID: 0x01004b9000490000\n  switchNSODependencies: \n  switchTitleNames_0: \n  switchTitleNames_1: \n  switchTitleNames_2: \n  switchTitleNames_3: \n  switchTitleNames_4: \n  switchTitleNames_5: \n  switchTitleNames_6: \n  switchTitleNames_7: \n  switchTitleNames_8: \n  switchTitleNames_9: \n  switchTitleNames_10: \n  switchTitleNames_11: \n  switchTitleNames_12: \n  switchTitleNames_13: \n  switchTitleNames_14: \n  switchPublisherNames_0: \n  switchPublisherNames_1: \n  switchPublisherNames_2: \n  switchPublisherNames_3: \n  switchPublisherNames_4: \n  switchPublisherNames_5: \n  switchPublisherNames_6: \n  switchPublisherNames_7: \n  switchPublisherNames_8: \n  switchPublisherNames_9: \n  switchPublisherNames_10: \n  switchPublisherNames_11: \n  switchPublisherNames_12: \n  switchPublisherNames_13: \n  switchPublisherNames_14: \n  switchIcons_0: {fileID: 0}\n  switchIcons_1: {fileID: 0}\n  switchIcons_2: {fileID: 0}\n  switchIcons_3: {fileID: 0}\n  switchIcons_4: {fileID: 0}\n  switchIcons_5: {fileID: 0}\n  switchIcons_6: {fileID: 0}\n  switchIcons_7: {fileID: 0}\n  switchIcons_8: {fileID: 0}\n  switchIcons_9: {fileID: 0}\n  switchIcons_10: {fileID: 0}\n  switchIcons_11: {fileID: 0}\n  switchIcons_12: {fileID: 0}\n  switchIcons_13: {fileID: 0}\n  switchIcons_14: {fileID: 0}\n  switchSmallIcons_0: {fileID: 0}\n  switchSmallIcons_1: {fileID: 0}\n  switchSmallIcons_2: {fileID: 0}\n  switchSmallIcons_3: {fileID: 0}\n  switchSmallIcons_4: {fileID: 0}\n  switchSmallIcons_5: {fileID: 0}\n  switchSmallIcons_6: {fileID: 0}\n  switchSmallIcons_7: {fileID: 0}\n  switchSmallIcons_8: {fileID: 0}\n  switchSmallIcons_9: {fileID: 0}\n  switchSmallIcons_10: {fileID: 0}\n  switchSmallIcons_11: {fileID: 0}\n  switchSmallIcons_12: {fileID: 0}\n  switchSmallIcons_13: {fileID: 0}\n  switchSmallIcons_14: {fileID: 0}\n  switchManualHTML: \n  switchAccessibleURLs: \n  switchLegalInformation: \n  switchMainThreadStackSize: 1048576\n  switchPresenceGroupId: \n  switchLogoHandling: 0\n  switchReleaseVersion: 0\n  switchDisplayVersion: 1.0.0\n  switchStartupUserAccount: 0\n  switchTouchScreenUsage: 0\n  switchSupportedLanguagesMask: 0\n  switchLogoType: 0\n  switchApplicationErrorCodeCategory: \n  switchUserAccountSaveDataSize: 0\n  switchUserAccountSaveDataJournalSize: 0\n  switchApplicationAttribute: 0\n  switchCardSpecSize: -1\n  switchCardSpecClock: -1\n  switchRatingsMask: 0\n  switchRatingsInt_0: 0\n  switchRatingsInt_1: 0\n  switchRatingsInt_2: 0\n  switchRatingsInt_3: 0\n  switchRatingsInt_4: 0\n  switchRatingsInt_5: 0\n  switchRatingsInt_6: 0\n  switchRatingsInt_7: 0\n  switchRatingsInt_8: 0\n  switchRatingsInt_9: 0\n  switchRatingsInt_10: 0\n  switchRatingsInt_11: 0\n  switchLocalCommunicationIds_0: \n  switchLocalCommunicationIds_1: \n  switchLocalCommunicationIds_2: \n  switchLocalCommunicationIds_3: \n  switchLocalCommunicationIds_4: \n  switchLocalCommunicationIds_5: \n  switchLocalCommunicationIds_6: \n  switchLocalCommunicationIds_7: \n  switchParentalControl: 0\n  switchAllowsScreenshot: 1\n  switchAllowsVideoCapturing: 1\n  switchAllowsRuntimeAddOnContentInstall: 0\n  switchDataLossConfirmation: 0\n  switchUserAccountLockEnabled: 0\n  switchSystemResourceMemory: 16777216\n  switchSupportedNpadStyles: 22\n  switchNativeFsCacheSize: 32\n  switchIsHoldTypeHorizontal: 0\n  switchSupportedNpadCount: 8\n  switchSocketConfigEnabled: 0\n  switchTcpInitialSendBufferSize: 32\n  switchTcpInitialReceiveBufferSize: 64\n  switchTcpAutoSendBufferSizeMax: 256\n  switchTcpAutoReceiveBufferSizeMax: 256\n  switchUdpSendBufferSize: 9\n  switchUdpReceiveBufferSize: 42\n  switchSocketBufferEfficiency: 4\n  switchSocketInitializeEnabled: 1\n  switchNetworkInterfaceManagerInitializeEnabled: 1\n  switchPlayerConnectionEnabled: 1\n  ps4NPAgeRating: 12\n  ps4NPTitleSecret: \n  ps4NPTrophyPackPath: \n  ps4ParentalLevel: 11\n  ps4ContentID: ED1633-NPXX51362_00-0000000000000000\n  ps4Category: 0\n  ps4MasterVersion: 01.00\n  ps4AppVersion: 01.00\n  ps4AppType: 0\n  ps4ParamSfxPath: \n  ps4VideoOutPixelFormat: 0\n  ps4VideoOutInitialWidth: 1920\n  ps4VideoOutBaseModeInitialWidth: 1920\n  ps4VideoOutReprojectionRate: 60\n  ps4PronunciationXMLPath: \n  ps4PronunciationSIGPath: \n  ps4BackgroundImagePath: \n  ps4StartupImagePath: \n  ps4StartupImagesFolder: \n  ps4IconImagesFolder: \n  ps4SaveDataImagePath: \n  ps4SdkOverride: \n  ps4BGMPath: \n  ps4ShareFilePath: \n  ps4ShareOverlayImagePath: \n  ps4PrivacyGuardImagePath: \n  ps4NPtitleDatPath: \n  ps4RemotePlayKeyAssignment: -1\n  ps4RemotePlayKeyMappingDir: \n  ps4PlayTogetherPlayerCount: 0\n  ps4EnterButtonAssignment: 1\n  ps4ApplicationParam1: 0\n  ps4ApplicationParam2: 0\n  ps4ApplicationParam3: 0\n  ps4ApplicationParam4: 0\n  ps4DownloadDataSize: 0\n  ps4GarlicHeapSize: 2048\n  ps4ProGarlicHeapSize: 2560\n  playerPrefsMaxSize: 32768\n  ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ\n  ps4pnSessions: 1\n  ps4pnPresence: 1\n  ps4pnFriends: 1\n  ps4pnGameCustomData: 1\n  playerPrefsSupport: 0\n  enableApplicationExit: 0\n  resetTempFolder: 1\n  restrictedAudioUsageRights: 0\n  ps4UseResolutionFallback: 0\n  ps4ReprojectionSupport: 0\n  ps4UseAudio3dBackend: 0\n  ps4SocialScreenEnabled: 0\n  ps4ScriptOptimizationLevel: 0\n  ps4Audio3dVirtualSpeakerCount: 14\n  ps4attribCpuUsage: 0\n  ps4PatchPkgPath: \n  ps4PatchLatestPkgPath: \n  ps4PatchChangeinfoPath: \n  ps4PatchDayOne: 0\n  ps4attribUserManagement: 0\n  ps4attribMoveSupport: 0\n  ps4attrib3DSupport: 0\n  ps4attribShareSupport: 0\n  ps4attribExclusiveVR: 0\n  ps4disableAutoHideSplash: 0\n  ps4videoRecordingFeaturesUsed: 0\n  ps4contentSearchFeaturesUsed: 0\n  ps4attribEyeToEyeDistanceSettingVR: 0\n  ps4IncludedModules: []\n  monoEnv: \n  splashScreenBackgroundSourceLandscape: {fileID: 0}\n  splashScreenBackgroundSourcePortrait: {fileID: 0}\n  blurSplashScreenBackground: 1\n  spritePackerPolicy: \n  webGLMemorySize: 16\n  webGLExceptionSupport: 1\n  webGLNameFilesAsHashes: 0\n  webGLDataCaching: 1\n  webGLDebugSymbols: 0\n  webGLEmscriptenArgs: \n  webGLModulesDirectory: \n  webGLTemplate: APPLICATION:Default\n  webGLAnalyzeBuildSize: 0\n  webGLUseEmbeddedResources: 0\n  webGLCompressionFormat: 1\n  webGLLinkerTarget: 1\n  webGLThreadsSupport: 0\n  webGLWasmStreaming: 0\n  scriptingDefineSymbols: {}\n  platformArchitecture: {}\n  scriptingBackend: {}\n  il2cppCompilerConfiguration: {}\n  managedStrippingLevel: {}\n  incrementalIl2cppBuild: {}\n  allowUnsafeCode: 0\n  additionalIl2CppArgs: \n  scriptingRuntimeVersion: 1\n  gcIncremental: 0\n  gcWBarrierValidation: 0\n  apiCompatibilityLevelPerPlatform: {}\n  m_RenderingPath: 1\n  m_MobileRenderingPath: 1\n  metroPackageName: Template_3D\n  metroPackageVersion: \n  metroCertificatePath: \n  metroCertificatePassword: \n  metroCertificateSubject: \n  metroCertificateIssuer: \n  metroCertificateNotAfter: 0000000000000000\n  metroApplicationDescription: Template_3D\n  wsaImages: {}\n  metroTileShortName: \n  metroTileShowName: 0\n  metroMediumTileShowName: 0\n  metroLargeTileShowName: 0\n  metroWideTileShowName: 0\n  metroSupportStreamingInstall: 0\n  metroLastRequiredScene: 0\n  metroDefaultTileSize: 1\n  metroTileForegroundText: 2\n  metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}\n  metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628,\n    a: 1}\n  metroSplashScreenUseBackgroundColor: 0\n  platformCapabilities: {}\n  metroTargetDeviceFamilies: {}\n  metroFTAName: \n  metroFTAFileTypes: []\n  metroProtocolName: \n  XboxOneProductId: \n  XboxOneUpdateKey: \n  XboxOneSandboxId: \n  XboxOneContentId: \n  XboxOneTitleId: \n  XboxOneSCId: \n  XboxOneGameOsOverridePath: \n  XboxOnePackagingOverridePath: \n  XboxOneAppManifestOverridePath: \n  XboxOneVersion: 1.0.0.0\n  XboxOnePackageEncryption: 0\n  XboxOnePackageUpdateGranularity: 2\n  XboxOneDescription: \n  XboxOneLanguage:\n  - enus\n  XboxOneCapability: []\n  XboxOneGameRating: {}\n  XboxOneIsContentPackage: 0\n  XboxOneEnableGPUVariability: 1\n  XboxOneSockets: {}\n  XboxOneSplashScreen: {fileID: 0}\n  XboxOneAllowedProductIds: []\n  XboxOnePersistentLocalStorageSize: 0\n  XboxOneXTitleMemory: 8\n  xboxOneScriptCompiler: 1\n  XboxOneOverrideIdentityName: \n  vrEditorSettings:\n    daydream:\n      daydreamIconForeground: {fileID: 0}\n      daydreamIconBackground: {fileID: 0}\n  cloudServicesEnabled:\n    UNet: 1\n  luminIcon:\n    m_Name: \n    m_ModelFolderPath: \n    m_PortalFolderPath: \n  luminCert:\n    m_CertPath: \n    m_SignPackage: 1\n  luminIsChannelApp: 0\n  luminVersion:\n    m_VersionCode: 1\n    m_VersionName: \n  facebookSdkVersion: 7.9.4\n  facebookAppId: \n  facebookCookies: 1\n  facebookLogging: 1\n  facebookStatus: 1\n  facebookXfbml: 0\n  facebookFrictionlessRequests: 1\n  apiCompatibilityLevel: 6\n  cloudProjectId: \n  framebufferDepthMemorylessMode: 0\n  projectName: \n  organizationId: \n  cloudEnabled: 0\n  enableNativePlatformBackendsForNewInputSystem: 0\n  disableOldInputManagerSupport: 0\n  legacyClampBlendShapeWeights: 0\n"
  },
  {
    "path": "csharp/ProjectSettings/ProjectVersion.txt",
    "content": "m_EditorVersion: 2019.2.14f1\nm_EditorVersionWithRevision: 2019.2.14f1 (49dd4e9fa428)\n"
  },
  {
    "path": "csharp/ProjectSettings/QualitySettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!47 &1\nQualitySettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 5\n  m_CurrentQuality: 5\n  m_QualitySettings:\n  - serializedVersion: 2\n    name: Very Low\n    pixelLightCount: 0\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 15\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 1\n    textureQuality: 1\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 0\n    lodBias: 0.3\n    maximumLODLevel: 0\n    streamingMipmapsActive: 0\n    streamingMipmapsAddAllCameras: 1\n    streamingMipmapsMemoryBudget: 512\n    streamingMipmapsRenderersPerFrame: 512\n    streamingMipmapsMaxLevelReduction: 2\n    streamingMipmapsMaxFileIORequests: 1024\n    particleRaycastBudget: 4\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    asyncUploadPersistentBuffer: 1\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Low\n    pixelLightCount: 0\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 20\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 0\n    lodBias: 0.4\n    maximumLODLevel: 0\n    streamingMipmapsActive: 0\n    streamingMipmapsAddAllCameras: 1\n    streamingMipmapsMemoryBudget: 512\n    streamingMipmapsRenderersPerFrame: 512\n    streamingMipmapsMaxLevelReduction: 2\n    streamingMipmapsMaxFileIORequests: 1024\n    particleRaycastBudget: 16\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    asyncUploadPersistentBuffer: 1\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Medium\n    pixelLightCount: 1\n    shadows: 1\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 20\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 1\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 0.7\n    maximumLODLevel: 0\n    streamingMipmapsActive: 0\n    streamingMipmapsAddAllCameras: 1\n    streamingMipmapsMemoryBudget: 512\n    streamingMipmapsRenderersPerFrame: 512\n    streamingMipmapsMaxLevelReduction: 2\n    streamingMipmapsMaxFileIORequests: 1024\n    particleRaycastBudget: 64\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    asyncUploadPersistentBuffer: 1\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: High\n    pixelLightCount: 2\n    shadows: 2\n    shadowResolution: 1\n    shadowProjection: 1\n    shadowCascades: 2\n    shadowDistance: 40\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 1\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 1\n    billboardsFaceCameraPosition: 1\n    vSyncCount: 1\n    lodBias: 1\n    maximumLODLevel: 0\n    streamingMipmapsActive: 0\n    streamingMipmapsAddAllCameras: 1\n    streamingMipmapsMemoryBudget: 512\n    streamingMipmapsRenderersPerFrame: 512\n    streamingMipmapsMaxLevelReduction: 2\n    streamingMipmapsMaxFileIORequests: 1024\n    particleRaycastBudget: 256\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    asyncUploadPersistentBuffer: 1\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Very High\n    pixelLightCount: 3\n    shadows: 2\n    shadowResolution: 2\n    shadowProjection: 1\n    shadowCascades: 2\n    shadowDistance: 70\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 4\n    textureQuality: 0\n    anisotropicTextures: 2\n    antiAliasing: 2\n    softParticles: 1\n    softVegetation: 1\n    realtimeReflectionProbes: 1\n    billboardsFaceCameraPosition: 1\n    vSyncCount: 1\n    lodBias: 1.5\n    maximumLODLevel: 0\n    streamingMipmapsActive: 0\n    streamingMipmapsAddAllCameras: 1\n    streamingMipmapsMemoryBudget: 512\n    streamingMipmapsRenderersPerFrame: 512\n    streamingMipmapsMaxLevelReduction: 2\n    streamingMipmapsMaxFileIORequests: 1024\n    particleRaycastBudget: 1024\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    asyncUploadPersistentBuffer: 1\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Ultra\n    pixelLightCount: 4\n    shadows: 2\n    shadowResolution: 2\n    shadowProjection: 1\n    shadowCascades: 4\n    shadowDistance: 150\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 4\n    textureQuality: 0\n    anisotropicTextures: 2\n    antiAliasing: 2\n    softParticles: 1\n    softVegetation: 1\n    realtimeReflectionProbes: 1\n    billboardsFaceCameraPosition: 1\n    vSyncCount: 1\n    lodBias: 2\n    maximumLODLevel: 0\n    streamingMipmapsActive: 0\n    streamingMipmapsAddAllCameras: 1\n    streamingMipmapsMemoryBudget: 512\n    streamingMipmapsRenderersPerFrame: 512\n    streamingMipmapsMaxLevelReduction: 2\n    streamingMipmapsMaxFileIORequests: 1024\n    particleRaycastBudget: 4096\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    asyncUploadPersistentBuffer: 1\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  m_PerPlatformDefaultQuality:\n    Android: 2\n    Lumin: 5\n    Nintendo 3DS: 5\n    Nintendo Switch: 5\n    PS4: 5\n    PSP2: 2\n    Standalone: 5\n    WebGL: 3\n    Windows Store Apps: 5\n    XboxOne: 5\n    iPhone: 2\n    tvOS: 2\n"
  },
  {
    "path": "csharp/ProjectSettings/TagManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!78 &1\nTagManager:\n  serializedVersion: 2\n  tags: []\n  layers:\n  - Default\n  - TransparentFX\n  - Ignore Raycast\n  - \n  - Water\n  - UI\n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  m_SortingLayers:\n  - name: Default\n    uniqueID: 0\n    locked: 0\n"
  },
  {
    "path": "csharp/ProjectSettings/TimeManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!5 &1\nTimeManager:\n  m_ObjectHideFlags: 0\n  Fixed Timestep: 0.02\n  Maximum Allowed Timestep: 0.33333334\n  m_TimeScale: 1\n  Maximum Particle Timestep: 0.03\n"
  },
  {
    "path": "csharp/ProjectSettings/UnityConnectSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!310 &1\nUnityConnectSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 1\n  m_Enabled: 0\n  m_TestMode: 0\n  m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events\n  m_EventUrl: https://cdp.cloud.unity3d.com/v1/events\n  m_ConfigUrl: https://config.uca.cloud.unity3d.com\n  m_TestInitMode: 0\n  CrashReportingSettings:\n    m_EventUrl: https://perf-events.cloud.unity3d.com\n    m_Enabled: 0\n    m_LogBufferSize: 10\n    m_CaptureEditorExceptions: 1\n  UnityPurchasingSettings:\n    m_Enabled: 0\n    m_TestMode: 0\n  UnityAnalyticsSettings:\n    m_Enabled: 0\n    m_TestMode: 0\n    m_InitializeOnStartup: 1\n  UnityAdsSettings:\n    m_Enabled: 0\n    m_InitializeOnStartup: 1\n    m_TestMode: 0\n    m_IosGameId: \n    m_AndroidGameId: \n    m_GameIds: {}\n    m_GameId: \n  PerformanceReportingSettings:\n    m_Enabled: 0\n"
  },
  {
    "path": "csharp/ProjectSettings/VFXManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!937362698 &1\nVFXManager:\n  m_ObjectHideFlags: 0\n  m_IndirectShader: {fileID: 0}\n  m_CopyBufferShader: {fileID: 0}\n  m_SortShader: {fileID: 0}\n  m_RenderPipeSettingsPath: \n  m_FixedTimeStep: 0.016666668\n  m_MaxDeltaTime: 0.05\n"
  },
  {
    "path": "csharp/ProjectSettings/XRSettings.asset",
    "content": "{\n    \"m_SettingKeys\": [\n        \"VR Device Disabled\",\n        \"VR Device User Alert\"\n    ],\n    \"m_SettingValues\": [\n        \"False\",\n        \"False\"\n    ]\n}"
  },
  {
    "path": "csharp/README.md",
    "content": "# Vaser Unity\n\nVaser Unity is a continuation of [VASE renderer](https://github.com/tyt2y3/vaserenderer), reimplementing the original C++ algorithm in C# while taking advantage of fragment shader to reduce triangle count. A major difference is that \"feathering\" no longer requires extra vertices. So the following patch requires only 4 vertices (instead of 8 in the C++ version):\n\n![Screenshort](Docs/Screenshot%20at%202019-12-02%2000-44-00.png)\n\nMore screenshots:\n\n![Screenshot](Docs/Screenshot%20at%202019-12-11%2001-11-10.png)\n![Screenshot](Docs/Screenshot%20at%202019-12-15%2003-31-00.png)\n![Screenshot](Docs/Screenshot%20at%202019-12-30%2017-36-00.png)\n\n## Features\n\n- Per vertex coloring and weighting\n- Linear gradient along curve\n- Brush-like feathering effects\n- Premium quality anti-aliasing\n- Various caps and joints\n\nMore screenshots can be found under [Docs](Docs).\n\n## Status\n\nCurrent implementation is still experimental, may be buggy and the API is subject to change. While this library can be used standalone, it may eventually be integrated into a larger framework.\n"
  },
  {
    "path": "docs/API.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<head>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n</head>\n<body>\n\t<div class='vaser_wrap'>\n\t\t<div class='bar'>VASEr: better stroke rendering</div>\n\t\t<div class='article_name'>\n\t\t\t<h2>VASEr API reference</h2>\n\t\t</div>\n\t<h2>Overview</h2>\n\t\t<h3>API design</h3>\n<p>VASEr functions have no state, all stateful operations are put into the class <code>renderer</code>. Thus you need to pass many parameters on each VASEr call, and these parameters are stored in structures. Normally if you do not know what options to set, just put <code>0</code> at the parameter <code>polyline_opt*</code>. VASEr ensures empty parameter <code>polyline_opt* options=0</code> <b>and</b> empty structure <code>polyline_opt opt={0};</code> are default options and will not cause any error.\n</p>\n\t\t<h3>Index</h3>\n<div class='textblock'>\n\tclass<br>\n\t<a href='#renderer'>renderer</a><br>\n\tstructures<br>\n\t<a href='#gradient'>gradient</a><br>\n\t<a href='#image'>Image</a><br>\n\t<a href='#tessellator_opt'>tessellator_opt</a><br>\n\t<a href='#polyline'>polyline_opt</a><br>\n\t<a href='#polybezier'>polybezier_opt</a><br>\n\tfunctions<br>\n\t<a href='#polyline'>polyline</a><br>\n\t<a href='#segment'>segment</a><br>\n\t<a href='#polybezier'>polybezier</a><br>\n</div>\n\n\t<h2>Details</h2>\n<table class='api_table' style='width:100%;'>\n\n\t<tr><td>\n\t<a name='renderer'></a><h3>class renderer</h3>\n<pre>\nclass renderer\n{\npublic:\n\tstatic void init();\n\tstatic void before();\n\tstatic void after();\n\tstatic Image get_image();\n};\n</pre>\n\t<p>As stated, all stateful operations are put into the class <code>renderer</code>. <code>init()</code> is intended to set up shaders etc. When calling <code>init()</code>, the open gl context is assumed to be set up by the user already. Call <code>renderer::before()</code> and <code>renderer::after()</code> before and after rendering. For more information, look at <a href='getting_started.html'>getting started</a>.</p>\n\t<p><code>get_image()</code> captures an image of the current render context, in rgba 32 bit format. the (x,y) pixel (r,g,b,a) is at <code>(image.buf[y*w*b+x*b],image.buf[y*w*b+x*b+1],image.buf[y*w*b+x*b+2],image.buf[y*w*b+x*b+3])</code>, where b=4 and w=image.width.</p>\n\t</td></tr><!--renderer-->\n\n\t<tr><td>\n\t<a name='gradient'></a><h3>struct gradient</h3>\n<pre>\nstruct gradient_stop\n{\n\tdouble t; //position\n\tchar type; //GS_xx\n\tunion\n\t{\n\t\tColor color;\n\t\tdouble weight;\n\t};\n};\n\tconst char GS_none  =0;\n\tconst char GS_rgba  =1;\n\tconst char GS_rgb   =2; //rgb only\n\tconst char GS_alpha =3;\n\tconst char GS_weight=4;\nstruct gradient\n{\n\tgradient_stop* stops; //array must be sorted in ascending order of t\n\tint length; //number of stops\n\tchar unit; //use_GD_XX\n};\n\tconst char GD_ratio =0; //default\n\tconst char GD_length=1;\n</pre>\n<p>A gradient is an array of stops that specify properties at different positions in a one dimensional space. The properties can be color or weight, and the space parameter (unit) can be the length along a curve. The default parameter is ratio, that frankly speaking t=0.5 is midway along the curve.</p>\n<p>Be aware of the union, which means each stop can only hold one type of information, which must be explicitly specified by <code>gradient_stop.type</code>. If the type is unspecified, the stop is ignored.</p>\n<p>Stops of each type must be sorted in ascending order of t, but it is okay for say stops of weight come after stops of color. If there is only one stop of a type, then it is considered constant everywhere. If there is no stop of a type, then no change will be applied.</p>\n<p>For a concrete example, look at <a href='#polybezier'>polybezier</a>.</p>\n\t</td></tr><!--gradient-->\n\n\t<tr><td>\n\t<a name='image'></a><h3>struct Image</h3>\n<pre>\nstruct Image\n{\n\tunsigned char* buf; //must **free** buffer manually\n\tshort width;\n\tshort height;\n};\n</pre>\n\tthe <code>unsigned char* buf</code> must be freed manually as in malloc/free\n\t</td></tr><!--Image-->\n\n\t<tr><td>\n\t<a name='tessellator_opt'></a><h3>struct tessellator_opt</h3>\n<pre>\nstruct tessellator_opt\n{\n\t//set the whole structure to 0 will give default options\n\tbool triangulation;\n\tchar parts; //use TS_xx\n\tbool tessellate_only;\n\tvoid* holder; //used as (VASErin::vertex_array_holder*) if tessellate_only is true\n};\n\t//for tessellator_opt.parts\n\tconst char TS_core_fade =0; //default\n\tconst char TS_core      =1;\n\tconst char TS_outer_fade=2;\n\tconst char TS_inner_fade=3;\n</pre>\n<p>a <code>polyline_opt</code> contains a pointer to <code>tessellator_opt</code> which can be used to control tessellating behavior. in this version, <code>parts</code> is unimplemented. if you don't need this control,, setting <code>polyline_opt.tess</code> to zero pointer is okay.</p>\n\t</td></tr><!--tessellator_opt-->\n\n\t<tr><td>\n\t<a name='polyline'></a><h3>polyline()</h3>\n<pre lang=\"C++\">void polyline(\n\tVec2* P,       //array of point of a polyline\n\tColor* C,      //array of color\n\tdouble* weight,//array of weight\n\tint length,    //length of the array\n\tpolyline_opt* options) //extra options\n</pre>\n\tAll arrays must be of the same size otherwise memory error will occur.<br>\n\tThere are 3 other variants if you do not need varying color or weight.\n<pre lang=\"C++\">\n//constant color and weight\nvoid polyline( const Vec2*, Color, double W, int length, const polyline_opt*);\n//constant weight\nvoid polyline( const Vec2*, const Color*, double W, int length, const polyline_opt*);\n//constant color\nvoid polyline( const Vec2*, Color, const double* W, int length, const polyline_opt*);\n</pre>\n\n\t<h4>Options</h4>\n<pre lang=\"C++\">\nstruct polyline_opt\n{\n\t//set the whole structure to 0 will give default options\n\tconst tessellator_opt* tess;\n\tchar joint; //use PLJ_xx\n\tchar cap;   //use PLC_xx\n\tbool feather;\n\t\tdouble feathering;\n\t\tbool no_feather_at_cap;\n\t\tbool no_feather_at_core;\n};\n\t//for polyline_opt.joint\n\tconst char PLJ_miter =0; //default\n\tconst char PLJ_bevel =1;\n\tconst char PLJ_round =2;\n\t//for polyline_opt.cap\n\tconst char PLC_butt  =0; //default\n\tconst char PLC_round =1;\n\tconst char PLC_square=2;\n\tconst char PLC_rect  =3;\n\tconst char PLC_both  =0; //default\n\tconst char PLC_first =10;\n\tconst char PLC_last  =20;\n\tconst char PLC_none  =30;\n</pre>\n\t\n\t<code>\n\t\t<table>\n\t\t\t<caption>opt.joint</caption>\n\t\t\t<tr><td>PLJ_miter<br><img src='sample_images/PLJ_miter.png' /></td>\n\t\t\t\t<td>PLJ_bevel<br><img src='sample_images/PLJ_bevel.png' /></td>\n\t\t\t\t<td>PLJ_round<br><img src='sample_images/PLJ_round.png' /></td>\n\t\t\t</tr>\n\t\t</table>\n\t\t<br>\n\t\t<table>\n\t\t\t<caption>opt.cap</caption>\n\t\t\t<tr><td>PLC_butt<br><img src='sample_images/PLC_butt.png' /></td>\n\t\t\t\t<td>PLC_round<br><img src='sample_images/PLC_round.png' /></td>\n\t\t\t\t<td>PLC_square<br><img src='sample_images/PLC_square.png' /></td>\n\t\t\t</tr>\n\t\t\t<tr><td colspan=3 style='text-align:left;'>PLC_rect<br>\n\t\t\t\tPLC_rect is related to feathering. PLC_rect puts the fade polygon out of the end points of a polyline.<br>When feather=false, PLC_rect looks very close to PLC_butt. The difference is only obvious at high value of feathering.\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>PLC_rect<br><img src='sample_images/PLC_rect.png' /></td>\n\t\t\t\t<td colspan=2>PLC_butt<br><img src='sample_images/PLC_butt_high_feathering.png' /></td>\n\t\t\t</tr>\n\t\t\t<tr><td colspan=3 style='text-align:left;'>There are modifiers and can be numerically added together to the value to control rendering of cap parts. it is necessary if you need to join segments yourself.</td>\n\t\t\t</tr>\n\t\t\t<tr><td>PLC_butt+PLC_both<br><img src='sample_images/PLC_both.png' /></td>\n\t\t\t\t<td>PLC_butt+PLC_first<br><img src='sample_images/PLC_first.png' /></td>\n\t\t\t\t<td>PLC_butt+PLC_none<br><img src='sample_images/PLC_none.png' /></td>\n\t\t\t</tr>\n\t\t</table>\n\t\t<br>\n\t\t<table>\n\t\t\t<caption>opt.feather, opt.feathering, opt.no_feather_at_cap, opt.no_feather_at_core</caption>\n\t\t\t<tr><td style='text-align:left;'>feathering is a multiplier to the magnitude of the outset-fade polygon. do not set it to lower than 1.0 .<br>\n\t\t\tfeathering is unique to VASEr. A feathered polyline with round cap and round joint can mimic the feel of an air brush stroke.\n\t\t\t</td>\n\t\t\t<td><img src='sample_images/PL_mimic_airbrush.png'/></td>\n\t\t</tr>\n\t\t</table>\n\t\t<table>\n\t\t\t<tr>\t\n\t\t\t\t<td>opt.feather = false;<br><img src='sample_images/PLJ_miter.png' /></td>\n\t\t\t\t\n\t\t\t\t<td>opt.feather = true;<br>opt.feathering = 8.0;<br>\n\t\t\t\t<img src='sample_images/PL_feathering_8.png' />\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>opt.feather = true;<br>\n\t\t\t\topt.feathering = 8.0;<br>\n\t\t\t\topt.no_feather_at_cap = true;<br>\n\t\t\t\topt.no_feather_at_core = false;<br>\n\t\t\t\t<img src='sample_images/PL_no_feather_at_cap.png' />\n\t\t\t\t</td>\n\t\t\t\t\n\t\t\t\t<td>opt.feather = true;<br>\n\t\t\t\topt.feathering = 8.0;<br>\n\t\t\t\topt.no_feather_at_cap = false;<br>\n\t\t\t\topt.no_feather_at_core = true;<br>\n\t\t\t\t<img src='sample_images/PL_no_feather_at_core.png' />\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr><td colspan=2>\n\t\t\t\tremarks: no_feather_at_cap only affects cap type PLC_butt,\n\t\t\t\tPLC_square and PLC_rect .\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</table>\n\t\t<br>\n\t</code>\n\n\t\t<h4>Usage</h4>\n\t<div style='float:right'>\n\t<img src='sample_images/polyline_4.png' /><br>\n\t<img src='sample_images/polyline_3.png' />\n\t</div>\n\t<pre lang=\"C++\">\nvoid sample_polyline1()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor col={ 0.5, 0.8, 1.0, 1};\n\tpolyline( AP, col, 8.0, size_of_AP, 0);\n}\nvoid sample_polyline2()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor AC[size_of_AP];\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[0]=col;}\n\t\t{ Color col={.8,.8, 0, 0.5}; AC[1]=col;}\n\t\t{ Color col={ 0, 0, 1, 0.5}; AC[2]=col;}\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[3]=col;}\n\tdouble AW[size_of_AP];\n\t\tAW[0] = 1.0;\n\t\tAW[1] = 15.0;\n\t\tAW[2] = 15.0;\n\t\tAW[3] = 1.0;\n\t\n\tpolyline_opt opt={0};\n\tpolyline( AP, AC, AW, size_of_AP, &opt);\n}\n</pre>\n\t<div style='clear:both'></div>\n\nexample program is at <code>samples/samples.cpp</code> under VASEr package.\n\n\t\t<h4>Notes</h4>\nVarying color is stable but will cause overdraw at degenerated cases.<br>\nVarying weight is stable most of the time but will go wild when extreme case occur.<br>\nThis version of polyline is improved to render dense vertices for curves.<br>\n\n\t\t<h4>Further work</h4>\nAfter solving the above mentioned problems, can provide the choice between color blending profiles, possibly 'hard' and 'soft'. and besides gradient, provide texture along path.\n\t</td></tr><!--polyline-->\n\t\n\t<tr><td>\n\t\t<a name='segment'></a><h3>segment()</h3>\n\t\t<pre lang=\"C++\">\nvoid segment( Vec2,  Vec2,\n              Color, Color,\n              double, double, //weight\n              const polyline_opt*); //same use as polyline\nvoid segment( Vec2, Vec2, Color, double W, const polyline_opt*); //constant color and weight\nvoid segment( Vec2, Vec2, Color, Color, double W, const polyline_opt*); //constant weight\nvoid segment( Vec2, Vec2, Color, double W1, double W2, const polyline_opt*); //const color\n</pre>\n\t<code>segment()</code> is merely a wrapper over <code>polyline()</code>. There are also three variants.\n\t\t\t<h4>Usage</h4>\n\t\t\t<pre lang=\"C++\">\nvoid sample_spectrum()\n{\n\tfor ( int i=0; i < 20; i++)\n\t{\n\t\tVec2  P1 = { 5+29.7*i, 187};\n\t\tVec2  P2 = { 35+29.7*i, 8};\n\t\tColor C1 = { 1.0,0.0,0.5, 1.0};\n\t\tColor C2 = { 0.5,0.0,1.0, 1.0};\n\t\tdouble W1= 0.3*(i+1);\n\t\tdouble W2= W1;\n\t\t\n\t\tsegment(P1,P2, C1,C2, W1,W2, 0);\n\t}\n}\n\nvoid sample_radial_spectrum()\n{\n\tfor ( double ag=0, i=0; ag < 2*vaser_pi-0.1; ag+=vaser_pi/12, i+=1)\n\t{\n\t\tdouble r1 = 30.0;\n\t\tdouble r2 = 90.0;\n\t\t\n\t\tdouble tx2=r2*cos(ag);\n\t\tdouble ty2=r2*sin(ag);\n\t\tdouble tx1=r1*cos(ag);\n\t\tdouble ty1=r1*sin(ag);\n\t\tdouble Ox = 120;\n\t\tdouble Oy = 194+97;\n\t\t\n\t\tVec2  P1 = { Ox+tx1,Oy-ty1};\n\t\tVec2  P2 = { Ox+tx2,Oy-ty2};\n\t\tColor C1 = { 1.0,0.0,0.5, 1.0};\n\t\tColor C2 = { 0.5,0.0,1.0, 1.0};\n\t\tdouble W1= 0.3*(i+1);\n\t\tdouble W2= W1;\n\t\t\n\t\tsegment(P1,P2, C1,C2, W1,W2, 0);\n\t}\n}\n</pre>\n\texample program is at <code>samples/samples.cpp<</code> under VASEr package. In the below spectrums, each segment is 0.3 pixel heavier than the previous segment, demonstrating sub pixel accuracy and anti aliasing quality of VASEr.\n\t<img src='sample_images/segment.png'/>\n\t</td></tr><!--segment-->\n\t\n\t<tr><td>\n\t<a name='polybezier'></a><h3>polybezier()</h3>\n\t<pre>\nvoid polybezier( const Vec2* P, const gradient*, int length, const polybezier_opt*);\nvoid polybezier( const Vec2* P, Color, double W, int length, const polybezier_opt*);\n\t</pre>\nThe <code></code> <code>P</code> is an array of control points. The evaluated curve is continuous, with adjacent bezier sections sharing a common control point. so n cubic bezier sections require 3n+1 points. A pointer to <a href='#gradient'>gradient</a> or constant color and width can be passed in.\n\t\t<h4>Options</h4>\n<pre>\nstruct polybezier_opt\n{\n\t//set the whole structure to 0 will give default options\n\tconst polyline_opt* poly;\n};\n</pre>\nUnder the hood, bezier curves will be evaluated, and the set of points will be sent to <code>polyline()</code> for rendering, using <code>const polyline_opt* poly</code> as options if specified.\n\t\t<h4>Usage</h4>\n\t<div style='float:right'>\n\t<img src='sample_images/bezier_3.png' /><br>\n\t<img src='sample_images/bezier_4.png' />\n\t</div>\n\t<pre>\nvoid sample_bezier1()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor col={ 0.5, 0.8, 1.0, 1};\n\tpolybezier( AP, col, 12.0, size_of_AP, 0);\n}\nvoid sample_bezier2()\n{\n\tint size_of_AP=4;\n\tVec2 AP[size_of_AP];\n\t\tAP[0].x=200; AP[0].y=50;\n\t\tAP[1].x=100; AP[1].y=150;\n\t\tAP[2].x=300; AP[2].y=150;\n\t\tAP[3].x=200; AP[3].y=250;\n\tColor AC[size_of_AP];\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[0]=col;}\n\t\t{ Color col={.8,.8, 0, 0.5}; AC[1]=col;}\n\t\t{ Color col={ 0, 0, 1, 0.5}; AC[2]=col;}\n\t\t{ Color col={1 , 0, 0, 0.5}; AC[3]=col;}\n\tdouble AW[size_of_AP];\n\t\tAW[0] = 1.0;\n\t\tAW[1] = 15.0;\n\t\tAW[2] = 15.0;\n\t\tAW[3] = 1.0;\n\tgradient grad = {0};\n\t\tgradient_stop stop[10] = {0};\n\t\tgrad.stops = stop;\n\t\tgrad.length = 10;\n\t\tstop[0].t = 0.00; stop[0].type = GS_rgba; stop[0].color = AC[0];\n\t\tstop[1].t = 0.50; stop[1].type = GS_rgba; stop[1].color = AC[2];\n\t\tstop[2].t = 1.00; stop[2].type = GS_rgba; stop[2].color = AC[3];\n\t\tstop[3].t = 0.00; stop[3].type = GS_weight; stop[3].weight = AW[0];\n\t\tstop[4].t = 0.33; stop[4].type = GS_weight; stop[4].weight = AW[1];\n\t\tstop[5].t = 0.66; stop[5].type = GS_weight; stop[5].weight = AW[2];\n\t\tstop[6].t = 1.00; stop[6].type = GS_weight; stop[6].weight = AW[3];\n\tpolybezier( AP, &grad, size_of_AP, 0);\n}\n</pre>\n\t<div style='clear:both'></div>\nAgain, example program is at <code>samples/samples.cpp</code>.\n\t</td></tr><!--polybezier-->\n\t\n\t<tr><td>\n\t<a name='template'></a><h3></h3>\n\t</td></tr><!--template-->\n\n</table><!--table of API-->\n</body>\n"
  },
  {
    "path": "docs/getting_started.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<head>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n</head>\n<body>\n\t<div class='vaser_wrap'>\n\t\t<div class='bar'>VASEr: better stroke rendering</div>\n\t\t<div class='article_name'>\n\t\t\t<h2>VASEr getting started</h2>\n\t\t</div>\n\t<h3>The role of a renderer</h3>\n<p>To use VASEr properly, you need to understand what role does VASEr play in the rendering pipeline. Suppose your application has a 2D rendering pipeline like:\n</p>\n\n<table>\n<tr><td>transformation</td></tr>\n<tr><td>clipping</td></tr>\n<tr class='box_redlight'>\n\t<td>primitives generation<br>\n\t\t<code>glDrawArrays();</code>\n\t\tor\n\t\t<code>glBegin(); glEnd();</code>\n\t</td>\n</tr>\n<tr><td>composition</td></tr>\n</table>\n\n<p>VASEr merely takes care the primitives generation part (redlighted). Call <code>renderer::before()</code> and <code>renderer::after()</code> before and after rendering. VASEr API is namespaced <code>VASEr</code>.\n</p>\n\n<div class='box_tips'>\nExample: <a onclick='box_tips_switchsize(this)'>hide</a><br>\nHow to correctly set gl states for VASEr..\n<p>Suppose you have a helloworld application that only renders a line segment in draw():</p>\n<pre lang=\"C++\">\nvoid draw()\n{\n\tglMatrixMode(GL_PROJECTION);\n\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglOrtho( 0,context_width,context_height,0,0.0f,100.0f);\n\t\t<b>glLineWidth(2.0);\n\t\tglBegin(GL_LINES);\n\t\t\tglColor4f(1,0,0.5, 1);\n\t\t\tglVertex2f(10,100);\n\t\t\tglColor4f(0.5,0,1, 1);\n\t\t\tglVertex2f(100,300);\n\t\tglEnd();\n\t\t//other drawings</b>\n\tglMatrixMode(GL_PROJECTION);\n\tglPopMatrix();\n}</pre>\nthen change to:\n<pre lang=\"C++\">\nvoid draw()\n{\n\t<b>using namespace VASEr;</b>\n\tglMatrixMode(GL_PROJECTION);\n\tglPushMatrix();\n\t\tglLoadIdentity();\n\t\tglOrtho( 0,context_width,context_height,0,0.0f,100.0f);\n\t\t<b>renderer::before();\n\t\t\t{\tVec2  P1 = {10,100};\n\t\t\t\tVec2  P2 = {100,300};\n\t\t\t\tColor C1 = {1,0,0.5, 1};\n\t\t\t\tColor C2 = {0.5,0,1, 1};\n\t\t\t\tdouble W1= 2.0;\n\t\t\t\tdouble W2= W1;\n\t\t\t\tsegment(P1,P2, C1,C2, W1,W2, 0);\n\t\t\t}\n\t\t\t//other VASEr renderings\n\t\trenderer::after();</b>\n\tglMatrixMode(GL_PROJECTION);\n\tglPopMatrix();\n}</pre>\n</div> <!--class='box_tips' -->\n\n\t<h3>include and compile</h3>\n<p>Provide these structs to VASEr <b>before</b> include by:</p>\n<pre lang=\"C++\">\nnamespace VASEr\n{\n\tstruct Vec2 { double x,y;};\n\tstruct Color { float r,g,b,a;};\n} </pre>\n<b>or</b>\n<pre lang=\"C++\">\nnamespace VASEr\n{\n\ttypedef your_vec2 Vec2;\n\ttypedef your_color Color;\n} </pre>\n<p>then <code>#include \"vaser.h\"</code>. There is only one file to compile, <code>vaser.cpp</code>. It is a unity build; no make file, no hassle. Alternatively, <code>#include \"vaser.cpp\"</code> just works, because implementation is namespaced in <code>VASErin</code> to prevent collision.</p>\n<p>defining a Vec2 with <code>float</code> precision may work but may lead to undesirable precision lost, because internally VASEr use <code>double</code> for floating point operations.</p>\n<script>\n/*\n(function()\n{\t//init\n\tvar tips_list = document.getElementsByClassName('box_tips');\n\tfor ( var i=0; i<tips_list.length; i++)\n\t{\n\t\tvar a_list = tips_list[i].getElementsByTagName('a');\n\t\tif ( a_list.length > 0)\n\t\t{\n\t\t\tvar instr = a_list[0].innerHTML;\n\t\t\tif ( instr.length==4)\n\t\t\t\tif ( instr=='hide')\n\t\t\t\t\tbox_tips_switchsize( a_list[0]);\n\t\t}\n\t}\n}());*/\nfunction box_tips_switchsize(obj)\n{\n\tif ( obj.innerHTML=='show')\n\t{\t//show the box tips\n\t\tobj.parentNode.style.height='auto';\n\t\tobj.innerHTML='hide';\n\t}\n\telse\n\t{\t//hide it\n\t\tobj.parentNode.style.height='140px';\n\t\tobj.innerHTML='show';\n\t}\n}\n</script>\n</body>\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<head>\n\t<title>VASE renderer: better stroke rendering</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n</head>\n<body>\n\t<div class='vaser_wrap'>\n\t\t<div class='bar'>VASEr: better stroke rendering</div>\n\t\t<div class='article_name'>\n\t\t\t<h1>VASE<span style='line-height:40px;'> renderer</span></h1>\n\t\t</div>\n<h2>About</h2>\n<p>VASE renderer version 0.42 (VASEr 0.42) is a tessellating library for rendering high quality vector graphics. It is an attempt to address unconventional features in 2d graphics. It is intended for OpenGL 1.1+, but much of the code is API independent C++.</p>\n \n<h2>Unconventional features</h2>\n\t<h3>Per vertex coloring and weighting</h3>\n<div style='position:relative; overflow:hidden; height:220px; float:left;'><img style='position:relative; top:-40px;' src='sample_images/polyline_3.png' /></div>\n<br><br>VASEr revolutionarily lets you control the color and thickness at each vertex for a polyline. This feature is unseen on any commonly available graphics library.\n\t<div style='clear:both'></div>\n\n\t<h3>Linear gradient along curve</h3>\n<div style='position:relative; overflow:hidden; height:200px; float:right;'><img style='position:relative; top:-60px;' src='sample_images/bezier_2.png' /></div>\n<br>A similar feature is also available to curves. Because there are so many vertices on a curve that it is impractical to specify data on each point, VASEr lets you define a linear gradient of color and thickness along the length of a curve, giving you two more degrees of expressive freedom.\n\t<div style='clear:both'></div>\n\n\t<h3>Feathering for brush like effects</h3>\n<img src='sample_images/bezier_3.png' style='float:left;' />\n<img src='sample_images/bezier_4.png' style='float:left;' />\n<img src='sample_images/bezier_5.png' style='float:left;' />\n<br>VASEr must use an outsetting polygon anyway, so it is free (as in computational cost) to scale it up, and it is called feathering in VASEr.\n\t<div style='clear:both'></div>\n\n\t<h3>Premium quality anti aliasing</h3>\n\t<div style='background:#333; margin: 10px; border:5px solid #AAA; float:left; height: 150px; overflow:hidden;'>\n\t<img src='sample_images/fade_intro_1.png' />\n\t<img src='sample_images/fade_intro_2.png' />\n\t<img src='sample_images/fade_intro_3.png' />\n\t<img src='sample_images/fade_intro_4.png' />\n\t</div>\n<br><br>From left to right: raw polygon without anti aliasing, <br>\nanti aliased with outset fade polygon, <br>\nexaggerated outsetting polygon with wireframe, <br>\nsame as left without wireframe.\n\t<div style='clear:both'></div>\n<p>Outset-fade polygon is the high quality, fast, portable, consistent and hassle free technique for anti aliasing. The difficulties are to calibrate the outsetting distance to achieve believable result and to perform tedious tessellation. Luckily VASEr did this for you.</p>\n<p>Below are line rendering comparison between VASEr and Cairo, click flip to compare:</p>\n<div>\n\t<div id='ab_va'>VASEr<br><img src='sample_images/ab_vaser_line_thickness1.png' /></div>\n\t<div id='ab_ca' style='display:none;'>Cairo<br><img src='sample_images/ab_cairo_line_thickness.png' /></div>\n\t<button onclick=\"\n\tif ( !document.getElementById('ab_va').style.display) {\n\tdocument.getElementById('ab_va').style.display='none';\n\tdocument.getElementById('ab_ca').style.display='';\n\t} else {\n\tdocument.getElementById('ab_va').style.display='';\n\tdocument.getElementById('ab_ca').style.display='none';\n\t}\n\t\" style=\"width: 606px;\">flip</button>\n</div>\n<p>between VASEr and AGG:</p>\n<div>\n\t<div id='a_va'>VASEr<br><img src='sample_images/a_vaser_line_thickness.png' /></div>\n\t<div id='a_agg' style='display:none;'>AGG<br><img src='sample_images/agg_line_thickness.png' /></div>\n\t<button onclick=\"\n\tif ( !document.getElementById('a_va').style.display) {\n\tdocument.getElementById('a_va').style.display='none';\n\tdocument.getElementById('a_agg').style.display='';\n\t} else {\n\tdocument.getElementById('a_va').style.display='';\n\tdocument.getElementById('a_agg').style.display='none';\n\t}\n\t\" style=\"width: 606px;\">flip</button>\n</div>\n<p>There is small difference, but it is more a matter of taste than correctness. In terms of clarity VASEr is like between Cairo and AGG, and VASEr is the most crisp among the three. VASEr even do pixel alignment to 1px completely horizontal or vertical lines.</p>\n<p>The flaw is, because the outsetting distance is resolution dependent, while the triangulation is unaffected under rotational transformation, fidelity will be lost under scale and sheer.</p>\n\n\t<h3>Performant</h3>\n<div style='position:relative; overflow:hidden; width:300px; height:250px; float:left;'><img style='position:relative; top:-20px; left:-50px;' src='sample_images/polyline_5.png' /></div>\n<p>VASEr is for real time rendering. Tessellation is done on CPU and the triangles are then sent off to GPU for rasterization. VASEr is fast regardless of the rendering resolution.</p>\n<p>Can tessellation be accelerated by GPU? Probably, but I have no plan to do so. The performance enhancement strategy of VASEr is adaptation, that means doing expensive joint processing only when necessary and unperceivablely switch to approximation for thin lines and curves. The decision requires some global information and result in lots of branching, which makes it harder to write GPU programs. But after all, correctness is the first priority and performance is the last.</p>\n\t<div style='clear:both'></div>\n\n<h2>\"Next generation\" 2d graphics?</h2>\n<p>Q: Who needs \"Per vertex coloring and weighting\", \"Linear gradient along curve\" or \"Feathering for brush like effects\" in vector graphics? A: Same answer as \"who needs typesetting and beautiful fonts on their computers?\".</p>\n<p>Q: These effects can be achieved by vector graphics package XXX using tool AAA then effect BBB, so who needs VASE renderer? A: Arguably true. But the fundamental change is <b>making vertex a three dimensional entity</b> (position, color, weight), and to <b>supercharge the intrinsic properties of strokes</b>. The idea is, it should be <i>natural</i> to apply colors and thickness to a stroke.</p>\n\n<h2>Status</h2>\n<p>VASEr is production ready for conventional use. The unconventional features are largely usable for normal circumstances, but may have defects at extreme cases. In particular, varying thickness is undesirable at acute angles and when weight difference is large. So it is not bullet proof yet. The tessellation code is tediously written for each point for each triangle for each circumstance, because a general outsetting and tessellating algorithm is doomed to be slow.</p>\n<p>Currently VASEr is only a programming library and is not used in any vector graphics tool. I do have a plan (a plan only) to develop associated tools.</p>\n<p>VASEr is not (yet) a general purpose graphics library like Cairo, its feature is still very limited. It has been developed slowly over the years. I always wanted to develop VASEr into a general purpose graphics library, but just do not have the time. It takes more effort than you may think to develop a rendering routine. Any comment, bug report or patch is greatly appreciated. In any case, sending me any form of encouragement (e.g. great work, thank you, donation) will help.</p>\n\n<h2>Documentation</h2>\n<div class='textblock'>\n\t<a href='getting_started.html'>getting started</a><br>\n\t<a href='API.html'>API reference</a><br>\n\tArticles<br>\n\t<a href='http://artgrammer.blogspot.com/2011/07/drawing-polylines-by-tessellation.html' target='_blank'>Drawing polylines by tessellation.</a><br>\n\t<a href='http://artgrammer.blogspot.hk/2011/05/drawing-nearly-perfect-2d-line-segments.html'>Drawing nearly perfect 2D line segments in OpenGL</a>\n</div>\n\n<h2>Source code</h2>\n<p><a href='https://github.com/tyt2y3/vaserenderer'>Package with documentation, source code, sample images and sample programs is on github</a>. Comments, discussions, and contributions are greatly appreciated. You can reach me via email or on github.</p>\n\n<h2>License</h2>\n\n<p>VASE renderer version 0.42 (VASEr 0.42) is licensed under The 3-Clause BSD License.</p>\n<p>Copyright (2011-2016) Tsang Hao Fung (Chris Tsang) tyt2y3@gmail.com</p>\n<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:</p>\n\n<p>1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.</p>\n\n<p>2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.</p>\n\n<p>3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.</p>\n\n<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>\n\n<p>Bezier curve subdivision code is extracted from Anti-Grain Geometry V2.4 Copyright 2002-2005 Maxim Shemanarev (McSeem) [Modified BSD License]</p>\n\n\t</div> <!--<div id='wrap'>-->\n</body>\n"
  },
  {
    "path": "docs/style.css",
    "content": "body\n{\n\tmargin: 0 auto;\n\tpadding: 0;\n\tfont: normal normal 13.8pt Palatino, \"Palatino Linotype\", Georgia, Times, \"Times New Roman\", serif;\n\tline-height: 18pt;\n\tcolor: #555;\n\tbackground: #AAA;\n}\n\n.vaser_wrap\n{\n\twidth: 900px; margin: 0 auto; padding: 0;\n\tborder-style: solid;\n\tborder-color: #FAFAFA;\n\tborder-width: 10px;\n\tbackground: #FFFFFF;\n}\n\nh1, h2, h3, h4, h5, h6\n{\n\tfont-family: Palatino, \"Palatino Linotype\", Georgia, Times, \"Times New Roman\", serif;\n\tfont-weight: normal;\n\tcolor: #777;\n\tmargin: 0;\n}\n\nh1 { font-size: 26pt; padding: 20px 0 10px 0;}\nh2 { font-size: 24pt; padding: 20px 0 10px 0;}\nh3 { font-size: 22pt; padding: 20px 0 10px 20px;}\nh4 { font-size: 20pt; padding: 15px 0 10px 100px;}\nh5 { font-size: 14pt; padding: 15px 0 10px 120px;}\nh6 { font-size: 12pt; font-weight: bold; color: #999; padding: 0 0 0 160px;}\n\na\n{\n\tcolor: #ba954f;\n\ttext-decoration: none;\n\toutline: 0;\n}\na:hover { color: #dabc83; }\na img { border: 0; }\n\nblockquote, q { quotes: none; }\n\nblockquote:before, \nblockquote:after,\nq:before,\nq:after\n{\n\tcontent: '';\n\tcontent: none;\n}\n\n/*vaser_wrap*/\n.article_name\n{\n\tpadding: 0px 0 0 0;\n\ttext-align: center;\n}\n.vaser_wrap .bar\n{\n\tbackground: #fe7f7f; /* Old browsers */\n\tbackground: -moz-linear-gradient(left,  #fe7f7f 0%, #e9d47f 33%, #807ffc 66%, #fc7f81 100%); /* FF3.6+ */\n\tbackground: -webkit-gradient(linear, left top, right top, color-stop(0%,#fe7f7f), color-stop(33%,#e9d47f), color-stop(66%,#807ffc), color-stop(100%,#fc7f81)); /* Chrome,Safari4+ */\n\tbackground: -webkit-linear-gradient(left,  #fe7f7f 0%,#e9d47f 33%,#807ffc 66%,#fc7f81 100%); /* Chrome10+,Safari5.1+ */\n\tbackground: -o-linear-gradient(left,  #fe7f7f 0%,#e9d47f 33%,#807ffc 66%,#fc7f81 100%); /* Opera 11.10+ */\n\tbackground: -ms-linear-gradient(left,  #fe7f7f 0%,#e9d47f 33%,#807ffc 66%,#fc7f81 100%); /* IE10+ */\n\tbackground: linear-gradient(to right,  #fe7f7f 0%,#e9d47f 33%,#807ffc 66%,#fc7f81 100%); /* W3C */\n\tfilter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fe7f7f', endColorstr='#fc7f81',GradientType=1 ); /* IE6-9 */\n\ttext-shadow: #000 1px 1px 8px;\n\n\tcolor: #FFF;\n\twidth: 100%;\n\theight: 30px;\n\ttext-align:center;\n\ttop: 0;\n\tfont: normal normal 15px Liberation mono, Ubuntu mono, monospace;\n\tline-height: 30px;\n}\n.vaser_wrap pre, code\n{\n\tfont: normal normal 11pt Courier New, Courier, monospace;\n\tmargin: 5px;\n}\n.vaser_wrap table\n{\n\tborder-collapse: collapse;\n\tborder-spacing: 0;\n}\n.vaser_wrap td\n{\n\tborder: 1px solid #AAAAAA;\n}\n.vaser_wrap img\n{\n    border: 0;\n}\n.vaser_wrap p\n{\n\tline-height: 18pt;\n\tmargin: 5px;\n}\n.vaser_wrap ul\n{\n\tmargin: 0;\n}\n.vaser_wrap .textblock\n{\n\tmargin-left:50px;\n}\n\n.api_table table\n{\t/*child tables*/\n\twidth:600px;\n}\n.api_table tr:nth-child(even) {}\n.api_table tr:nth-child(odd) {}\n.api_table table td\n{\n\ttext-align: center;\n}\n.api_table caption\n{\n\tfont-size:130%;\n}\n\n.para_tips\n{\n\tfont-size:11pt;\n}\n.box_tips\n{\n\tborder: 1px solid #AAA;\n\tmargin: 0 0 0 80px;\n\toverflow: hidden;\n}\n.box_redlight\n{\n\tbackground: #FFE0E0;\n}\n/*end vaser_wrap*/\n"
  }
]