Vendor SVGnest assets; remove stale submodule gitlink

This commit is contained in:
makearmy 2025-10-15 20:29:50 -04:00
parent b93d6bf3a5
commit 90cab6f8b2
65 changed files with 19417 additions and 1 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
!public/svgnest/**

@ -1 +0,0 @@
Subproject commit 1248dc21efd3f90d1aa52ba5785e27e5217ed2c9

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Jack Qiao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,5 @@
# Font Squirrel Font-face Generator Configuration File
# Upload this file to the generator to recreate the settings
# you used to create these fonts.
{"mode":"optimal","formats":["ttf","woff","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"}

View file

@ -0,0 +1,612 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="specimen_files/easytabs.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" href="specimen_files/specimen_stylesheet.css" type="text/css" charset="utf-8" />
<link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" />
<style type="text/css">
body{
font-family: 'latohairline';
}
</style>
<title>Lato Hairline Regular Specimen</title>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#container').easyTabs({defaultContent:1});
});
</script>
</head>
<body>
<div id="container">
<div id="header">
Lato Hairline Regular </div>
<ul class="tabs">
<li><a href="#specimen">Specimen</a></li>
<li><a href="#layout">Sample Layout</a></li>
<li><a href="#glyphs">Glyphs &amp; Languages</a></li>
<li><a href="#installing">Installing Webfonts</a></li>
</ul>
<div id="main_content">
<div id="specimen">
<div class="section">
<div class="grid12 firstcol">
<div class="huge">AaBb</div>
</div>
</div>
<div class="section">
<div class="glyph_range">A&#x200B;B&#x200b;C&#x200b;D&#x200b;E&#x200b;F&#x200b;G&#x200b;H&#x200b;I&#x200b;J&#x200b;K&#x200b;L&#x200b;M&#x200b;N&#x200b;O&#x200b;P&#x200b;Q&#x200b;R&#x200b;S&#x200b;T&#x200b;U&#x200b;V&#x200b;W&#x200b;X&#x200b;Y&#x200b;Z&#x200b;a&#x200b;b&#x200b;c&#x200b;d&#x200b;e&#x200b;f&#x200b;g&#x200b;h&#x200b;i&#x200b;j&#x200b;k&#x200b;l&#x200b;m&#x200b;n&#x200b;o&#x200b;p&#x200b;q&#x200b;r&#x200b;s&#x200b;t&#x200b;u&#x200b;v&#x200b;w&#x200b;x&#x200b;y&#x200b;z&#x200b;1&#x200b;2&#x200b;3&#x200b;4&#x200b;5&#x200b;6&#x200b;7&#x200b;8&#x200b;9&#x200b;0&#x200b;&amp;&#x200b;.&#x200b;,&#x200b;?&#x200b;!&#x200b;&#64;&#x200b;(&#x200b;)&#x200b;#&#x200b;$&#x200b;%&#x200b;*&#x200b;+&#x200b;-&#x200b;=&#x200b;:&#x200b;;</div>
</div>
<div class="section">
<div class="grid12 firstcol">
<table class="sample_table">
<tr><td>10</td><td class="size10">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>11</td><td class="size11">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>12</td><td class="size12">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>13</td><td class="size13">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>14</td><td class="size14">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>16</td><td class="size16">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>18</td><td class="size18">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>20</td><td class="size20">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>24</td><td class="size24">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>30</td><td class="size30">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>36</td><td class="size36">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>48</td><td class="size48">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>60</td><td class="size60">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>72</td><td class="size72">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>90</td><td class="size90">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
</table>
</div>
</div>
<div class="section" id="bodycomparison">
<div id="xheight">
<div class="fontbody">&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;body</div><div class="arialbody">body</div><div class="verdanabody">body</div><div class="georgiabody">body</div></div>
<div class="fontbody" style="z-index:1">
body<span>Lato Hairline Regular</span>
</div>
<div class="arialbody" style="z-index:1">
body<span>Arial</span>
</div>
<div class="verdanabody" style="z-index:1">
body<span>Verdana</span>
</div>
<div class="georgiabody" style="z-index:1">
body<span>Georgia</span>
</div>
</div>
<div class="section psample psample_row1" id="">
<div class="grid2 firstcol">
<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row2" id="">
<div class="grid3 firstcol">
<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid5">
<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row3" id="">
<div class="grid5 firstcol">
<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid7">
<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row4" id="">
<div class="grid12 firstcol">
<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row1 fullreverse">
<div class="grid2 firstcol">
<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample psample_row2 fullreverse">
<div class="grid3 firstcol">
<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid5">
<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample fullreverse psample_row3" id="">
<div class="grid5 firstcol">
<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid7">
<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample fullreverse psample_row4" id="" style="border-bottom: 20px #000 solid;">
<div class="grid12 firstcol">
<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
</div>
<div id="layout">
<div class="section">
<div class="grid12 firstcol">
<h1>Lorem Ipsum Dolor</h1>
<h2>Etiam porta sem malesuada magna mollis euismod</h2>
<p class="byline">By <a href="#link">Aenean Lacinia</a></p>
</div>
</div>
<div class="section">
<div class="grid8 firstcol">
<p class="large">Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
<h3>Pellentesque ornare sem</h3>
<p>Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit. </p>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. </p>
<p>Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur. </p>
<p>Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla. </p>
<h3>Cras mattis consectetur</h3>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum. </p>
<p>Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.</p>
</div>
<div class="grid4 sidebar">
<div class="box reverse">
<p class="last">Nullam quis risus eget urna mollis ornare vel eu leo. Donec ullamcorper nulla non metus auctor fringilla. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
</div>
<p class="caption">Maecenas sed diam eget risus varius.</p>
<p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean lacinia bibendum nulla sed consectetur. Nullam quis risus eget urna mollis ornare vel eu leo. </p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec ullamcorper nulla non metus auctor fringilla. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
</div>
</div>
</div>
<div id="glyphs">
<div class="section">
<div class="grid12 firstcol">
<h1>Language Support</h1>
<p>The subset of Lato Hairline Regular in this kit supports the following languages:<br />
Albanian, Basque, Breton, Chamorro, Danish, Dutch, English, Faroese, Finnish, French, Frisian, Galician, German, Icelandic, Italian, Malagasy, Norwegian, Portuguese, Spanish, Swedish </p>
<h1>Glyph Chart</h1>
<p>The subset of Lato Hairline Regular in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.</p>
<div id="glyph_chart">
<div><p>&amp;#13;</p>&#13;</div>
<div><p>&amp;#32;</p>&#32;</div>
<div><p>&amp;#33;</p>&#33;</div>
<div><p>&amp;#34;</p>&#34;</div>
<div><p>&amp;#35;</p>&#35;</div>
<div><p>&amp;#36;</p>&#36;</div>
<div><p>&amp;#37;</p>&#37;</div>
<div><p>&amp;#38;</p>&#38;</div>
<div><p>&amp;#39;</p>&#39;</div>
<div><p>&amp;#40;</p>&#40;</div>
<div><p>&amp;#41;</p>&#41;</div>
<div><p>&amp;#42;</p>&#42;</div>
<div><p>&amp;#43;</p>&#43;</div>
<div><p>&amp;#44;</p>&#44;</div>
<div><p>&amp;#45;</p>&#45;</div>
<div><p>&amp;#46;</p>&#46;</div>
<div><p>&amp;#47;</p>&#47;</div>
<div><p>&amp;#48;</p>&#48;</div>
<div><p>&amp;#49;</p>&#49;</div>
<div><p>&amp;#50;</p>&#50;</div>
<div><p>&amp;#51;</p>&#51;</div>
<div><p>&amp;#52;</p>&#52;</div>
<div><p>&amp;#53;</p>&#53;</div>
<div><p>&amp;#54;</p>&#54;</div>
<div><p>&amp;#55;</p>&#55;</div>
<div><p>&amp;#56;</p>&#56;</div>
<div><p>&amp;#57;</p>&#57;</div>
<div><p>&amp;#58;</p>&#58;</div>
<div><p>&amp;#59;</p>&#59;</div>
<div><p>&amp;#60;</p>&#60;</div>
<div><p>&amp;#61;</p>&#61;</div>
<div><p>&amp;#62;</p>&#62;</div>
<div><p>&amp;#63;</p>&#63;</div>
<div><p>&amp;#64;</p>&#64;</div>
<div><p>&amp;#65;</p>&#65;</div>
<div><p>&amp;#66;</p>&#66;</div>
<div><p>&amp;#67;</p>&#67;</div>
<div><p>&amp;#68;</p>&#68;</div>
<div><p>&amp;#69;</p>&#69;</div>
<div><p>&amp;#70;</p>&#70;</div>
<div><p>&amp;#71;</p>&#71;</div>
<div><p>&amp;#72;</p>&#72;</div>
<div><p>&amp;#73;</p>&#73;</div>
<div><p>&amp;#74;</p>&#74;</div>
<div><p>&amp;#75;</p>&#75;</div>
<div><p>&amp;#76;</p>&#76;</div>
<div><p>&amp;#77;</p>&#77;</div>
<div><p>&amp;#78;</p>&#78;</div>
<div><p>&amp;#79;</p>&#79;</div>
<div><p>&amp;#80;</p>&#80;</div>
<div><p>&amp;#81;</p>&#81;</div>
<div><p>&amp;#82;</p>&#82;</div>
<div><p>&amp;#83;</p>&#83;</div>
<div><p>&amp;#84;</p>&#84;</div>
<div><p>&amp;#85;</p>&#85;</div>
<div><p>&amp;#86;</p>&#86;</div>
<div><p>&amp;#87;</p>&#87;</div>
<div><p>&amp;#88;</p>&#88;</div>
<div><p>&amp;#89;</p>&#89;</div>
<div><p>&amp;#90;</p>&#90;</div>
<div><p>&amp;#91;</p>&#91;</div>
<div><p>&amp;#92;</p>&#92;</div>
<div><p>&amp;#93;</p>&#93;</div>
<div><p>&amp;#94;</p>&#94;</div>
<div><p>&amp;#95;</p>&#95;</div>
<div><p>&amp;#96;</p>&#96;</div>
<div><p>&amp;#97;</p>&#97;</div>
<div><p>&amp;#98;</p>&#98;</div>
<div><p>&amp;#99;</p>&#99;</div>
<div><p>&amp;#100;</p>&#100;</div>
<div><p>&amp;#101;</p>&#101;</div>
<div><p>&amp;#102;</p>&#102;</div>
<div><p>&amp;#103;</p>&#103;</div>
<div><p>&amp;#104;</p>&#104;</div>
<div><p>&amp;#105;</p>&#105;</div>
<div><p>&amp;#106;</p>&#106;</div>
<div><p>&amp;#107;</p>&#107;</div>
<div><p>&amp;#108;</p>&#108;</div>
<div><p>&amp;#109;</p>&#109;</div>
<div><p>&amp;#110;</p>&#110;</div>
<div><p>&amp;#111;</p>&#111;</div>
<div><p>&amp;#112;</p>&#112;</div>
<div><p>&amp;#113;</p>&#113;</div>
<div><p>&amp;#114;</p>&#114;</div>
<div><p>&amp;#115;</p>&#115;</div>
<div><p>&amp;#116;</p>&#116;</div>
<div><p>&amp;#117;</p>&#117;</div>
<div><p>&amp;#118;</p>&#118;</div>
<div><p>&amp;#119;</p>&#119;</div>
<div><p>&amp;#120;</p>&#120;</div>
<div><p>&amp;#121;</p>&#121;</div>
<div><p>&amp;#122;</p>&#122;</div>
<div><p>&amp;#123;</p>&#123;</div>
<div><p>&amp;#124;</p>&#124;</div>
<div><p>&amp;#125;</p>&#125;</div>
<div><p>&amp;#126;</p>&#126;</div>
<div><p>&amp;#160;</p>&#160;</div>
<div><p>&amp;#161;</p>&#161;</div>
<div><p>&amp;#162;</p>&#162;</div>
<div><p>&amp;#163;</p>&#163;</div>
<div><p>&amp;#164;</p>&#164;</div>
<div><p>&amp;#165;</p>&#165;</div>
<div><p>&amp;#166;</p>&#166;</div>
<div><p>&amp;#167;</p>&#167;</div>
<div><p>&amp;#168;</p>&#168;</div>
<div><p>&amp;#169;</p>&#169;</div>
<div><p>&amp;#170;</p>&#170;</div>
<div><p>&amp;#171;</p>&#171;</div>
<div><p>&amp;#172;</p>&#172;</div>
<div><p>&amp;#173;</p>&#173;</div>
<div><p>&amp;#174;</p>&#174;</div>
<div><p>&amp;#175;</p>&#175;</div>
<div><p>&amp;#176;</p>&#176;</div>
<div><p>&amp;#177;</p>&#177;</div>
<div><p>&amp;#178;</p>&#178;</div>
<div><p>&amp;#179;</p>&#179;</div>
<div><p>&amp;#180;</p>&#180;</div>
<div><p>&amp;#181;</p>&#181;</div>
<div><p>&amp;#182;</p>&#182;</div>
<div><p>&amp;#183;</p>&#183;</div>
<div><p>&amp;#184;</p>&#184;</div>
<div><p>&amp;#185;</p>&#185;</div>
<div><p>&amp;#186;</p>&#186;</div>
<div><p>&amp;#187;</p>&#187;</div>
<div><p>&amp;#188;</p>&#188;</div>
<div><p>&amp;#189;</p>&#189;</div>
<div><p>&amp;#190;</p>&#190;</div>
<div><p>&amp;#191;</p>&#191;</div>
<div><p>&amp;#192;</p>&#192;</div>
<div><p>&amp;#193;</p>&#193;</div>
<div><p>&amp;#194;</p>&#194;</div>
<div><p>&amp;#195;</p>&#195;</div>
<div><p>&amp;#196;</p>&#196;</div>
<div><p>&amp;#197;</p>&#197;</div>
<div><p>&amp;#198;</p>&#198;</div>
<div><p>&amp;#199;</p>&#199;</div>
<div><p>&amp;#200;</p>&#200;</div>
<div><p>&amp;#201;</p>&#201;</div>
<div><p>&amp;#202;</p>&#202;</div>
<div><p>&amp;#203;</p>&#203;</div>
<div><p>&amp;#204;</p>&#204;</div>
<div><p>&amp;#205;</p>&#205;</div>
<div><p>&amp;#206;</p>&#206;</div>
<div><p>&amp;#207;</p>&#207;</div>
<div><p>&amp;#208;</p>&#208;</div>
<div><p>&amp;#209;</p>&#209;</div>
<div><p>&amp;#210;</p>&#210;</div>
<div><p>&amp;#211;</p>&#211;</div>
<div><p>&amp;#212;</p>&#212;</div>
<div><p>&amp;#213;</p>&#213;</div>
<div><p>&amp;#214;</p>&#214;</div>
<div><p>&amp;#215;</p>&#215;</div>
<div><p>&amp;#216;</p>&#216;</div>
<div><p>&amp;#217;</p>&#217;</div>
<div><p>&amp;#218;</p>&#218;</div>
<div><p>&amp;#219;</p>&#219;</div>
<div><p>&amp;#220;</p>&#220;</div>
<div><p>&amp;#221;</p>&#221;</div>
<div><p>&amp;#222;</p>&#222;</div>
<div><p>&amp;#223;</p>&#223;</div>
<div><p>&amp;#224;</p>&#224;</div>
<div><p>&amp;#225;</p>&#225;</div>
<div><p>&amp;#226;</p>&#226;</div>
<div><p>&amp;#227;</p>&#227;</div>
<div><p>&amp;#228;</p>&#228;</div>
<div><p>&amp;#229;</p>&#229;</div>
<div><p>&amp;#230;</p>&#230;</div>
<div><p>&amp;#231;</p>&#231;</div>
<div><p>&amp;#232;</p>&#232;</div>
<div><p>&amp;#233;</p>&#233;</div>
<div><p>&amp;#234;</p>&#234;</div>
<div><p>&amp;#235;</p>&#235;</div>
<div><p>&amp;#236;</p>&#236;</div>
<div><p>&amp;#237;</p>&#237;</div>
<div><p>&amp;#238;</p>&#238;</div>
<div><p>&amp;#239;</p>&#239;</div>
<div><p>&amp;#240;</p>&#240;</div>
<div><p>&amp;#241;</p>&#241;</div>
<div><p>&amp;#242;</p>&#242;</div>
<div><p>&amp;#243;</p>&#243;</div>
<div><p>&amp;#244;</p>&#244;</div>
<div><p>&amp;#245;</p>&#245;</div>
<div><p>&amp;#246;</p>&#246;</div>
<div><p>&amp;#247;</p>&#247;</div>
<div><p>&amp;#248;</p>&#248;</div>
<div><p>&amp;#249;</p>&#249;</div>
<div><p>&amp;#250;</p>&#250;</div>
<div><p>&amp;#251;</p>&#251;</div>
<div><p>&amp;#252;</p>&#252;</div>
<div><p>&amp;#253;</p>&#253;</div>
<div><p>&amp;#254;</p>&#254;</div>
<div><p>&amp;#255;</p>&#255;</div>
<div><p>&amp;#338;</p>&#338;</div>
<div><p>&amp;#339;</p>&#339;</div>
<div><p>&amp;#376;</p>&#376;</div>
<div><p>&amp;#710;</p>&#710;</div>
<div><p>&amp;#732;</p>&#732;</div>
<div><p>&amp;#8192;</p>&#8192;</div>
<div><p>&amp;#8193;</p>&#8193;</div>
<div><p>&amp;#8194;</p>&#8194;</div>
<div><p>&amp;#8195;</p>&#8195;</div>
<div><p>&amp;#8196;</p>&#8196;</div>
<div><p>&amp;#8197;</p>&#8197;</div>
<div><p>&amp;#8198;</p>&#8198;</div>
<div><p>&amp;#8199;</p>&#8199;</div>
<div><p>&amp;#8200;</p>&#8200;</div>
<div><p>&amp;#8201;</p>&#8201;</div>
<div><p>&amp;#8202;</p>&#8202;</div>
<div><p>&amp;#8208;</p>&#8208;</div>
<div><p>&amp;#8209;</p>&#8209;</div>
<div><p>&amp;#8210;</p>&#8210;</div>
<div><p>&amp;#8211;</p>&#8211;</div>
<div><p>&amp;#8212;</p>&#8212;</div>
<div><p>&amp;#8216;</p>&#8216;</div>
<div><p>&amp;#8217;</p>&#8217;</div>
<div><p>&amp;#8218;</p>&#8218;</div>
<div><p>&amp;#8220;</p>&#8220;</div>
<div><p>&amp;#8221;</p>&#8221;</div>
<div><p>&amp;#8222;</p>&#8222;</div>
<div><p>&amp;#8226;</p>&#8226;</div>
<div><p>&amp;#8230;</p>&#8230;</div>
<div><p>&amp;#8239;</p>&#8239;</div>
<div><p>&amp;#8249;</p>&#8249;</div>
<div><p>&amp;#8250;</p>&#8250;</div>
<div><p>&amp;#8287;</p>&#8287;</div>
<div><p>&amp;#8364;</p>&#8364;</div>
<div><p>&amp;#8482;</p>&#8482;</div>
<div><p>&amp;#9724;</p>&#9724;</div>
<div><p>&amp;#64257;</p>&#64257;</div>
<div><p>&amp;#64258;</p>&#64258;</div>
</div>
</div>
</div>
</div>
<div id="specs">
</div>
<div id="installing">
<div class="section">
<div class="grid7 firstcol">
<h1>Installing Webfonts</h1>
<p>Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.</p>
<h2>1. Upload your webfonts</h2>
<p>You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.</p>
<h2>2. Include the webfont stylesheet</h2>
<p>A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the <a href="http://www.fontspring.com/blog/further-hardening-of-the-bulletproof-syntax">Fontspring blog post</a> about it. The code for it is as follows:</p>
<code>
@font-face{
font-family: 'MyWebFont';
src: url('WebFont.eot');
src: url('WebFont.eot?#iefix') format('embedded-opentype'),
url('WebFont.woff') format('woff'),
url('WebFont.ttf') format('truetype'),
url('WebFont.svg#webfont') format('svg');
}
</code>
<p>We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:</p>
<code>&lt;link rel=&quot;stylesheet&quot; href=&quot;stylesheet.css&quot; type=&quot;text/css&quot; charset=&quot;utf-8&quot; /&gt;</code>
<h2>3. Modify your own stylesheet</h2>
<p>To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:</p>
<code>p { font-family: 'WebFont', Arial, sans-serif; }</code>
<h2>4. Test</h2>
<p>Getting webfonts to work cross-browser <em>can</em> be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.</p>
</div>
<div class="grid5 sidebar">
<div class="box">
<h2>Troubleshooting<br />Font-Face Problems</h2>
<p>Having trouble getting your webfonts to load in your new website? Here are some tips to sort out what might be the problem.</p>
<h3>Fonts not showing in any browser</h3>
<p>This sounds like you need to work on the plumbing. You either did not upload the fonts to the correct directory, or you did not link the fonts properly in the CSS. If you've confirmed that all this is correct and you still have a problem, take a look at your .htaccess file and see if requests are getting intercepted.</p>
<h3>Fonts not loading in iPhone or iPad</h3>
<p>The most common problem here is that you are serving the fonts from an IIS server. IIS refuses to serve files that have unknown MIME types. If that is the case, you must set the MIME type for SVG to "image/svg+xml" in the server settings. Follow these instructions from Microsoft if you need help.</p>
<h3>Fonts not loading in Firefox</h3>
<p>The primary reason for this failure? You are still using a version Firefox older than 3.5. So upgrade already! If that isn't it, then you are very likely serving fonts from a different domain. Firefox requires that all font assets be served from the same domain. Lastly it is possible that you need to add WOFF to your list of MIME types (if you are serving via IIS.)</p>
<h3>Fonts not loading in IE</h3>
<p>Are you looking at Internet Explorer on an actual Windows machine or are you cheating by using a service like Adobe BrowserLab? Many of these screenshot services do not render @font-face for IE. Best to test it on a real machine.</p>
<h3>Fonts not loading in IE9</h3>
<p>IE9, like Firefox, requires that fonts be served from the same domain as the website. Make sure that is the case.</p>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<p>&copy;2010-2011 Font Squirrel. All rights reserved.</p>
</div>
</div>
</body>
</html>

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,612 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="specimen_files/easytabs.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" href="specimen_files/specimen_stylesheet.css" type="text/css" charset="utf-8" />
<link rel="stylesheet" href="stylesheet.css" type="text/css" charset="utf-8" />
<style type="text/css">
body{
font-family: 'latolight';
}
</style>
<title>Lato Light Regular Specimen</title>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#container').easyTabs({defaultContent:1});
});
</script>
</head>
<body>
<div id="container">
<div id="header">
Lato Light Regular </div>
<ul class="tabs">
<li><a href="#specimen">Specimen</a></li>
<li><a href="#layout">Sample Layout</a></li>
<li><a href="#glyphs">Glyphs &amp; Languages</a></li>
<li><a href="#installing">Installing Webfonts</a></li>
</ul>
<div id="main_content">
<div id="specimen">
<div class="section">
<div class="grid12 firstcol">
<div class="huge">AaBb</div>
</div>
</div>
<div class="section">
<div class="glyph_range">A&#x200B;B&#x200b;C&#x200b;D&#x200b;E&#x200b;F&#x200b;G&#x200b;H&#x200b;I&#x200b;J&#x200b;K&#x200b;L&#x200b;M&#x200b;N&#x200b;O&#x200b;P&#x200b;Q&#x200b;R&#x200b;S&#x200b;T&#x200b;U&#x200b;V&#x200b;W&#x200b;X&#x200b;Y&#x200b;Z&#x200b;a&#x200b;b&#x200b;c&#x200b;d&#x200b;e&#x200b;f&#x200b;g&#x200b;h&#x200b;i&#x200b;j&#x200b;k&#x200b;l&#x200b;m&#x200b;n&#x200b;o&#x200b;p&#x200b;q&#x200b;r&#x200b;s&#x200b;t&#x200b;u&#x200b;v&#x200b;w&#x200b;x&#x200b;y&#x200b;z&#x200b;1&#x200b;2&#x200b;3&#x200b;4&#x200b;5&#x200b;6&#x200b;7&#x200b;8&#x200b;9&#x200b;0&#x200b;&amp;&#x200b;.&#x200b;,&#x200b;?&#x200b;!&#x200b;&#64;&#x200b;(&#x200b;)&#x200b;#&#x200b;$&#x200b;%&#x200b;*&#x200b;+&#x200b;-&#x200b;=&#x200b;:&#x200b;;</div>
</div>
<div class="section">
<div class="grid12 firstcol">
<table class="sample_table">
<tr><td>10</td><td class="size10">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>11</td><td class="size11">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>12</td><td class="size12">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>13</td><td class="size13">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>14</td><td class="size14">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>16</td><td class="size16">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>18</td><td class="size18">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>20</td><td class="size20">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>24</td><td class="size24">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>30</td><td class="size30">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>36</td><td class="size36">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>48</td><td class="size48">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>60</td><td class="size60">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>72</td><td class="size72">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
<tr><td>90</td><td class="size90">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</td></tr>
</table>
</div>
</div>
<div class="section" id="bodycomparison">
<div id="xheight">
<div class="fontbody">&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;&#x25FC;body</div><div class="arialbody">body</div><div class="verdanabody">body</div><div class="georgiabody">body</div></div>
<div class="fontbody" style="z-index:1">
body<span>Lato Light Regular</span>
</div>
<div class="arialbody" style="z-index:1">
body<span>Arial</span>
</div>
<div class="verdanabody" style="z-index:1">
body<span>Verdana</span>
</div>
<div class="georgiabody" style="z-index:1">
body<span>Georgia</span>
</div>
</div>
<div class="section psample psample_row1" id="">
<div class="grid2 firstcol">
<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row2" id="">
<div class="grid3 firstcol">
<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid5">
<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row3" id="">
<div class="grid5 firstcol">
<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid7">
<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row4" id="">
<div class="grid12 firstcol">
<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="white_blend"></div>
</div>
<div class="section psample psample_row1 fullreverse">
<div class="grid2 firstcol">
<p class="size10"><span>10.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size11"><span>11.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid3">
<p class="size12"><span>12.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size13"><span>13.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample psample_row2 fullreverse">
<div class="grid3 firstcol">
<p class="size14"><span>14.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid4">
<p class="size16"><span>16.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid5">
<p class="size18"><span>18.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample fullreverse psample_row3" id="">
<div class="grid5 firstcol">
<p class="size20"><span>20.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="grid7">
<p class="size24"><span>24.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
<div class="section psample fullreverse psample_row4" id="" style="border-bottom: 20px #000 solid;">
<div class="grid12 firstcol">
<p class="size30"><span>30.</span>Aenean lacinia bibendum nulla sed consectetur. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue.</p>
</div>
<div class="black_blend"></div>
</div>
</div>
<div id="layout">
<div class="section">
<div class="grid12 firstcol">
<h1>Lorem Ipsum Dolor</h1>
<h2>Etiam porta sem malesuada magna mollis euismod</h2>
<p class="byline">By <a href="#link">Aenean Lacinia</a></p>
</div>
</div>
<div class="section">
<div class="grid8 firstcol">
<p class="large">Donec sed odio dui. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
<h3>Pellentesque ornare sem</h3>
<p>Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit. </p>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. </p>
<p>Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Aenean lacinia bibendum nulla sed consectetur. </p>
<p>Nullam quis risus eget urna mollis ornare vel eu leo. Nullam quis risus eget urna mollis ornare vel eu leo. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec ullamcorper nulla non metus auctor fringilla. </p>
<h3>Cras mattis consectetur</h3>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum. </p>
<p>Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum.</p>
</div>
<div class="grid4 sidebar">
<div class="box reverse">
<p class="last">Nullam quis risus eget urna mollis ornare vel eu leo. Donec ullamcorper nulla non metus auctor fringilla. Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
</div>
<p class="caption">Maecenas sed diam eget risus varius.</p>
<p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Vestibulum id ligula porta felis euismod semper. Sed posuere consectetur est at lobortis. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean lacinia bibendum nulla sed consectetur. Nullam quis risus eget urna mollis ornare vel eu leo. </p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec ullamcorper nulla non metus auctor fringilla. Maecenas faucibus mollis interdum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. </p>
</div>
</div>
</div>
<div id="glyphs">
<div class="section">
<div class="grid12 firstcol">
<h1>Language Support</h1>
<p>The subset of Lato Light Regular in this kit supports the following languages:<br />
Albanian, Basque, Breton, Chamorro, Danish, Dutch, English, Faroese, Finnish, French, Frisian, Galician, German, Icelandic, Italian, Malagasy, Norwegian, Portuguese, Spanish, Swedish </p>
<h1>Glyph Chart</h1>
<p>The subset of Lato Light Regular in this kit includes all the glyphs listed below. Unicode entities are included above each glyph to help you insert individual characters into your layout.</p>
<div id="glyph_chart">
<div><p>&amp;#13;</p>&#13;</div>
<div><p>&amp;#32;</p>&#32;</div>
<div><p>&amp;#33;</p>&#33;</div>
<div><p>&amp;#34;</p>&#34;</div>
<div><p>&amp;#35;</p>&#35;</div>
<div><p>&amp;#36;</p>&#36;</div>
<div><p>&amp;#37;</p>&#37;</div>
<div><p>&amp;#38;</p>&#38;</div>
<div><p>&amp;#39;</p>&#39;</div>
<div><p>&amp;#40;</p>&#40;</div>
<div><p>&amp;#41;</p>&#41;</div>
<div><p>&amp;#42;</p>&#42;</div>
<div><p>&amp;#43;</p>&#43;</div>
<div><p>&amp;#44;</p>&#44;</div>
<div><p>&amp;#45;</p>&#45;</div>
<div><p>&amp;#46;</p>&#46;</div>
<div><p>&amp;#47;</p>&#47;</div>
<div><p>&amp;#48;</p>&#48;</div>
<div><p>&amp;#49;</p>&#49;</div>
<div><p>&amp;#50;</p>&#50;</div>
<div><p>&amp;#51;</p>&#51;</div>
<div><p>&amp;#52;</p>&#52;</div>
<div><p>&amp;#53;</p>&#53;</div>
<div><p>&amp;#54;</p>&#54;</div>
<div><p>&amp;#55;</p>&#55;</div>
<div><p>&amp;#56;</p>&#56;</div>
<div><p>&amp;#57;</p>&#57;</div>
<div><p>&amp;#58;</p>&#58;</div>
<div><p>&amp;#59;</p>&#59;</div>
<div><p>&amp;#60;</p>&#60;</div>
<div><p>&amp;#61;</p>&#61;</div>
<div><p>&amp;#62;</p>&#62;</div>
<div><p>&amp;#63;</p>&#63;</div>
<div><p>&amp;#64;</p>&#64;</div>
<div><p>&amp;#65;</p>&#65;</div>
<div><p>&amp;#66;</p>&#66;</div>
<div><p>&amp;#67;</p>&#67;</div>
<div><p>&amp;#68;</p>&#68;</div>
<div><p>&amp;#69;</p>&#69;</div>
<div><p>&amp;#70;</p>&#70;</div>
<div><p>&amp;#71;</p>&#71;</div>
<div><p>&amp;#72;</p>&#72;</div>
<div><p>&amp;#73;</p>&#73;</div>
<div><p>&amp;#74;</p>&#74;</div>
<div><p>&amp;#75;</p>&#75;</div>
<div><p>&amp;#76;</p>&#76;</div>
<div><p>&amp;#77;</p>&#77;</div>
<div><p>&amp;#78;</p>&#78;</div>
<div><p>&amp;#79;</p>&#79;</div>
<div><p>&amp;#80;</p>&#80;</div>
<div><p>&amp;#81;</p>&#81;</div>
<div><p>&amp;#82;</p>&#82;</div>
<div><p>&amp;#83;</p>&#83;</div>
<div><p>&amp;#84;</p>&#84;</div>
<div><p>&amp;#85;</p>&#85;</div>
<div><p>&amp;#86;</p>&#86;</div>
<div><p>&amp;#87;</p>&#87;</div>
<div><p>&amp;#88;</p>&#88;</div>
<div><p>&amp;#89;</p>&#89;</div>
<div><p>&amp;#90;</p>&#90;</div>
<div><p>&amp;#91;</p>&#91;</div>
<div><p>&amp;#92;</p>&#92;</div>
<div><p>&amp;#93;</p>&#93;</div>
<div><p>&amp;#94;</p>&#94;</div>
<div><p>&amp;#95;</p>&#95;</div>
<div><p>&amp;#96;</p>&#96;</div>
<div><p>&amp;#97;</p>&#97;</div>
<div><p>&amp;#98;</p>&#98;</div>
<div><p>&amp;#99;</p>&#99;</div>
<div><p>&amp;#100;</p>&#100;</div>
<div><p>&amp;#101;</p>&#101;</div>
<div><p>&amp;#102;</p>&#102;</div>
<div><p>&amp;#103;</p>&#103;</div>
<div><p>&amp;#104;</p>&#104;</div>
<div><p>&amp;#105;</p>&#105;</div>
<div><p>&amp;#106;</p>&#106;</div>
<div><p>&amp;#107;</p>&#107;</div>
<div><p>&amp;#108;</p>&#108;</div>
<div><p>&amp;#109;</p>&#109;</div>
<div><p>&amp;#110;</p>&#110;</div>
<div><p>&amp;#111;</p>&#111;</div>
<div><p>&amp;#112;</p>&#112;</div>
<div><p>&amp;#113;</p>&#113;</div>
<div><p>&amp;#114;</p>&#114;</div>
<div><p>&amp;#115;</p>&#115;</div>
<div><p>&amp;#116;</p>&#116;</div>
<div><p>&amp;#117;</p>&#117;</div>
<div><p>&amp;#118;</p>&#118;</div>
<div><p>&amp;#119;</p>&#119;</div>
<div><p>&amp;#120;</p>&#120;</div>
<div><p>&amp;#121;</p>&#121;</div>
<div><p>&amp;#122;</p>&#122;</div>
<div><p>&amp;#123;</p>&#123;</div>
<div><p>&amp;#124;</p>&#124;</div>
<div><p>&amp;#125;</p>&#125;</div>
<div><p>&amp;#126;</p>&#126;</div>
<div><p>&amp;#160;</p>&#160;</div>
<div><p>&amp;#161;</p>&#161;</div>
<div><p>&amp;#162;</p>&#162;</div>
<div><p>&amp;#163;</p>&#163;</div>
<div><p>&amp;#164;</p>&#164;</div>
<div><p>&amp;#165;</p>&#165;</div>
<div><p>&amp;#166;</p>&#166;</div>
<div><p>&amp;#167;</p>&#167;</div>
<div><p>&amp;#168;</p>&#168;</div>
<div><p>&amp;#169;</p>&#169;</div>
<div><p>&amp;#170;</p>&#170;</div>
<div><p>&amp;#171;</p>&#171;</div>
<div><p>&amp;#172;</p>&#172;</div>
<div><p>&amp;#173;</p>&#173;</div>
<div><p>&amp;#174;</p>&#174;</div>
<div><p>&amp;#175;</p>&#175;</div>
<div><p>&amp;#176;</p>&#176;</div>
<div><p>&amp;#177;</p>&#177;</div>
<div><p>&amp;#178;</p>&#178;</div>
<div><p>&amp;#179;</p>&#179;</div>
<div><p>&amp;#180;</p>&#180;</div>
<div><p>&amp;#181;</p>&#181;</div>
<div><p>&amp;#182;</p>&#182;</div>
<div><p>&amp;#183;</p>&#183;</div>
<div><p>&amp;#184;</p>&#184;</div>
<div><p>&amp;#185;</p>&#185;</div>
<div><p>&amp;#186;</p>&#186;</div>
<div><p>&amp;#187;</p>&#187;</div>
<div><p>&amp;#188;</p>&#188;</div>
<div><p>&amp;#189;</p>&#189;</div>
<div><p>&amp;#190;</p>&#190;</div>
<div><p>&amp;#191;</p>&#191;</div>
<div><p>&amp;#192;</p>&#192;</div>
<div><p>&amp;#193;</p>&#193;</div>
<div><p>&amp;#194;</p>&#194;</div>
<div><p>&amp;#195;</p>&#195;</div>
<div><p>&amp;#196;</p>&#196;</div>
<div><p>&amp;#197;</p>&#197;</div>
<div><p>&amp;#198;</p>&#198;</div>
<div><p>&amp;#199;</p>&#199;</div>
<div><p>&amp;#200;</p>&#200;</div>
<div><p>&amp;#201;</p>&#201;</div>
<div><p>&amp;#202;</p>&#202;</div>
<div><p>&amp;#203;</p>&#203;</div>
<div><p>&amp;#204;</p>&#204;</div>
<div><p>&amp;#205;</p>&#205;</div>
<div><p>&amp;#206;</p>&#206;</div>
<div><p>&amp;#207;</p>&#207;</div>
<div><p>&amp;#208;</p>&#208;</div>
<div><p>&amp;#209;</p>&#209;</div>
<div><p>&amp;#210;</p>&#210;</div>
<div><p>&amp;#211;</p>&#211;</div>
<div><p>&amp;#212;</p>&#212;</div>
<div><p>&amp;#213;</p>&#213;</div>
<div><p>&amp;#214;</p>&#214;</div>
<div><p>&amp;#215;</p>&#215;</div>
<div><p>&amp;#216;</p>&#216;</div>
<div><p>&amp;#217;</p>&#217;</div>
<div><p>&amp;#218;</p>&#218;</div>
<div><p>&amp;#219;</p>&#219;</div>
<div><p>&amp;#220;</p>&#220;</div>
<div><p>&amp;#221;</p>&#221;</div>
<div><p>&amp;#222;</p>&#222;</div>
<div><p>&amp;#223;</p>&#223;</div>
<div><p>&amp;#224;</p>&#224;</div>
<div><p>&amp;#225;</p>&#225;</div>
<div><p>&amp;#226;</p>&#226;</div>
<div><p>&amp;#227;</p>&#227;</div>
<div><p>&amp;#228;</p>&#228;</div>
<div><p>&amp;#229;</p>&#229;</div>
<div><p>&amp;#230;</p>&#230;</div>
<div><p>&amp;#231;</p>&#231;</div>
<div><p>&amp;#232;</p>&#232;</div>
<div><p>&amp;#233;</p>&#233;</div>
<div><p>&amp;#234;</p>&#234;</div>
<div><p>&amp;#235;</p>&#235;</div>
<div><p>&amp;#236;</p>&#236;</div>
<div><p>&amp;#237;</p>&#237;</div>
<div><p>&amp;#238;</p>&#238;</div>
<div><p>&amp;#239;</p>&#239;</div>
<div><p>&amp;#240;</p>&#240;</div>
<div><p>&amp;#241;</p>&#241;</div>
<div><p>&amp;#242;</p>&#242;</div>
<div><p>&amp;#243;</p>&#243;</div>
<div><p>&amp;#244;</p>&#244;</div>
<div><p>&amp;#245;</p>&#245;</div>
<div><p>&amp;#246;</p>&#246;</div>
<div><p>&amp;#247;</p>&#247;</div>
<div><p>&amp;#248;</p>&#248;</div>
<div><p>&amp;#249;</p>&#249;</div>
<div><p>&amp;#250;</p>&#250;</div>
<div><p>&amp;#251;</p>&#251;</div>
<div><p>&amp;#252;</p>&#252;</div>
<div><p>&amp;#253;</p>&#253;</div>
<div><p>&amp;#254;</p>&#254;</div>
<div><p>&amp;#255;</p>&#255;</div>
<div><p>&amp;#338;</p>&#338;</div>
<div><p>&amp;#339;</p>&#339;</div>
<div><p>&amp;#376;</p>&#376;</div>
<div><p>&amp;#710;</p>&#710;</div>
<div><p>&amp;#732;</p>&#732;</div>
<div><p>&amp;#8192;</p>&#8192;</div>
<div><p>&amp;#8193;</p>&#8193;</div>
<div><p>&amp;#8194;</p>&#8194;</div>
<div><p>&amp;#8195;</p>&#8195;</div>
<div><p>&amp;#8196;</p>&#8196;</div>
<div><p>&amp;#8197;</p>&#8197;</div>
<div><p>&amp;#8198;</p>&#8198;</div>
<div><p>&amp;#8199;</p>&#8199;</div>
<div><p>&amp;#8200;</p>&#8200;</div>
<div><p>&amp;#8201;</p>&#8201;</div>
<div><p>&amp;#8202;</p>&#8202;</div>
<div><p>&amp;#8208;</p>&#8208;</div>
<div><p>&amp;#8209;</p>&#8209;</div>
<div><p>&amp;#8210;</p>&#8210;</div>
<div><p>&amp;#8211;</p>&#8211;</div>
<div><p>&amp;#8212;</p>&#8212;</div>
<div><p>&amp;#8216;</p>&#8216;</div>
<div><p>&amp;#8217;</p>&#8217;</div>
<div><p>&amp;#8218;</p>&#8218;</div>
<div><p>&amp;#8220;</p>&#8220;</div>
<div><p>&amp;#8221;</p>&#8221;</div>
<div><p>&amp;#8222;</p>&#8222;</div>
<div><p>&amp;#8226;</p>&#8226;</div>
<div><p>&amp;#8230;</p>&#8230;</div>
<div><p>&amp;#8239;</p>&#8239;</div>
<div><p>&amp;#8249;</p>&#8249;</div>
<div><p>&amp;#8250;</p>&#8250;</div>
<div><p>&amp;#8287;</p>&#8287;</div>
<div><p>&amp;#8364;</p>&#8364;</div>
<div><p>&amp;#8482;</p>&#8482;</div>
<div><p>&amp;#9724;</p>&#9724;</div>
<div><p>&amp;#64257;</p>&#64257;</div>
<div><p>&amp;#64258;</p>&#64258;</div>
</div>
</div>
</div>
</div>
<div id="specs">
</div>
<div id="installing">
<div class="section">
<div class="grid7 firstcol">
<h1>Installing Webfonts</h1>
<p>Webfonts are supported by all major browser platforms but not all in the same way. There are currently four different font formats that must be included in order to target all browsers. This includes TTF, WOFF, EOT and SVG.</p>
<h2>1. Upload your webfonts</h2>
<p>You must upload your webfont kit to your website. They should be in or near the same directory as your CSS files.</p>
<h2>2. Include the webfont stylesheet</h2>
<p>A special CSS @font-face declaration helps the various browsers select the appropriate font it needs without causing you a bunch of headaches. Learn more about this syntax by reading the <a href="http://www.fontspring.com/blog/further-hardening-of-the-bulletproof-syntax">Fontspring blog post</a> about it. The code for it is as follows:</p>
<code>
@font-face{
font-family: 'MyWebFont';
src: url('WebFont.eot');
src: url('WebFont.eot?#iefix') format('embedded-opentype'),
url('WebFont.woff') format('woff'),
url('WebFont.ttf') format('truetype'),
url('WebFont.svg#webfont') format('svg');
}
</code>
<p>We've already gone ahead and generated the code for you. All you have to do is link to the stylesheet in your HTML, like this:</p>
<code>&lt;link rel=&quot;stylesheet&quot; href=&quot;stylesheet.css&quot; type=&quot;text/css&quot; charset=&quot;utf-8&quot; /&gt;</code>
<h2>3. Modify your own stylesheet</h2>
<p>To take advantage of your new fonts, you must tell your stylesheet to use them. Look at the original @font-face declaration above and find the property called "font-family." The name linked there will be what you use to reference the font. Prepend that webfont name to the font stack in the "font-family" property, inside the selector you want to change. For example:</p>
<code>p { font-family: 'WebFont', Arial, sans-serif; }</code>
<h2>4. Test</h2>
<p>Getting webfonts to work cross-browser <em>can</em> be tricky. Use the information in the sidebar to help you if you find that fonts aren't loading in a particular browser.</p>
</div>
<div class="grid5 sidebar">
<div class="box">
<h2>Troubleshooting<br />Font-Face Problems</h2>
<p>Having trouble getting your webfonts to load in your new website? Here are some tips to sort out what might be the problem.</p>
<h3>Fonts not showing in any browser</h3>
<p>This sounds like you need to work on the plumbing. You either did not upload the fonts to the correct directory, or you did not link the fonts properly in the CSS. If you've confirmed that all this is correct and you still have a problem, take a look at your .htaccess file and see if requests are getting intercepted.</p>
<h3>Fonts not loading in iPhone or iPad</h3>
<p>The most common problem here is that you are serving the fonts from an IIS server. IIS refuses to serve files that have unknown MIME types. If that is the case, you must set the MIME type for SVG to "image/svg+xml" in the server settings. Follow these instructions from Microsoft if you need help.</p>
<h3>Fonts not loading in Firefox</h3>
<p>The primary reason for this failure? You are still using a version Firefox older than 3.5. So upgrade already! If that isn't it, then you are very likely serving fonts from a different domain. Firefox requires that all font assets be served from the same domain. Lastly it is possible that you need to add WOFF to your list of MIME types (if you are serving via IIS.)</p>
<h3>Fonts not loading in IE</h3>
<p>Are you looking at Internet Explorer on an actual Windows machine or are you cheating by using a service like Adobe BrowserLab? Many of these screenshot services do not render @font-face for IE. Best to test it on a real machine.</p>
<h3>Fonts not loading in IE9</h3>
<p>IE9, like Firefox, requires that fonts be served from the same domain as the website. Make sure that is the case.</p>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<p>&copy;2010-2011 Font Squirrel. All rights reserved.</p>
</div>
</div>
</body>
</html>

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,35 @@
/* Webfont: LatoLatin-Bold */@font-face {
font-family: 'LatoLatinWeb';
src: url('fonts/LatoLatin-Bold.eot'); /* IE9 Compat Modes */
src: url('fonts/LatoLatin-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('fonts/LatoLatin-Bold.woff2') format('woff2'), /* Modern Browsers */
url('fonts/LatoLatin-Bold.woff') format('woff'), /* Modern Browsers */
url('fonts/LatoLatin-Bold.ttf') format('truetype');
font-style: normal;
font-weight: bold;
text-rendering: optimizeLegibility;
}
/* Webfont: LatoLatin-Regular */@font-face {
font-family: 'LatoLatinWeb';
src: url('fonts/LatoLatin-Regular.eot'); /* IE9 Compat Modes */
src: url('fonts/LatoLatin-Regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('fonts/LatoLatin-Regular.woff2') format('woff2'), /* Modern Browsers */
url('fonts/LatoLatin-Regular.woff') format('woff'), /* Modern Browsers */
url('fonts/LatoLatin-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
/* Webfont: LatoLatin-Light */@font-face {
font-family: 'LatoLatinWebLight';
src: url('fonts/LatoLatin-Light.eot'); /* IE9 Compat Modes */
src: url('fonts/LatoLatin-Light.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('fonts/LatoLatin-Light.woff2') format('woff2'), /* Modern Browsers */
url('fonts/LatoLatin-Light.woff') format('woff'), /* Modern Browsers */
url('fonts/LatoLatin-Light.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}

View file

@ -0,0 +1,7 @@
(function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;}
if(typeof param.defaultContent=="number")
{var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;}
$(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();}
function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none")
{$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}}
$(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery);

View file

@ -0,0 +1,129 @@
/*Notes about grid:
Columns: 12
Grid Width: 825px
Column Width: 55px
Gutter Width: 15px
-------------------------------*/
.section {margin-bottom: 18px;
}
.section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;}
.section {*zoom: 1;}
.section .firstcolumn,
.section .firstcol {margin-left: 0;}
/* Border on left hand side of a column. */
.border {
padding-left: 7px;
margin-left: 7px;
border-left: 1px solid #eee;
}
/* Border with more whitespace, spans one column. */
.colborder {
padding-left: 42px;
margin-left: 42px;
border-left: 1px solid #eee;
}
/* The Grid Classes */
.grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12
{margin-left: 15px;float: left;display: inline; overflow: hidden;}
.width1, .grid1, .span-1 {width: 55px;}
.width1_2cols,.grid1_2cols {width: 20px;}
.width1_3cols,.grid1_3cols {width: 8px;}
.width1_4cols,.grid1_4cols {width: 2px;}
.input_width1 {width: 49px;}
.width2, .grid2, .span-2 {width: 125px;}
.width2_3cols,.grid2_3cols {width: 31px;}
.width2_4cols,.grid2_4cols {width: 20px;}
.input_width2 {width: 119px;}
.width3, .grid3, .span-3 {width: 195px;}
.width3_2cols,.grid3_2cols {width: 90px;}
.width3_4cols,.grid3_4cols {width: 37px;}
.input_width3 {width: 189px;}
.width4, .grid4, .span-4 {width: 265px;}
.width4_3cols,.grid4_3cols {width: 78px;}
.input_width4 {width: 259px;}
.width5, .grid5, .span-5 {width: 335px;}
.width5_2cols,.grid5_2cols {width: 160px;}
.width5_3cols,.grid5_3cols {width: 101px;}
.width5_4cols,.grid5_4cols {width: 72px;}
.input_width5 {width: 329px;}
.width6, .grid6, .span-6 {width: 405px;}
.width6_4cols,.grid6_4cols {width: 90px;}
.input_width6 {width: 399px;}
.width7, .grid7, .span-7 {width: 475px;}
.width7_2cols,.grid7_2cols {width: 230px;}
.width7_3cols,.grid7_3cols {width: 148px;}
.width7_4cols,.grid7_4cols {width: 107px;}
.input_width7 {width: 469px;}
.width8, .grid8, .span-8 {width: 545px;}
.width8_3cols,.grid8_3cols {width: 171px;}
.input_width8 {width: 539px;}
.width9, .grid9, .span-9 {width: 615px;}
.width9_2cols,.grid9_2cols {width: 300px;}
.width9_4cols,.grid9_4cols {width: 142px;}
.input_width9 {width: 609px;}
.width10, .grid10, .span-10 {width: 685px;}
.width10_3cols,.grid10_3cols {width: 218px;}
.width10_4cols,.grid10_4cols {width: 160px;}
.input_width10 {width: 679px;}
.width11, .grid11, .span-11 {width: 755px;}
.width11_2cols,.grid11_2cols {width: 370px;}
.width11_3cols,.grid11_3cols {width: 241px;}
.width11_4cols,.grid11_4cols {width: 177px;}
.input_width11 {width: 749px;}
.width12, .grid12, .span-12 {width: 825px;}
.input_width12 {width: 819px;}
/* Subdivided grid spaces */
.emptycols_left1, .prepend-1 {padding-left: 70px;}
.emptycols_right1, .append-1 {padding-right: 70px;}
.emptycols_left2, .prepend-2 {padding-left: 140px;}
.emptycols_right2, .append-2 {padding-right: 140px;}
.emptycols_left3, .prepend-3 {padding-left: 210px;}
.emptycols_right3, .append-3 {padding-right: 210px;}
.emptycols_left4, .prepend-4 {padding-left: 280px;}
.emptycols_right4, .append-4 {padding-right: 280px;}
.emptycols_left5, .prepend-5 {padding-left: 350px;}
.emptycols_right5, .append-5 {padding-right: 350px;}
.emptycols_left6, .prepend-6 {padding-left: 420px;}
.emptycols_right6, .append-6 {padding-right: 420px;}
.emptycols_left7, .prepend-7 {padding-left: 490px;}
.emptycols_right7, .append-7 {padding-right: 490px;}
.emptycols_left8, .prepend-8 {padding-left: 560px;}
.emptycols_right8, .append-8 {padding-right: 560px;}
.emptycols_left9, .prepend-9 {padding-left: 630px;}
.emptycols_right9, .append-9 {padding-right: 630px;}
.emptycols_left10, .prepend-10 {padding-left: 700px;}
.emptycols_right10, .append-10 {padding-right: 700px;}
.emptycols_left11, .prepend-11 {padding-left: 770px;}
.emptycols_right11, .append-11 {padding-right: 770px;}
.pull-1 {margin-left: -70px;}
.push-1 {margin-right: -70px;margin-left: 18px;float: right;}
.pull-2 {margin-left: -140px;}
.push-2 {margin-right: -140px;margin-left: 18px;float: right;}
.pull-3 {margin-left: -210px;}
.push-3 {margin-right: -210px;margin-left: 18px;float: right;}
.pull-4 {margin-left: -280px;}
.push-4 {margin-right: -280px;margin-left: 18px;float: right;}

View file

@ -0,0 +1,396 @@
@import url('grid_12-825-55-15.css');
/*
CSS Reset by Eric Meyer - Released under Public Domain
http://meyerweb.com/eric/tools/css/reset/
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend, table,
caption, tbody, tfoot, thead, tr, th, td
{margin: 0;padding: 0;border: 0;outline: 0;
font-size: 100%;vertical-align: baseline;
background: transparent;}
body {line-height: 1;}
ol, ul {list-style: none;}
blockquote, q {quotes: none;}
blockquote:before, blockquote:after,
q:before, q:after {content: ''; content: none;}
:focus {outline: 0;}
ins {text-decoration: none;}
del {text-decoration: line-through;}
table {border-collapse: collapse;border-spacing: 0;}
body {
color: #000;
background-color: #dcdcdc;
}
a {
text-decoration: none;
color: #1883ba;
}
h1{
font-size: 32px;
font-weight: normal;
font-style: normal;
margin-bottom: 18px;
}
h2{
font-size: 18px;
}
#container {
width: 865px;
margin: 0px auto;
}
#header {
padding: 20px;
font-size: 36px;
background-color: #000;
color: #fff;
}
#header span {
color: #666;
}
#main_content {
background-color: #fff;
padding: 60px 20px 20px;
}
#footer p {
margin: 0;
padding-top: 10px;
padding-bottom: 50px;
color: #333;
font: 10px Arial, sans-serif;
}
.tabs {
width: 100%;
height: 31px;
background-color: #444;
}
.tabs li {
float: left;
margin: 0;
overflow: hidden;
background-color: #444;
}
.tabs li a {
display: block;
color: #fff;
text-decoration: none;
font: bold 11px/11px 'Arial';
text-transform: uppercase;
padding: 10px 15px;
border-right: 1px solid #fff;
}
.tabs li a:hover {
background-color: #00b3ff;
}
.tabs li.active a {
color: #000;
background-color: #fff;
}
div.huge {
font-size: 300px;
line-height: 1em;
padding: 0;
letter-spacing: -.02em;
overflow: hidden;
}
div.glyph_range {
font-size: 72px;
line-height: 1.1em;
}
.size10{ font-size: 10px; }
.size11{ font-size: 11px; }
.size12{ font-size: 12px; }
.size13{ font-size: 13px; }
.size14{ font-size: 14px; }
.size16{ font-size: 16px; }
.size18{ font-size: 18px; }
.size20{ font-size: 20px; }
.size24{ font-size: 24px; }
.size30{ font-size: 30px; }
.size36{ font-size: 36px; }
.size48{ font-size: 48px; }
.size60{ font-size: 60px; }
.size72{ font-size: 72px; }
.size90{ font-size: 90px; }
.psample_row1 { height: 120px;}
.psample_row1 { height: 120px;}
.psample_row2 { height: 160px;}
.psample_row3 { height: 160px;}
.psample_row4 { height: 160px;}
.psample {
overflow: hidden;
position: relative;
}
.psample p {
line-height: 1.3em;
display: block;
overflow: hidden;
margin: 0;
}
.psample span {
margin-right: .5em;
}
.white_blend {
width: 100%;
height: 61px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==);
position: absolute;
bottom: 0;
}
.black_blend {
width: 100%;
height: 61px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC);
position: absolute;
bottom: 0;
}
.fullreverse {
background: #000 !important;
color: #fff !important;
margin-left: -20px;
padding-left: 20px;
margin-right: -20px;
padding-right: 20px;
padding: 20px;
margin-bottom:0;
}
.sample_table td {
padding-top: 3px;
padding-bottom:5px;
padding-left: 5px;
vertical-align: middle;
line-height: 1.2em;
}
.sample_table td:first-child {
background-color: #eee;
text-align: right;
padding-right: 5px;
padding-left: 0;
padding: 5px;
font: 11px/12px "Courier New", Courier, mono;
}
code {
white-space: pre;
background-color: #eee;
display: block;
padding: 10px;
margin-bottom: 18px;
overflow: auto;
}
.bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;}
.box {
padding: 18px;
margin-bottom: 18px;
background: #eee;
}
.reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;}
#bodycomparison {
position: relative;
overflow: hidden;
font-size: 72px;
height: 90px;
white-space: nowrap;
}
#bodycomparison div{
font-size: 72px;
line-height: 90px;
display: inline;
margin: 0 15px 0 0;
padding: 0;
}
#bodycomparison div span{
font: 10px Arial;
position: absolute;
left: 0;
}
#xheight {
float: none;
position: absolute;
color: #d9f3ff;
font-size: 72px;
line-height: 90px;
}
.fontbody {
position: relative;
}
.arialbody{
font-family: Arial;
position: relative;
}
.verdanabody{
font-family: Verdana;
position: relative;
}
.georgiabody{
font-family: Georgia;
position: relative;
}
/* @group Layout page
*/
#layout h1 {
font-size: 36px;
line-height: 42px;
font-weight: normal;
font-style: normal;
}
#layout h2 {
font-size: 24px;
line-height: 23px;
font-weight: normal;
font-style: normal;
}
#layout h3 {
font-size: 22px;
line-height: 1.4em;
margin-top: 1em;
font-weight: normal;
font-style: normal;
}
#layout p.byline {
font-size: 12px;
margin-top: 18px;
line-height: 12px;
margin-bottom: 0;
}
#layout p {
font-size: 14px;
line-height: 21px;
margin-bottom: .5em;
}
#layout p.large{
font-size: 18px;
line-height: 26px;
}
#layout .sidebar p{
font-size: 12px;
line-height: 1.4em;
}
#layout p.caption {
font-size: 10px;
margin-top: -16px;
margin-bottom: 18px;
}
/* @end */
/* @group Glyphs */
#glyph_chart div{
background-color: #d9f3ff;
color: black;
float: left;
font-size: 36px;
height: 1.2em;
line-height: 1.2em;
margin-bottom: 1px;
margin-right: 1px;
text-align: center;
width: 1.2em;
position: relative;
padding: .6em .2em .2em;
}
#glyph_chart div p {
position: absolute;
left: 0;
top: 0;
display: block;
text-align: center;
font: bold 9px Arial, sans-serif;
background-color: #3a768f;
width: 100%;
color: #fff;
padding: 2px 0;
}
#glyphs h1 {
font-family: Arial, sans-serif;
}
/* @end */
/* @group Installing */
#installing {
font: 13px Arial, sans-serif;
}
#installing p,
#glyphs p{
line-height: 1.2em;
margin-bottom: 18px;
font: 13px Arial, sans-serif;
}
#installing h3{
font-size: 15px;
margin-top: 18px;
}
/* @end */
#rendering h1 {
font-family: Arial, sans-serif;
}
.render_table td {
font: 11px "Courier New", Courier, mono;
vertical-align: middle;
}

View file

@ -0,0 +1,30 @@
/* Generated by Font Squirrel (http://www.fontsquirrel.com) on July 12, 2014 */
@font-face {
font-family: 'latohairline';
src: url('lato-hai-webfont.eot');
src: url('lato-hai-webfont.eot?#iefix') format('embedded-opentype'),
url('lato-hai-webfont.woff') format('woff'),
url('lato-hai-webfont.ttf') format('truetype'),
url('lato-hai-webfont.svg#latohairline') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'latolight';
src: url('lato-lig-webfont.eot');
src: url('lato-lig-webfont.eot?#iefix') format('embedded-opentype'),
url('lato-lig-webfont.woff') format('woff'),
url('lato-lig-webfont.ttf') format('truetype'),
url('lato-lig-webfont.svg#latolight') format('svg');
font-weight: normal;
font-style: normal;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
<![CDATA[
.st0{fill:#dddddd;}
]]>
</style>
<polygon class="st0" points="340.2,160 255.8,244.3 171.8,160.4 160,172.2 244,256 160,339.9 171.8,351.6 255.8,267.8 340.2,352
352,340.3 267.6,256 352,171.8 "/>
</svg>

After

Width:  |  Height:  |  Size: 713 B

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<g>
<circle fill="#3bb34a" cx="173.602" cy="256.19" r="27.529"/>
<circle fill="#3bb34a" cx="256.19" cy="256.19" r="27.529"/>
<circle fill="#3bb34a" cx="338.778" cy="256.19" r="27.529"/>
</g>
<g>
<path fill="#3bb34a" d="M155.059,412c-7.046,0-14.092-2.688-19.467-8.063L7.121,275.467c-10.751-10.752-10.751-28.183,0-38.933
l128.471-128.47c10.751-10.75,28.182-10.75,38.933,0c10.749,10.75,10.749,28.181,0,38.932L65.521,256l109.004,109.004
c10.749,10.751,10.749,28.181,0,38.932C169.148,409.312,162.105,412,155.059,412z"/>
<path fill="#3bb34a" d="M356.941,412c7.046,0,14.092-2.688,19.467-8.063l128.47-128.47c10.752-10.752,10.752-28.183,0-38.933
l-128.47-128.47c-10.752-10.75-28.182-10.75-38.934,0c-10.748,10.75-10.748,28.181,0,38.932L446.479,256L337.475,365.004
c-10.748,10.751-10.748,28.181,0,38.932C342.852,409.312,349.896,412,356.941,412z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: IcoMoon.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 16 16" xml:space="preserve" fill="#3bb34a"> <path d="M 48.00,38.994c0.00,4.971-4.029,9.00-9.00,9.00l-5.268,0.00 l 2.64-2.64 c 0.966-0.972, 1.68-2.124, 2.115-3.36L39.00,41.994 c 1.659,0.00, 3.00-1.341, 3.00-3.00L42.00,11.997 L6.00,11.997 l0.00,26.997 c0.00,1.659, 1.344,3.00, 3.00,3.00l0.51,0.00 c 0.438,1.242, 1.152,2.397, 2.124,3.369l 2.634,2.631L9.00,47.994 c-4.971,0.00-9.00-4.029-9.00-9.00L0.00,8.997 c0.00-4.971, 4.029-9.00, 9.00-9.00l30.00,0.00 c 4.971,0.00, 9.00,4.029, 9.00,9.00L48.00,38.994 z M 9.00,2.997c-1.656,0.00-3.00,1.344-3.00,3.00s 1.344,3.00, 3.00,3.00s 3.00-1.344, 3.00-3.00S 10.656,2.997, 9.00,2.997z M 18.00,35.994l3.00,0.00 L21.00,26.997 c0.00-1.659, 1.344-3.00, 3.00-3.00 c 1.659,0.00, 3.00,1.341, 3.00,3.00l0.00,8.997 l3.00,0.00 c 1.212,0.00, 2.31,0.732, 2.772,1.854c 0.462,1.125, 0.204,2.415-0.651,3.27l-6.00,6.00C 25.536,47.703, 24.768,48.00, 24.00,48.00 s-1.536-0.297-2.121-0.882l-6.00-6.00c-0.858-0.855-1.113-2.151-0.651-3.27C 15.69,36.726, 16.788,35.994, 18.00,35.994z" ></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1000mm"
height="1000mm"
viewBox="0 0 1000 1000"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xml:space="preserve"
sodipodi:docname="logo.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#2d2d2d"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#454545"
inkscape:document-units="px"
inkscape:zoom="0.057101021"
inkscape:cx="192.64104"
inkscape:cy="-604.19235"
inkscape:window-width="2194"
inkscape:window-height="1138"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><g
id="g1"
transform="translate(13.547694,22.337196)"><path
style="fill:#20c5b5;fill-opacity:1"
d="m 271.56929,741.91167 c -0.091,-0.14756 0.1372,-1.00514 0.5075,-1.90572 0.6346,-1.54353 22.1751,-50.80875 148.417,-339.44407 28.7625,-65.7615 52.7197,-120.551 53.2383,-121.7544 l 0.9428,-2.1881 h -18.0158 -18.0158 l -76.317,174.2676 c -41.9744,95.84719 -87.5411,199.9074 -101.2593,231.24493 -13.7182,31.33753 -25.1333,57.34685 -25.3668,57.79849 -0.2336,0.45164 -0.7092,0.98013 -1.057,1.17442 -0.4996,0.2791 -8.4542,0.33402 -37.8997,0.26166 -20.4971,-0.0504 -37.3083,-0.12994 -37.3582,-0.17681 -0.05,-0.0469 6.7005,-15.62895 15.0009,-34.62685 19.3618,-44.31487 155.6842,-356.26024 173.9466,-398.04024 l 13.8835,-31.762 -17.5022,-0.073 c -9.6261,-0.04 -17.59,0.024 -17.6974,0.1411 -0.166,0.1819 -160.2068,365.52815 -192.0371,438.38856 -7.4539,17.06209 -10.979,24.82126 -11.544,25.40961 l -0.8134,0.84698 h -5.8534 c -38.009999,0 -68.953899,-0.18458 -68.953899,-0.41132 0,-0.14858 5.5115,-12.88515 12.2477,-28.30348 15.9566,-36.52257 184.113899,-421.31205 203.429599,-465.50215 l 14.8707,-34.0206 150.1152,-0.071 c 82.5635,-0.039 150.1153,-0.018 150.1153,0.046 0,0.2284 -3.7189,8.7317 -84.8383,193.9853 -45.1487,103.10657 -96.5512,220.49706 -114.2278,260.86778 -17.6767,40.37073 -32.3592,73.56203 -32.6278,73.75846 -0.388,0.2837 -8.1659,0.35714 -37.8264,0.35714 -23.2847,0 -37.4004,-0.10098 -37.5038,-0.26829 z"
id="path2" /><path
style="fill:#ffffff;fill-opacity:1"
d="m 394.13379,741.87517 c -0.073,-0.11762 5.8119,-13.80705 13.077,-30.42095 7.2651,-16.6139 48.2414,-110.31089 91.0584,-208.21553 42.817,-97.90465 87.3104,-199.67015 98.8743,-226.14555 11.5638,-26.4754 22.42275,-51.3319 24.13095,-55.2368 2.143,-4.8988 3.3665,-7.3494 3.9466,-7.9052 l 0.8407,-0.8055 h 33.7134 33.7134 l 0.7136,0.7137 c 0.4491,0.449 4.2591,8.823 10.2764,22.5863 5.2596,12.0299 19.0587,43.5979 30.6648,70.1509 33.8184,77.3715 90.147,206.18781 141.1725,322.84322 26.0474,59.55024 47.7254,109.15363 48.1733,110.22975 0.6285,1.51036 0.7326,2.00838 0.4564,2.18372 -0.1969,0.12494 -16.8487,0.2291 -37.0041,0.23148 -40.0627,0.005 -37.6025,0.10707 -38.8194,-1.61481 -0.3034,-0.42937 -4.8376,-10.94451 -10.0759,-23.36699 -32.5572,-77.2083 -177.6141,-420.70687 -178.8963,-423.63117 l -0.5556,-1.267 -3.0975,7.3371 c -1.7036,4.0354 -44.29755,104.956 -94.65315,224.26813 -54.5055,129.14474 -91.8182,217.20384 -92.2044,217.60508 l -0.6487,0.67398 h -37.3623 c -20.5492,0 -37.4218,-0.0962 -37.4944,-0.21386 z"
id="path1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: IcoMoon.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 16 16" xml:space="preserve" fill="#3bb34a"> <path d="M 45.00,21.00l-3.00,0.00 c-0.105,0.00-0.192,0.051-0.297,0.06 c-0.456-2.769-1.527-5.316-3.093-7.506c 0.075-0.063, 0.168-0.087, 0.24-0.159l 2.121-2.121c 1.173-1.173, 1.173-3.069,0.00-4.242 s-3.072-1.173-4.245,0.00L 34.605,9.15c-0.069,0.069-0.096,0.165-0.159,0.24C 32.259,7.821, 29.709,6.753, 26.94,6.297 C 26.949,6.192, 27.00,6.105, 27.00,6.00L27.00,3.00 c0.00-1.656-1.341-3.00-3.00-3.00S 21.00,1.344, 21.00,3.00l0.00,3.00 c0.00,0.105, 0.051,0.192, 0.06,0.297 C 18.291,6.753, 15.741,7.821, 13.554,9.39C 13.491,9.315, 13.464,9.222, 13.395,9.15L 11.274,7.029c-1.173-1.173-3.072-1.173-4.245,0.00 c-1.17,1.173-1.17,3.069,0.00,4.242l 2.124,2.121C 9.222,13.464, 9.312,13.491, 9.39,13.554C 7.821,15.744, 6.753,18.291, 6.297,21.06 C 6.192,21.051, 6.105,21.00, 6.00,21.00L3.00,21.00 C 1.341,21.00,0.00,22.344,0.00,24.00s 1.341,3.00, 3.00,3.00l3.00,0.00 c 0.105,0.00, 0.192-0.048, 0.297-0.06 c 0.456,2.769, 1.527,5.316, 3.093,7.506c-0.075,0.063-0.168,0.087-0.237,0.159l-2.124,2.121c-1.17,1.173-1.17,3.072,0.00,4.242 c 1.173,1.173, 3.072,1.173, 4.245,0.00l 2.121-2.121c 0.069-0.069, 0.096-0.165, 0.159-0.24c 2.187,1.569, 4.737,2.637, 7.506,3.096 C 21.051,41.808, 21.00,41.895, 21.00,42.00l0.00,3.00 c0.00,1.659, 1.341,3.00, 3.00,3.00s 3.00-1.341, 3.00-3.00l0.00,-3.00 c0.00-0.105-0.051-0.192-0.06-0.297 c 2.769-0.459, 5.316-1.527, 7.506-3.096c 0.063,0.075, 0.087,0.171, 0.159,0.24l 2.121,2.121c 1.173,1.173, 3.072,1.173, 4.245,0.00 c 1.173-1.17, 1.173-3.069,0.00-4.242l-2.121-2.121c-0.072-0.069-0.165-0.093-0.24-0.159c 1.566-2.19, 2.637-4.737, 3.093-7.506 C 41.808,26.952, 41.895,27.00, 42.00,27.00l3.00,0.00 c 1.659,0.00, 3.00-1.344, 3.00-3.00S 46.659,21.00, 45.00,21.00z M 24.00,33.00c-4.971,0.00-9.00-4.032-9.00-9.00c0.00-4.971, 4.029-9.00, 9.00-9.00s 9.00,4.029, 9.00,9.00 C 33.00,28.968, 28.971,33.00, 24.00,33.00z" ></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg width='120px' height='120px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-spin"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><g transform="translate(50 50)"><g transform="rotate(0) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(45) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.12s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.12s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(90) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.25s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.25s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(135) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.37s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.37s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(180) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.5s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.5s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(225) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.62s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.62s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(270) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.75s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.75s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(315) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#3bb34a"><animate attributeName="opacity" from="1" to="0.1" begin="0.87s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.87s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g></g></svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: IcoMoon.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 16 16" xml:space="preserve" fill="#3bb34a"> <path d="M 12.00,39.00c-0.549,0.00-1.095-0.15-1.578-0.447C 9.54,38.004, 9.00,37.041, 9.00,36.00L9.00,12.00 c0.00-1.041, 0.54-2.007, 1.422-2.553C 10.905,9.153, 11.451,9.00, 12.00,9.00c 0.456,0.00, 0.921,0.105, 1.341,0.315l 24.00,12.00C 38.358,21.825, 39.00,22.863, 39.00,24.00 s-0.642,2.178-1.659,2.685l-24.00,12.00C 12.921,38.895, 12.456,39.00, 12.00,39.00z" ></path></svg>

After

Width:  |  Height:  |  Size: 786 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: IcoMoon.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 16 16" xml:space="preserve" fill="#3bb34a"> <path d="M 39.00,48.00l-6.00,0.00 l0.00,-6.00 l6.00,0.00 c 1.659,0.00, 3.00-1.341, 3.00-3.00L42.00,12.00 L6.00,12.00 l0.00,27.00 c0.00,1.659, 1.341,3.00, 3.00,3.00l6.00,0.00 l0.00,6.00 L9.00,48.00 c-4.971,0.00-9.00-4.032-9.00-9.00L0.00,9.00 c0.00-4.971, 4.029-9.00, 9.00-9.00l30.00,0.00 c 4.971,0.00, 9.00,4.029, 9.00,9.00l0.00,30.00 C 48.00,43.968, 43.971,48.00, 39.00,48.00z M 9.00,3.00C 7.341,3.00, 6.00,4.344, 6.00,6.00 s 1.341,3.00, 3.00,3.00s 3.00-1.344, 3.00-3.00S 10.659,3.00, 9.00,3.00z M 21.879,24.876C 22.464,24.291, 23.232,23.997, 24.00,23.997s 1.536,0.294, 2.121,0.879l 6.00,6.00 c 0.855,0.858, 1.113,2.148, 0.651,3.273C 32.31,35.268, 31.212,36.00, 30.00,36.00L27.00,36.00 l0.00,9.00 c0.00,1.659-1.341,3.00-3.00,3.00c-1.656,0.00-3.00-1.341-3.00-3.00l0.00,-9.00 L18.00,36.00 c-1.212,0.00-2.31-0.732-2.772-1.851c-0.462-1.122-0.207-2.415, 0.651-3.273L 21.879,24.876z" ></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,5 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48" height="48" viewBox="0 0 24 24" enable-background="new 0 0 16 16" xml:space="preserve" fill="#3bb34a">
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/>
</svg>

After

Width:  |  Height:  |  Size: 591 B

View file

@ -0,0 +1,4 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48" height="48" viewBox="0 0 24 24" enable-background="new 0 0 16 16" xml:space="preserve" fill="#3bb34a">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

912
public/svgnest/index.html Normal file
View file

@ -0,0 +1,912 @@
<!doctype html>
<html itemscope="" itemtype="http://schema.org/WebPage" lang="en">
<head><base href="/svgnest/">
<link rel="shortcut icon" href="/favicon32.gif" type="image/x-icon" sizes="32x32" />
<link rel="shortcut icon" href="/favicon16.gif" type="image/x-icon" sizes="16x16" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MAnest - MakeArmy hosted nesting app powered by SVGnest!</title>
<meta name="description" content="A completely free and open source application for automatic nesting. Comes with advanced features like part-in-part nesting and concave area detection." />
<link type="text/css" rel="stylesheet" media="all" href="font/latolatinfonts.css" />
<link rel="stylesheet" type="text/css" href="style.css" />
<script src="util/pathsegpolyfill.js"></script>
<script src="util/matrix.js"></script>
<script src="util/domparser.js"></script>
<script src="util/clipper.js"></script>
<script src="util/parallel.js"></script>
<script src="util/geometryutil.js"></script>
<script src="util/placementworker.js"></script>
<script src="svgparser.js"></script>
<script src="svgnest.js"></script>
<script src="util/filesaver.js"></script>
<script>
// UI-specific stuff, button clicks go here
function ready(fn){
if (document.readyState != 'loading'){
fn();
}
else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function(){
// FAQ toggle
var faq = document.getElementById('faq');
var faqbutton = document.getElementById('faqbutton');
var faqvisible = false;
faqbutton.onclick = function(e){
if(!faqvisible){
faq.setAttribute('style','display: block');
}
else{
faq.removeAttribute('style');
}
faqvisible = !faqvisible;
};
function hideSplash(){
var splash = document.getElementById('splash');
var svgnest = document.getElementById('svgnest');
if(splash){
splash.remove();
}
svgnest.setAttribute('style','display: block');
}
var demo = document.getElementById('demo');
var upload = document.getElementById('upload');
var display = document.getElementById('select');
demo.onclick = function(){
try{
var svg = window.SvgNest.parsesvg(display.innerHTML);
display.innerHTML = '';
display.appendChild(svg);
}
catch(e){
message.innerHTML = e;
message.className = 'error animated bounce';
return;
}
hideSplash();
message.innerHTML = 'Click on the outline to use as the bin';
message.className = 'active animated bounce';
attachSvgListeners(svg);
};
var message = document.getElementById('message');
if(!document.createElementNS || !document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect){
message.innerHTML = 'Your browser does not have SVG support';
message.className = 'error animated bounce';
return
}
if (!window.SvgNest) {
message.innerHTML = "Couldn't initialize SVGnest";
message.className = 'error animated bounce';
return;
}
if(!window.File || !window.FileReader){
message.innerHTML = 'Your browser does not have file upload support';
message.className = 'error animated bounce';
return
}
if(!window.Worker){
message.innerHTML = 'Your browser does not have web worker support';
message.className = 'error animated bounce';
return
}
// button clicks
var upload = document.getElementById('upload');
var start = document.getElementById('start');
var download = document.getElementById('download');
var startlabel = document.getElementById('startlabel');
var fileinput = document.getElementById('fileinput');
var config = document.getElementById('config');
var configbutton = document.getElementById('configbutton');
var configsave = document.getElementById('configsave');
var zoomin = document.getElementById('zoominbutton');
var zoomout = document.getElementById('zoomoutbutton');
var exit = document.getElementById('exitbutton');
var isworking = false;
start.onclick = function(){
if(this.className == 'button start disabled'){
return false;
}
iterations = 0;
if(isworking){
stopnest();
}
else{
startnest();
}
display.className = 'disabled';
document.getElementById('info_time').setAttribute('style','display: none');
};
function startnest(){
// Once started, don't allow this anymore
document.removeEventListener('dragover', FileDragHover, false);
document.removeEventListener('dragleave', FileDragHover, false);
document.removeEventListener('drop', FileDrop, false);
SvgNest.start(progress, renderSvg);
startlabel.innerHTML = 'Stop Nest';
start.className = 'button spinner';
configbutton.className = 'button config disabled';
config.className = '';
zoomin.className = 'button zoomin disabled';
zoomout.className = 'button zoomout disabled';
var svg = document.querySelector('#select svg');
if(svg){
svg.removeAttribute('style');
}
isworking = true;
}
function stopnest(){
SvgNest.stop();
startlabel.innerHTML = 'Start Nest';
start.className = 'button start';
configbutton.className = 'button config';
isworking = false;
}
// config
var configvisible = false;
configbutton.onclick = function(){
if(this.className == 'button config disabled'){
return false;
}
if(!configvisible){
config.className = 'active';
configbutton.className = 'button close';
}
else{
config.className = '';
configbutton.className = 'button config';
}
configvisible = !configvisible;
return false;
}
configsave.onclick = function(){
var c = {};
var inputs = document.querySelectorAll('#config input');
for (var i = 0; i < inputs.length; i++) {
var key = inputs[i].getAttribute('data-config');
if (inputs[i].getAttribute('type') === 'text') {
c[key] = inputs[i].value;
} else if (inputs[i].getAttribute('type') === 'checkbox') {
c[key] = inputs[i].checked;
}
}
window.SvgNest.config(c);
if (isworking) stopnest(); // invalidate current nest
configvisible = false;
config.className = '';
configbutton.className = 'button config'; // <— ensure the gear comes back
return false;
};
upload.onclick = function(){
fileinput.click();
}
document.addEventListener('dragover', FileDragHover, false);
document.addEventListener('dragleave', FileDragHover, false);
document.addEventListener('drop', FileDrop, false);
function FileDragHover(e){
e.stopPropagation();
e.preventDefault();
upload.style.backgroundColor = (e.type == "dragover" ? "#d7e9b7" : "");
}
function FileDrop(e){
e.stopPropagation(); // Make sure not to replace website by file
e.preventDefault();
handleFile(e.dataTransfer.files[0]);
}
download.onclick = function(){
if(download.className == 'button download disabled'){
return false;
}
var bins = document.getElementById('bins');
if(bins.children.length == 0){
message.innerHTML = 'No SVG to export';
message.className = 'error animated bounce';
return
}
var svg;
svg = display.querySelector('svg');
if(!svg){
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
}
svg = svg.cloneNode(false);
// maintain stroke, fill etc of input
if(SvgNest.style){
svg.appendChild(SvgNest.style);
}
var binHeight = parseInt(bins.children[0].getAttribute('height'));
for(var i=0; i<bins.children.length; i++){
var b = bins.children[i];
var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
group.setAttribute('transform', 'translate(0 '+binHeight*1.1*i+')');
for(var j=0; j<b.children.length; j++){
group.appendChild(b.children[j].cloneNode(true));
}
svg.appendChild(group);
}
var output;
if(typeof XMLSerializer != 'undefined'){
output = (new XMLSerializer()).serializeToString(svg);
}
else{
output = svg.outerHTML;
}
var blob = new Blob([output], {type: "image/svg+xml;charset=utf-8"});
saveAs(blob, "SVGnest-output.svg");
}
var zoomlevel = 1.0;
zoomin.onclick = function(){
if(this.className == 'button zoomin disabled'){
return false;
}
zoomlevel *= 1.2;
var svg = document.querySelector('#select svg');
if(svg){
svg.setAttribute('style', 'transform-origin: top left; transform:scale('+zoomlevel+'); -webkit-transform:scale('+zoomlevel+'); -moz-transform:scale('+zoomlevel+'); -ms-transform:scale('+zoomlevel+'); -o-transform:scale('+zoomlevel+');');
}
}
zoomout.onclick = function(){
if(this.className == 'button zoomout disabled'){
return false;
}
zoomlevel *= 0.8;
if(zoomlevel < 0.02){
zoomlevel = 0.02;
}
var svg = document.querySelector('#select svg');
if(svg){
svg.setAttribute('style', 'transform-origin: top left; transform:scale('+zoomlevel+'); -webkit-transform:scale('+zoomlevel+'); -moz-transform:scale('+zoomlevel+'); -ms-transform:scale('+zoomlevel+'); -o-transform:scale('+zoomlevel+');');
}
}
exit.onclick = function(){
location.reload();
}
fileinput.onchange = function(e){
handleFile(e.target.files[0]);
}
function handleFile(file){
if(!file){
return;
}
if(!file.type || (file.type.search('svg') < 0 && file.type.search('xml') < 0 && file.type.search('text') < 0)){
message.innerHTML = 'Only SVG files allowed';
message.className = 'error animated bounce';
return
}
var reader = new FileReader();
var input = this;
reader.onload = function(e) {
input.value = null;
if(reader.result){
try{
var svg = window.SvgNest.parsesvg(reader.result);
{
var wholeSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
// Copy relevant scaling info
wholeSVG.setAttribute('width',svg.getAttribute('width'));
wholeSVG.setAttribute('height',svg.getAttribute('height'));
wholeSVG.setAttribute('viewBox',svg.getAttribute('viewBox'));
var rect = document.createElementNS(wholeSVG.namespaceURI,'rect');
rect.setAttribute('x', wholeSVG.viewBox.baseVal.x);
rect.setAttribute('y', wholeSVG.viewBox.baseVal.x);
rect.setAttribute('width', wholeSVG.viewBox.baseVal.width);
rect.setAttribute('height', wholeSVG.viewBox.baseVal.height);
rect.setAttribute('class', 'fullRect');
wholeSVG.appendChild(rect);
}
display.innerHTML = '';
display.appendChild(wholeSVG); // As a default bin in background
display.appendChild(svg);
}
catch(e){
message.innerHTML = e;
message.className = 'error animated bounce';
return;
}
hideSplash();
message.innerHTML = 'Click on the outline to use as the bin';
message.className = 'active animated bounce';
start.className = 'button start disabled';
attachSvgListeners(svg);
attachSvgListeners(wholeSVG);
}
}
reader.readAsText(file);
};
function attachSvgListeners(svg){
// attach event listeners
for(var i=0; i<svg.childNodes.length; i++){
var node = svg.childNodes[i];
if(node.nodeType == 1){
node.onclick = function(){
if(display.className == 'disabled'){
return;
}
var currentbin = document.querySelector('#select .active');
if(currentbin){
var className = currentbin.getAttribute('class').replace('active', '').trim();
if(!className)
currentbin.removeAttribute('class');
else
currentbin.setAttribute('class', className);
}
window.SvgNest.setbin(this);
this.setAttribute('class',(this.getAttribute('class') ? this.getAttribute('class')+' ' : '') + 'active');
start.className = 'button start animated bounce';
message.className = '';
}
}
}
}
var prevpercent = 0;
var startTime = null;
function progress(percent){
var transition = percent > prevpercent ? '; transition: width 0.1s' : '';
document.getElementById('info_progress').setAttribute('style','width: '+Math.round(percent*100)+'% ' + transition);
document.getElementById('info').setAttribute('style','display: block');
prevpercent = percent;
var now = new Date().getTime();
if(startTime && now){
var diff = now-startTime;
// show a time estimate for long-running placements
var estimate = (diff/percent)*(1-percent);
document.getElementById('info_time').innerHTML = millisecondsToStr(estimate)+' remaining';
if(diff > 5000 && percent < 0.3 && percent > 0.02 && estimate > 10000){
document.getElementById('info_time').setAttribute('style','display: block');
}
}
if(percent > 0.95 || percent < 0.02){
document.getElementById('info_time').setAttribute('style','display: none');
}
if(percent < 0.02){
startTime = new Date().getTime();
}
}
var iterations = 0;
function renderSvg(svglist, efficiency, placed, total){
iterations++;
document.getElementById('info_iterations').innerHTML = iterations;
if(!svglist || svglist.length == 0){
return;
}
var bins = document.getElementById('bins');
bins.innerHTML = '';
for(var i=0; i<svglist.length; i++){
if(svglist.length > 2){
svglist[i].setAttribute('class','grid');
}
bins.appendChild(svglist[i]);
}
if(efficiency || efficiency === 0){
document.getElementById('info_efficiency').innerHTML = Math.round(efficiency*100);
}
document.getElementById('info_placed').innerHTML = placed+'/'+total;
document.getElementById('info_placement').setAttribute('style','display: block');
display.setAttribute('style','display: none');
download.className = 'button download animated bounce';
}
message.onclick = function(e){
this.className='';
}
function millisecondsToStr (milliseconds) {
function numberEnding (number) {
return (number > 1) ? 's' : '';
}
var temp = Math.floor(milliseconds / 1000);
var years = Math.floor(temp / 31536000);
if (years) {
return years + ' year' + numberEnding(years);
}
var days = Math.floor((temp %= 31536000) / 86400);
if (days) {
return days + ' day' + numberEnding(days);
}
var hours = Math.floor((temp %= 86400) / 3600);
if (hours) {
return hours + ' hour' + numberEnding(hours);
}
var minutes = Math.floor((temp %= 3600) / 60);
if (minutes) {
return minutes + ' minute' + numberEnding(minutes);
}
var seconds = temp % 60;
if (seconds) {
return seconds + ' second' + numberEnding(seconds);
}
return 'less than a second';
}
});
</script>
</head>
<body>
<div id="splash">
<!-- a small splash page that explains what this thing is -->
<img src="img/logo.svg" alt="SVGnest" class="logo" style="display:block;margin:0 auto;max-width:220px;" />
<h1 class="title">MAnest</h1>
<em class="subscript">Fast Nesting for Makers</em>
<ul class="nav" id="splash-nav">
<li class="button start" id="demo">Example</li>
<li class="button upload" id="upload">Upload SVG</li>
<li class="button" id="faqbutton">FAQ</li>
<li><a class="button back" id="back" href="https://makearmy.io" rel="noopener">Back to Main</a></li>
</ul>
<p id="poweredby">
Powered by SVGnest, a free open source project by Jack000 &mdash;
visit the github <a href="https://github.com/Jack000/SVGnest" target="_blank" rel="noopener">here</a>.
</p>
<div id="faq">
<h1>What exactly is "nesting"?</h1>
<p>If you have some parts to cut out of a piece of metal/plastic/wood etc, you'd want to arrange the parts to use as little material as possible. This is a common problem if you use a laser cutter, plasma cutter, or CNC machine.</p>
<p>In computer terms this is called the irregular bin-packing problem</p>
<h1>How much does it cost?</h1>
<p>It's free and open source. The code and implementation details are on <a href="https://github.com/Jack000/SVGnest" target="_blank">Github</a></p>
<h1>Does it use inches? mm?</h1>
<p>SVG has its internal units, the distance related fields in the settings use SVG units, ie. pixels. The conversion between a pixel and real units depend on the exporting software, but it's typically 72 pixels = 1 inch</p>
<h1>My SVG text/image doesn't show up?</h1>
<p>Nesting only works for closed shapes, so SVG elements that don't represent closed shapes are removed. Convert text and any other elements to outlines first. Ensure that outlines do not intersect or overlap eachother. Outlines that are inside other outlines are considered holes.</p>
<h1>It doesn't ever stop?</h1>
<p>The software will continuously look for better solutions until you press the stop button. You can stop at any time and download the SVG file.</p>
<h1>Some parts seem to slightly overlap?</h1>
<p>Curved shapes are approximated with line segments. For a more accurate nest with curved parts, decrease the curve tolerance parameter in the configuration.</p>
<h1>I need help?</h1>
<p>Add an issue on Github or contact me personally: <a href="http://jack.works">jack.works</a>
</div>
</div>
<div id="svgnest">
<div id="controls">
<ul class="nav">
<li class="button start disabled" id="start"><span id="startlabel">Start Nest</span></li>
<li class="button download disabled" id="download">Download SVG</li>
<li class="button config" id="configbutton"></li>
<li class="button zoomin" id="zoominbutton"></li>
<li class="button zoomout" id="zoomoutbutton"></li>
<li class="button exit" id="exitbutton"></li>
</ul>
<div id="config">
<div id="configwrapper">
<input type="text" value="0" data-config="spacing" />
<h3>Space between parts</h3><span class="tooltip" title="The space between parts in SVG units (conversion depends on exporting software but usually 1 SVG unit = 1px = 1/72 inches = 0.3527777~ mm)">?</span>
<input type="text" value="0.3" data-config="curveTolerance" />
<h3>Curve tolerance</h3><span class="tooltip" title="The maximum error allowed when converting Beziers and arcs to line segments. In SVG units. Smaller tolerances will take longer to compute">?</span>
<input type="text" value="4" data-config="rotations" />
<h3>Part rotations</h3><span class="tooltip" title="Number of rotations to consider when inserting a part. Larger rotations will take longer to compute, and may also take longer to converge to a good solution">?</span>
<input type="text" value="10" data-config="populationSize" />
<h3>GA population</h3><span class="tooltip" title="The number of solutions in the Genetic Algorithm population. Larger populations will converge slower but may result in better solutions in the long run">?</span>
<input type="text" value="10" data-config="mutationRate" />
<h3>GA mutation rate</h3><span class="tooltip" title="Mutation rate (in percent) at each generation of the Genetic Algorithm. A 100% mutation rate is equivalent to random sampling">?</span>
<input type="checkbox" class="checkbox" data-config="useHoles" />
<h3>Part in Part</h3><span class="tooltip" title="Place parts in the holes of other parts. This will take much longer to compute ">?</span>
<input type="checkbox" class="checkbox" data-config="exploreConcave" />
<h3>Explore concave areas</h3><span class="tooltip" title="Try to solve for enclosed concave areas (eg. a jigsaw puzzle piece) This will take much longer to compute ">?</span>
<a href="#" class="button" id="configsave">Save Settings</a>
</div>
</div>
</div>
<div class="sidebar">
<div id="info">
<h2 id="info_time"></h2>
<div class="progress"><div class="progress_inner" id="info_progress"></div></div>
<span class="subscript">Placement progress</span>
<div id="info_placement">
<div class="column left">
<h1 class="label"><span id="info_efficiency"></span><sup>%</sup></h1>
<span class="subscript">Material Utilization</span>
</div>
<div class="column right">
<h1 class="label" id="info_iterations"></h1>
<span class="subscript">Iterations</span>
</div>
<div class="column left">
<h1 class="label"><span id="info_placed"></span></h1>
<span class="subscript">Parts placed</span>
</div>
</div>
</div>
</div>
<div id="select">
<svg version="1.1" id="svg2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1147.592px" height="1397.27px" viewBox="0 0 1147.592 1397.27" enable-background="new 0 0 1147.592 1397.27" xml:space="preserve">
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="684.045,443.734 688.396,447.215
666.488,450.935 666.488,432.651 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="697.067,404.901 697.067,415.601
709.719,415.905 710.293,406.067 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="723.908,440.886 715.362,442.463
709.719,435.85 712.627,427.66 721.17,426.079 726.81,432.692 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="617.292,458.369 618.742,465.049
599.735,465.566 599.735,447.28 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="619.963,428.025 619.963,438.722
633.189,438.467 633.189,429.192 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="646.805,464.01 638.259,465.587
632.618,458.974 635.523,450.784 644.069,449.207 649.706,455.819 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="777.062,428.025 746.983,457.397
760.468,481.256 782.898,488.173 788.995,478.145 814.669,479.959 818.817,438.722 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="666.589,476.434 664.255,503.557
681.632,505.633 694.857,499.916 692.262,483.07 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="599.735,501.612 636.247,531.953
680.981,531.953 680.981,554.904 599.735,554.904 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="759.315,529.301 775.721,500.886
792.128,529.301 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="741.307,428.338 734.229,419.461
736.757,408.396 746.983,403.469 757.206,408.396 759.735,419.461 752.654,428.338 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="647.399,599.509 651.749,602.988
629.84,606.702 629.84,588.419 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="650.068,569.167 650.068,579.861
662.723,580.171 663.294,570.334 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="758.061,626.35 800.104,626.854
807.438,600.447 796.468,579.694 785.065,582.478 768.195,563.04 736.104,589.264 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="694.308,592.604 687.232,583.727
689.758,572.661 699.984,567.735 710.211,572.661 712.736,583.727 705.658,592.604 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="854.273,544.551 858.625,548.03
836.717,551.75 836.717,533.467 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="856.944,514.209 856.944,524.909
869.597,525.213 870.171,515.376 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="883.785,550.194 875.24,551.771
869.597,545.158 872.505,536.968 881.048,535.388 886.688,542.001 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="779.841,537.333 779.841,548.03
793.067,547.775 793.067,538.5 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="901.184,537.646 894.106,528.769
896.635,517.703 906.861,512.777 917.084,517.703 919.613,528.769 912.532,537.646 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="836.717,410.401 827.633,456.421
894.106,463.102 910.101,434.273 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="676.909,605.146 668.363,606.727
662.723,600.113 665.628,591.92 674.174,590.346 679.814,596.959 "/>
<rect x="21.066" y="439.913" width="30.913" height="18.155" fill="none" stroke="#010101"/>
<rect x="106.758" y="452.881" width="29.563" height="5.188" fill="none" stroke="#010101"/>
<rect x="184.038" y="464.809" width="25.963" height="17.637" fill="none" stroke="#010101"/>
<rect x="305.408" y="427.01" width="17.01" height="40.393" fill="none" stroke="#010101"/>
<rect x="262.876" y="458.068" width="17.004" height="17.633" fill="none" stroke="#010101"/>
<rect x="338.931" y="427.01" width="47.904" height="25.868" fill="none" stroke="#010101"/>
<polygon fill="none" stroke="#010101" points="66.82,475.701 47.112,507.86 101.95,510.972 140.64,493.336 90.161,496.448 87.322,473.625 "/>
<polygon fill="none" stroke="#010101" points="196.401,495.929 224.016,523.938 348.972,517.196 271.061,492.297 227.967,473.625 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1065.045,441.528 1069.396,445.009
1047.488,448.729 1047.488,430.445 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1078.067,402.694 1078.067,413.395
1090.719,413.699 1091.293,403.861 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1104.908,438.68 1096.362,440.257
1090.719,433.644 1093.627,425.454 1102.17,423.873 1107.811,430.486 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="998.292,456.163 999.742,462.843
980.735,463.359 980.735,445.074 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1000.963,425.818 1000.963,436.516
1014.189,436.261 1014.189,426.985 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1027.805,461.804 1019.259,463.381
1013.618,456.768 1016.523,448.578 1025.069,447.001 1030.706,453.613 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="601.531,174.639 571.451,204.011
584.936,227.87 607.366,234.787 613.463,224.759 639.137,226.573 643.285,185.336 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1047.589,474.228 1045.255,501.351
1062.632,503.427 1075.857,497.71 1073.262,480.864 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="980.735,499.406 1017.247,529.747
1061.98,529.747 1061.98,552.698 980.735,552.698 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="583.783,275.915 600.188,247.5
616.596,275.915 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="565.774,174.952 558.696,166.075
561.225,155.01 571.451,150.083 581.674,155.01 584.203,166.075 577.122,174.952 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1028.399,597.303 1032.749,600.782
1010.84,604.496 1010.84,586.213 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1031.068,566.961 1031.068,577.655
1043.723,577.965 1044.294,568.128 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="582.528,372.964 624.57,373.468
631.906,347.062 620.936,326.309 609.533,329.092 592.663,309.654 560.571,335.878 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1075.308,590.397 1068.232,581.521
1070.758,570.455 1080.984,565.529 1091.211,570.455 1093.736,581.521 1086.658,590.397 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="678.741,291.165 683.093,294.645
661.185,298.364 661.185,280.081 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="681.412,260.823 681.412,271.524
694.064,271.827 694.639,261.99 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="708.253,296.809 699.708,298.386
694.064,291.773 696.973,283.582 705.516,282.002 711.155,288.615 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="604.309,283.947 604.309,294.645
617.535,294.389 617.535,285.114 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="725.651,284.26 718.574,275.383
721.104,264.317 731.329,259.392 741.552,264.317 744.081,275.383 737,284.26 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="661.185,157.016 652.101,203.035
718.574,209.716 734.568,180.887 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1057.909,602.939 1049.363,604.521
1043.723,597.907 1046.628,589.714 1055.174,588.14 1060.814,594.753 "/>
<rect x="402.066" y="437.707" width="30.912" height="18.155" fill="none" stroke="#010101" />
<rect x="487.758" y="450.675" width="29.564" height="5.188" fill="none" stroke="#010101" />
<rect x="565.949" y="691.507" width="25.963" height="17.636" fill="none" stroke="#010101" />
<rect x="687.32" y="653.708" width="17.01" height="40.392" fill="none" stroke="#010101" />
<rect x="644.789" y="684.767" width="17.004" height="17.634" fill="none" stroke="#010101" />
<rect x="720.844" y="653.708" width="47.904" height="25.868" fill="none" stroke="#010101" />
<polygon fill="none" stroke="#010101" points="447.82,473.495 428.112,505.654 482.95,508.766 521.641,491.13 471.161,494.242 468.322,471.419 "/>
<polygon fill="none" stroke="#010101" points="578.312,722.627 605.928,750.635 730.885,743.895 652.973,718.995 609.879,700.323 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="903.513,195.164 907.864,198.645
885.956,202.364 885.956,184.081 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="916.535,156.33 916.535,167.03
929.188,167.335 929.761,157.497 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="943.376,192.316 934.83,193.893
929.188,187.279 932.095,179.09 940.638,177.509 946.277,184.122 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="836.76,209.799 838.209,216.479
819.203,216.995 819.203,198.71 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="839.432,179.454 839.432,190.151
852.657,189.897 852.657,180.621 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="866.271,215.44 857.727,217.017
852.086,210.403 854.991,202.214 863.537,200.637 869.174,207.249 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="996.531,179.454 966.451,208.826
979.936,232.686 1002.366,239.603 1008.463,229.574 1034.137,231.389 1038.285,190.151 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="886.057,227.863 883.723,254.986
901.1,257.063 914.325,251.346 911.729,234.5 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="819.203,253.042 855.715,283.383
900.449,283.383 900.449,306.334 819.203,306.334 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="978.783,280.731 995.188,252.316
1011.596,280.731 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="960.774,179.768 953.696,170.891
956.225,159.825 966.451,154.899 976.674,159.825 979.203,170.891 972.122,179.768 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="866.867,350.939 871.217,354.418
849.308,358.132 849.308,339.849 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="869.536,320.597 869.536,331.291
882.19,331.601 882.762,321.764 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="977.528,377.779 1019.57,378.283
1026.906,351.877 1015.936,331.124 1004.533,333.907 987.663,314.47 955.571,340.693 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="913.775,344.033 906.7,335.156
909.226,324.091 919.452,319.165 929.679,324.091 932.204,335.156 925.126,344.033 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1073.741,295.981 1078.093,299.46
1056.186,303.18 1056.186,284.897 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1076.412,265.639 1076.412,276.339
1089.064,276.643 1089.639,266.806 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1103.253,301.624 1094.708,303.201
1089.064,296.588 1091.973,288.398 1100.516,286.817 1106.155,293.431 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="999.309,288.763 999.309,299.46
1012.535,299.204 1012.535,289.93 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1120.651,289.075 1113.574,280.198
1116.104,269.133 1126.329,264.207 1136.552,269.133 1139.081,280.198 1132,289.075 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1056.186,161.831 1047.101,207.851
1113.574,214.531 1129.568,185.702 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="896.377,356.575 887.831,358.156
882.19,351.543 885.096,343.35 893.642,341.776 899.282,348.389 "/>
<rect x="797.979" y="671.427" width="30.912" height="18.155" fill="none" stroke="#010101" />
<rect x="883.67" y="684.395" width="29.564" height="5.188" fill="none" stroke="#010101" />
<rect x="916.535" y="655.978" width="25.963" height="17.637" fill="none" stroke="#010101" />
<rect x="1037.906" y="618.179" width="17.01" height="40.393" fill="none" stroke="#010101" />
<rect x="995.375" y="649.237" width="17.004" height="17.633" fill="none" stroke="#010101" />
<rect x="1071.43" y="618.179" width="47.904" height="25.868" fill="none" stroke="#010101" />
<polygon fill="none" stroke="#010101" points="843.732,707.215 824.023,739.374 878.861,742.484 917.553,724.85 867.072,727.962 864.234,705.139 "/>
<polygon fill="none" stroke="#010101" points="928.898,687.098 956.514,715.105 1081.471,708.365 1003.559,683.466 960.465,664.794 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="105.375,574.337 109.727,577.817
87.819,581.537 87.819,563.254 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="118.398,535.503 118.398,546.203
131.049,546.508 131.624,536.67 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="145.239,571.488 136.693,573.065
131.049,566.452 133.958,558.263 142.5,556.682 148.14,563.295 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="38.623,588.972 40.072,595.651
21.066,596.168 21.066,577.883 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="41.294,558.627 41.294,569.324
54.52,569.069 54.52,559.794 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="68.135,594.612 59.589,596.189
53.949,589.576 56.854,581.387 65.4,579.81 71.037,586.422 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="198.394,558.627 168.314,587.999
181.798,611.858 204.229,618.775 210.326,608.747 236,610.562 240.148,569.324 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="87.919,607.036 85.585,634.159
102.962,636.235 116.188,630.519 113.592,613.673 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="21.066,632.215 57.578,662.556
102.312,662.556 102.312,685.507 21.066,685.507 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="180.646,659.903 197.051,631.488
213.458,659.903 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="162.637,558.94 155.559,550.062
158.087,538.998 168.314,534.071 178.537,538.998 181.066,550.062 173.985,558.94 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="68.73,730.111 73.08,733.591
51.17,737.305 51.17,719.021 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="71.399,699.77 71.399,710.464
84.053,710.773 84.625,700.938 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="115.638,723.206 108.563,714.329
111.088,703.264 121.315,698.338 131.542,703.264 134.067,714.329 126.989,723.206 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="275.604,675.153 279.956,678.633
258.047,682.354 258.047,664.069 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="278.275,644.812 278.275,655.512
290.927,655.815 291.501,645.979 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="305.116,680.797 296.571,682.374
290.927,675.761 293.835,667.57 302.378,665.99 308.018,672.604 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="201.171,667.936 201.171,678.633
214.398,678.377 214.398,669.104 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="322.514,668.248 315.438,659.371
317.965,648.306 328.192,643.38 338.415,648.306 340.944,659.371 333.863,668.248 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="258.047,541.004 248.963,587.023
315.438,593.704 331.431,564.875 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="98.24,735.748 89.694,737.329
84.053,730.716 86.958,722.521 95.504,720.948 101.145,727.562 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="504.59,600.795 508.941,604.275
487.033,607.995 487.033,589.712 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="517.611,561.961 517.611,572.661
530.264,572.966 530.838,563.128 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="545.365,716.138 536.818,717.715
531.176,711.102 534.084,702.912 542.627,701.331 548.266,707.944 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="437.837,615.43 439.287,622.109
420.28,622.626 420.28,604.341 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="440.508,585.085 440.508,595.782
453.734,595.527 453.734,586.252 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="467.35,621.07 458.804,622.646
453.163,616.034 456.068,607.845 464.614,606.268 470.251,612.88 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="598.52,813.989 568.439,843.361
581.924,867.221 604.355,874.138 610.451,864.109 636.125,865.924 640.273,824.687 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="487.134,633.494 484.8,660.617
502.177,662.693 515.402,656.977 512.807,640.131 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="420.28,658.673 456.792,689.014
501.526,689.014 501.526,711.965 420.28,711.965 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="580.771,915.266 597.178,886.851
613.584,915.266 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="562.764,814.303 555.686,805.426
558.213,794.359 568.439,789.434 578.662,794.359 581.191,805.426 574.111,814.303 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="467.944,756.569 472.294,760.049
450.385,763.763 450.385,745.479 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="470.613,726.229 470.613,736.922
483.268,737.231 483.839,727.395 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="514.852,749.664 507.777,740.787
510.303,729.722 520.529,724.796 530.756,729.722 533.281,740.787 526.203,749.664 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="675.73,930.516 680.082,933.995
658.174,937.715 658.174,919.432 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="678.4,900.174 678.4,910.874
691.053,911.178 691.627,901.341 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="705.242,936.159 696.697,937.736
691.053,931.123 693.961,922.933 702.504,921.353 708.145,927.966 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="601.297,923.298 601.297,933.995
614.523,933.739 614.523,924.465 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="563.383,919.432 559.621,926.865
550.156,929.546 550.156,937.715 559.621,940.869 563.383,946.966 571.941,946.966 575.18,942.553 581.07,937.736 581.07,929.546
575.18,926.087 570.123,919.432 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="722.641,923.609 715.562,914.734
718.092,903.668 728.318,898.742 738.541,903.668 741.07,914.734 733.988,923.609 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="658.174,796.366 649.09,842.386
715.562,849.066 731.557,820.237 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="497.454,762.206 488.908,763.787
483.268,757.174 486.173,748.98 494.719,747.406 500.359,754.02 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="900.502,834.515 904.854,837.995
882.945,841.715 882.945,823.432 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="913.523,795.681 913.523,806.381
926.176,806.686 926.75,796.848 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="940.365,831.666 931.818,833.243
926.176,826.63 929.084,818.441 937.627,816.859 943.266,823.473 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="833.748,849.15 835.197,855.829
816.191,856.346 816.191,838.061 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="836.42,818.805 836.42,829.502
849.646,829.247 849.646,819.972 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="863.262,854.79 854.715,856.367
849.074,849.754 851.98,841.564 860.525,839.987 866.162,846.6 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="993.52,818.805 963.439,848.177
976.924,872.036 999.355,878.953 1005.451,868.925 1031.125,870.739 1035.273,829.502 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="957.764,819.118 950.686,810.241
953.213,799.176 963.439,794.249 973.662,799.176 976.191,810.241 969.111,819.118 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1053.174,801.182 1044.09,847.201
1110.562,853.882 1126.557,825.053 "/>
<rect width="511.822" height="339.235" fill="none" stroke="#010101" />
<path fill="none" stroke="#010101" d="M746.843,60.679c-2.465,1.232-7.395,2.465-13.711,2.465c-14.635,0-25.65-9.243-25.65-26.266
c0-16.252,11.016-27.268,27.113-27.268c6.471,0,10.553,1.387,12.324,2.311l-1.617,5.469c-2.542-1.232-6.162-2.157-10.476-2.157
c-12.17,0-20.258,7.78-20.258,21.414c0,12.709,7.317,20.874,19.95,20.874c4.082,0,8.241-0.848,10.938-2.157L746.843,60.679z"/>
<path fill="none" stroke="#010101" d="M755.008,7.685h6.778v54.688h-6.778V7.685z"/>
<path fill="none" stroke="#010101" d="M780.734,14.617c0.077,2.311-1.617,4.16-4.313,4.16c-2.388,0-4.082-1.849-4.082-4.16c0-2.388,1.771-4.236,4.236-4.236
C779.117,10.38,780.734,12.229,780.734,14.617z M773.187,62.373V25.092h6.777v37.281H773.187z"/>
<path fill="none" stroke="#010101" d="M817.784,60.986c-1.771,0.925-5.7,2.157-10.707,2.157c-11.246,0-18.563-7.626-18.563-19.026
c0-11.477,7.856-19.795,20.027-19.795c4.005,0,7.548,1.001,9.397,1.925l-1.541,5.238c-1.617-0.924-4.159-1.771-7.856-1.771
c-8.55,0-13.172,6.316-13.172,14.096c0,8.627,5.546,13.942,12.94,13.942c3.852,0,6.394-1.001,8.318-1.849L817.784,60.986z"/>
<path fill="none" stroke="#010101" d="M832.42,42.192h0.154c0.924-1.31,2.233-2.927,3.312-4.237l10.938-12.863h8.164L840.585,40.42l16.406,21.953h-8.241
l-12.864-17.87l-3.466,3.852v14.019h-6.701V7.685h6.701V42.192z"/>
<path fill="none" stroke="#010101" d="M885.107,16.157h-15.79v-5.7h38.437v5.7h-15.867v46.216h-6.779V16.157z"/>
<path fill="none" stroke="#010101" d="M912.762,7.685h6.778v23.262h0.154c1.078-1.926,2.772-3.62,4.853-4.775c2.002-1.156,4.391-1.926,6.932-1.926
c5.007,0,13.018,3.081,13.018,15.944v22.184h-6.778V40.96c0-6.008-2.233-11.092-8.627-11.092c-4.39,0-7.856,3.081-9.089,6.778
c-0.385,0.924-0.462,1.925-0.462,3.235v22.492h-6.778V7.685z"/>
<path fill="none" stroke="#010101" d="M963.062,14.617c0.077,2.311-1.618,4.16-4.313,4.16c-2.389,0-4.083-1.849-4.083-4.16c0-2.388,1.771-4.236,4.236-4.236
C961.443,10.38,963.062,12.229,963.062,14.617z M955.513,62.373V25.092h6.778v37.281H955.513z"/>
<path fill="none" stroke="#010101" d="M972.611,55.44c2.003,1.31,5.546,2.696,8.936,2.696c4.93,0,7.24-2.465,7.24-5.546c0-3.235-1.926-5.007-6.933-6.855
c-6.701-2.388-9.859-6.085-9.859-10.553c0-6.008,4.853-10.938,12.864-10.938c3.773,0,7.086,1.078,9.166,2.311l-1.695,4.93
c-1.463-0.924-4.159-2.157-7.625-2.157c-4.006,0-6.239,2.311-6.239,5.084c0,3.081,2.233,4.467,7.086,6.316
c6.471,2.465,9.782,5.7,9.782,11.246c0,6.547-5.083,11.169-13.941,11.169c-4.082,0-7.856-1.001-10.476-2.542L972.611,55.44z"/>
<polygon fill="none" stroke="#010101" points="676.149,23 616.756,23 616.756,1 558.443,34.667 616.756,68.333 616.756,46.333 676.149,46.333 "/>
</svg>
</div>
<div id="bins"></div>
<input id="fileinput" type="file" style="visibility:hidden" />
</div>
<div id="messagewrapper">
<div id="message"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,914 @@
<!doctype html>
<html itemscope="" itemtype="http://schema.org/WebPage" lang="en">
<head><base href="/svgnest/">
<link rel="shortcut icon" href="/favicon32.gif" type="image/x-icon" sizes="32x32" />
<link rel="shortcut icon" href="/favicon16.gif" type="image/x-icon" sizes="16x16" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MAnest - MakeArmy hosted nesting app powered by SVGnest!</title>
<meta name="description" content="A completely free and open source application for automatic nesting. Comes with advanced features like part-in-part nesting and concave area detection." />
<link type="text/css" rel="stylesheet" media="all" href="font/latolatinfonts.css" />
<link rel="stylesheet" type="text/css" href="style.css" />
<script src="util/pathsegpolyfill.js"></script>
<script src="util/matrix.js"></script>
<script src="util/domparser.js"></script>
<script src="util/clipper.js"></script>
<script src="util/parallel.js"></script>
<script src="util/geometryutil.js"></script>
<script src="util/placementworker.js"></script>
<script src="svgparser.js"></script>
<script src="svgnest.js"></script>
<script src="util/filesaver.js"></script>
<script>
// UI-specific stuff, button clicks go here
function ready(fn){
if (document.readyState != 'loading'){
fn();
}
else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function(){
// FAQ toggle
var faq = document.getElementById('faq');
var faqbutton = document.getElementById('faqbutton');
var faqvisible = false;
faqbutton.onclick = function(e){
if(!faqvisible){
faq.setAttribute('style','display: block');
}
else{
faq.removeAttribute('style');
}
faqvisible = !faqvisible;
};
function hideSplash(){
var splash = document.getElementById('splash');
var svgnest = document.getElementById('svgnest');
if(splash){
splash.remove();
}
svgnest.setAttribute('style','display: block');
}
var demo = document.getElementById('demo');
var upload = document.getElementById('upload');
var display = document.getElementById('select');
demo.onclick = function(){
try{
var svg = window.SvgNest.parsesvg(display.innerHTML);
display.innerHTML = '';
display.appendChild(svg);
}
catch(e){
message.innerHTML = e;
message.className = 'error animated bounce';
return;
}
hideSplash();
message.innerHTML = 'Click on the outline to use as the bin';
message.className = 'active animated bounce';
attachSvgListeners(svg);
};
var message = document.getElementById('message');
if(!document.createElementNS || !document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect){
message.innerHTML = 'Your browser does not have SVG support';
message.className = 'error animated bounce';
return
}
if (!window.SvgNest) {
message.innerHTML = "Couldn't initialize SVGnest";
message.className = 'error animated bounce';
return;
}
if(!window.File || !window.FileReader){
message.innerHTML = 'Your browser does not have file upload support';
message.className = 'error animated bounce';
return
}
if(!window.Worker){
message.innerHTML = 'Your browser does not have web worker support';
message.className = 'error animated bounce';
return
}
// button clicks
var upload = document.getElementById('upload');
var start = document.getElementById('start');
var download = document.getElementById('download');
var startlabel = document.getElementById('startlabel');
var fileinput = document.getElementById('fileinput');
var config = document.getElementById('config');
var configbutton = document.getElementById('configbutton');
var configsave = document.getElementById('configsave');
var zoomin = document.getElementById('zoominbutton');
var zoomout = document.getElementById('zoomoutbutton');
var exit = document.getElementById('exitbutton');
var isworking = false;
start.onclick = function(){
if(this.className == 'button start disabled'){
return false;
}
iterations = 0;
if(isworking){
stopnest();
}
else{
startnest();
}
display.className = 'disabled';
document.getElementById('info_time').setAttribute('style','display: none');
};
function startnest(){
// Once started, don't allow this anymore
document.removeEventListener('dragover', FileDragHover, false);
document.removeEventListener('dragleave', FileDragHover, false);
document.removeEventListener('drop', FileDrop, false);
SvgNest.start(progress, renderSvg);
startlabel.innerHTML = 'Stop Nest';
start.className = 'button spinner';
configbutton.className = 'button config disabled';
config.className = '';
zoomin.className = 'button zoomin disabled';
zoomout.className = 'button zoomout disabled';
var svg = document.querySelector('#select svg');
if(svg){
svg.removeAttribute('style');
}
isworking = true;
}
function stopnest(){
SvgNest.stop();
startlabel.innerHTML = 'Start Nest';
start.className = 'button start';
configbutton.className = 'button config';
isworking = false;
}
// config
var configvisible = false;
configbutton.onclick = function(){
if(this.className == 'button config disabled'){
return false;
}
if(!configvisible){
config.className = 'active';
configbutton.className = 'button close';
}
else{
config.className = '';
configbutton.className = 'button config';
}
configvisible = !configvisible;
return false;
}
configsave.onclick = function(){
var c = {};
var inputs = document.querySelectorAll('#config input');
for(var i=0; i<inputs.length; i++){
var key = inputs[i].getAttribute('data-config');
if(inputs[i].getAttribute('type') == 'text'){
c[key] = inputs[i].value;
}
else if(inputs[i].getAttribute('type') == 'checkbox'){
c[key] = inputs[i].checked;
}
}
window.SvgNest.config(c);
// new configs will invalidate current nest
if(isworking){
stopnest();
}
configvisible = false;
config.className = '';
return false;
}
upload.onclick = function(){
fileinput.click();
}
document.addEventListener('dragover', FileDragHover, false);
document.addEventListener('dragleave', FileDragHover, false);
document.addEventListener('drop', FileDrop, false);
function FileDragHover(e){
e.stopPropagation();
e.preventDefault();
upload.style.backgroundColor = (e.type == "dragover" ? "#d7e9b7" : "");
}
function FileDrop(e){
e.stopPropagation(); // Make sure not to replace website by file
e.preventDefault();
handleFile(e.dataTransfer.files[0]);
}
download.onclick = function(){
if(download.className == 'button download disabled'){
return false;
}
var bins = document.getElementById('bins');
if(bins.children.length == 0){
message.innerHTML = 'No SVG to export';
message.className = 'error animated bounce';
return
}
var svg;
svg = display.querySelector('svg');
if(!svg){
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
}
svg = svg.cloneNode(false);
// maintain stroke, fill etc of input
if(SvgNest.style){
svg.appendChild(SvgNest.style);
}
var binHeight = parseInt(bins.children[0].getAttribute('height'));
for(var i=0; i<bins.children.length; i++){
var b = bins.children[i];
var group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
group.setAttribute('transform', 'translate(0 '+binHeight*1.1*i+')');
for(var j=0; j<b.children.length; j++){
group.appendChild(b.children[j].cloneNode(true));
}
svg.appendChild(group);
}
var output;
if(typeof XMLSerializer != 'undefined'){
output = (new XMLSerializer()).serializeToString(svg);
}
else{
output = svg.outerHTML;
}
var blob = new Blob([output], {type: "image/svg+xml;charset=utf-8"});
saveAs(blob, "SVGnest-output.svg");
}
var zoomlevel = 1.0;
zoomin.onclick = function(){
if(this.className == 'button zoomin disabled'){
return false;
}
zoomlevel *= 1.2;
var svg = document.querySelector('#select svg');
if(svg){
svg.setAttribute('style', 'transform-origin: top left; transform:scale('+zoomlevel+'); -webkit-transform:scale('+zoomlevel+'); -moz-transform:scale('+zoomlevel+'); -ms-transform:scale('+zoomlevel+'); -o-transform:scale('+zoomlevel+');');
}
}
zoomout.onclick = function(){
if(this.className == 'button zoomout disabled'){
return false;
}
zoomlevel *= 0.8;
if(zoomlevel < 0.02){
zoomlevel = 0.02;
}
var svg = document.querySelector('#select svg');
if(svg){
svg.setAttribute('style', 'transform-origin: top left; transform:scale('+zoomlevel+'); -webkit-transform:scale('+zoomlevel+'); -moz-transform:scale('+zoomlevel+'); -ms-transform:scale('+zoomlevel+'); -o-transform:scale('+zoomlevel+');');
}
}
exit.onclick = function(){
location.reload();
}
fileinput.onchange = function(e){
handleFile(e.target.files[0]);
}
function handleFile(file){
if(!file){
return;
}
if(!file.type || (file.type.search('svg') < 0 && file.type.search('xml') < 0 && file.type.search('text') < 0)){
message.innerHTML = 'Only SVG files allowed';
message.className = 'error animated bounce';
return
}
var reader = new FileReader();
var input = this;
reader.onload = function(e) {
input.value = null;
if(reader.result){
try{
var svg = window.SvgNest.parsesvg(reader.result);
{
var wholeSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
// Copy relevant scaling info
wholeSVG.setAttribute('width',svg.getAttribute('width'));
wholeSVG.setAttribute('height',svg.getAttribute('height'));
wholeSVG.setAttribute('viewBox',svg.getAttribute('viewBox'));
var rect = document.createElementNS(wholeSVG.namespaceURI,'rect');
rect.setAttribute('x', wholeSVG.viewBox.baseVal.x);
rect.setAttribute('y', wholeSVG.viewBox.baseVal.x);
rect.setAttribute('width', wholeSVG.viewBox.baseVal.width);
rect.setAttribute('height', wholeSVG.viewBox.baseVal.height);
rect.setAttribute('class', 'fullRect');
wholeSVG.appendChild(rect);
}
display.innerHTML = '';
display.appendChild(wholeSVG); // As a default bin in background
display.appendChild(svg);
}
catch(e){
message.innerHTML = e;
message.className = 'error animated bounce';
return;
}
hideSplash();
message.innerHTML = 'Click on the outline to use as the bin';
message.className = 'active animated bounce';
start.className = 'button start disabled';
attachSvgListeners(svg);
attachSvgListeners(wholeSVG);
}
}
reader.readAsText(file);
};
function attachSvgListeners(svg){
// attach event listeners
for(var i=0; i<svg.childNodes.length; i++){
var node = svg.childNodes[i];
if(node.nodeType == 1){
node.onclick = function(){
if(display.className == 'disabled'){
return;
}
var currentbin = document.querySelector('#select .active');
if(currentbin){
var className = currentbin.getAttribute('class').replace('active', '').trim();
if(!className)
currentbin.removeAttribute('class');
else
currentbin.setAttribute('class', className);
}
window.SvgNest.setbin(this);
this.setAttribute('class',(this.getAttribute('class') ? this.getAttribute('class')+' ' : '') + 'active');
start.className = 'button start animated bounce';
message.className = '';
}
}
}
}
var prevpercent = 0;
var startTime = null;
function progress(percent){
var transition = percent > prevpercent ? '; transition: width 0.1s' : '';
document.getElementById('info_progress').setAttribute('style','width: '+Math.round(percent*100)+'% ' + transition);
document.getElementById('info').setAttribute('style','display: block');
prevpercent = percent;
var now = new Date().getTime();
if(startTime && now){
var diff = now-startTime;
// show a time estimate for long-running placements
var estimate = (diff/percent)*(1-percent);
document.getElementById('info_time').innerHTML = millisecondsToStr(estimate)+' remaining';
if(diff > 5000 && percent < 0.3 && percent > 0.02 && estimate > 10000){
document.getElementById('info_time').setAttribute('style','display: block');
}
}
if(percent > 0.95 || percent < 0.02){
document.getElementById('info_time').setAttribute('style','display: none');
}
if(percent < 0.02){
startTime = new Date().getTime();
}
}
var iterations = 0;
function renderSvg(svglist, efficiency, placed, total){
iterations++;
document.getElementById('info_iterations').innerHTML = iterations;
if(!svglist || svglist.length == 0){
return;
}
var bins = document.getElementById('bins');
bins.innerHTML = '';
for(var i=0; i<svglist.length; i++){
if(svglist.length > 2){
svglist[i].setAttribute('class','grid');
}
bins.appendChild(svglist[i]);
}
if(efficiency || efficiency === 0){
document.getElementById('info_efficiency').innerHTML = Math.round(efficiency*100);
}
document.getElementById('info_placed').innerHTML = placed+'/'+total;
document.getElementById('info_placement').setAttribute('style','display: block');
display.setAttribute('style','display: none');
download.className = 'button download animated bounce';
}
message.onclick = function(e){
this.className='';
}
function millisecondsToStr (milliseconds) {
function numberEnding (number) {
return (number > 1) ? 's' : '';
}
var temp = Math.floor(milliseconds / 1000);
var years = Math.floor(temp / 31536000);
if (years) {
return years + ' year' + numberEnding(years);
}
var days = Math.floor((temp %= 31536000) / 86400);
if (days) {
return days + ' day' + numberEnding(days);
}
var hours = Math.floor((temp %= 86400) / 3600);
if (hours) {
return hours + ' hour' + numberEnding(hours);
}
var minutes = Math.floor((temp %= 3600) / 60);
if (minutes) {
return minutes + ' minute' + numberEnding(minutes);
}
var seconds = temp % 60;
if (seconds) {
return seconds + ' second' + numberEnding(seconds);
}
return 'less than a second';
}
});
</script>
</head>
<body>
<div id="splash">
<!-- a small splash page that explains what this thing is -->
<img src="img/logo.svg" alt="SVGnest" class="logo" style="display:block;margin:0 auto;max-width:220px;" />
<h1 class="title">MAnest</h1>
<em class="subscript">Fast Nesting for Makers</em>
<ul class="nav" id="splash-nav">
<li class="button start" id="demo">Demo</li>
<li class="button upload" id="upload">Upload SVG</li>
<li class="button" id="faqbutton">FAQ</li>
<li><a class="button back" id="back" href="https://makearmy.io" rel="noopener">Back to Main</a></li>
</ul>
<p id="poweredby">
Powered by SVGnest, a free open source project by Jack000 &mdash;
visit the github <a href="https://github.com/Jack000/SVGnest" target="_blank" rel="noopener">here</a>.
</p>
<div id="faq">
<h1>What exactly is "nesting"?</h1>
<p>If you have some parts to cut out of a piece of metal/plastic/wood etc, you'd want to arrange the parts to use as little material as possible. This is a common problem if you use a laser cutter, plasma cutter, or CNC machine.</p>
<p>In computer terms this is called the irregular bin-packing problem</p>
<h1>How much does it cost?</h1>
<p>It's free and open source. The code and implementation details are on <a href="https://github.com/Jack000/SVGnest" target="_blank">Github</a></p>
<h1>Does it use inches? mm?</h1>
<p>SVG has its internal units, the distance related fields in the settings use SVG units, ie. pixels. The conversion between a pixel and real units depend on the exporting software, but it's typically 72 pixels = 1 inch</p>
<h1>My SVG text/image doesn't show up?</h1>
<p>Nesting only works for closed shapes, so SVG elements that don't represent closed shapes are removed. Convert text and any other elements to outlines first. Ensure that outlines do not intersect or overlap eachother. Outlines that are inside other outlines are considered holes.</p>
<h1>It doesn't ever stop?</h1>
<p>The software will continuously look for better solutions until you press the stop button. You can stop at any time and download the SVG file.</p>
<h1>Some parts seem to slightly overlap?</h1>
<p>Curved shapes are approximated with line segments. For a more accurate nest with curved parts, decrease the curve tolerance parameter in the configuration.</p>
<h1>I need help?</h1>
<p>Add an issue on Github or contact me personally: <a href="http://jack.works">jack.works</a>
</div>
</div>
<div id="svgnest">
<div id="controls">
<ul class="nav">
<li class="button start disabled" id="start"><span id="startlabel">Start Nest</span></li>
<li class="button download disabled" id="download">Download SVG</li>
<li class="button config" id="configbutton"></li>
<li class="button zoomin" id="zoominbutton"></li>
<li class="button zoomout" id="zoomoutbutton"></li>
<li class="button exit" id="exitbutton"></li>
</ul>
<div id="config">
<div id="configwrapper">
<input type="text" value="0" data-config="spacing" />
<h3>Space between parts</h3><span class="tooltip" title="The space between parts in SVG units (conversion depends on exporting software but usually 1 SVG unit = 1px = 1/72 inches = 0.3527777~ mm)">?</span>
<input type="text" value="0.3" data-config="curveTolerance" />
<h3>Curve tolerance</h3><span class="tooltip" title="The maximum error allowed when converting Beziers and arcs to line segments. In SVG units. Smaller tolerances will take longer to compute">?</span>
<input type="text" value="4" data-config="rotations" />
<h3>Part rotations</h3><span class="tooltip" title="Number of rotations to consider when inserting a part. Larger rotations will take longer to compute, and may also take longer to converge to a good solution">?</span>
<input type="text" value="10" data-config="populationSize" />
<h3>GA population</h3><span class="tooltip" title="The number of solutions in the Genetic Algorithm population. Larger populations will converge slower but may result in better solutions in the long run">?</span>
<input type="text" value="10" data-config="mutationRate" />
<h3>GA mutation rate</h3><span class="tooltip" title="Mutation rate (in percent) at each generation of the Genetic Algorithm. A 100% mutation rate is equivalent to random sampling">?</span>
<input type="checkbox" class="checkbox" data-config="useHoles" />
<h3>Part in Part</h3><span class="tooltip" title="Place parts in the holes of other parts. This will take much longer to compute ">?</span>
<input type="checkbox" class="checkbox" data-config="exploreConcave" />
<h3>Explore concave areas</h3><span class="tooltip" title="Try to solve for enclosed concave areas (eg. a jigsaw puzzle piece) This will take much longer to compute ">?</span>
<a href="#" class="button" id="configsave">Save Settings</a>
</div>
</div>
</div>
<div class="sidebar">
<div id="info">
<h2 id="info_time"></h2>
<div class="progress"><div class="progress_inner" id="info_progress"></div></div>
<span class="subscript">Placement progress</span>
<div id="info_placement">
<div class="column left">
<h1 class="label"><span id="info_efficiency"></span><sup>%</sup></h1>
<span class="subscript">Material Utilization</span>
</div>
<div class="column right">
<h1 class="label" id="info_iterations"></h1>
<span class="subscript">Iterations</span>
</div>
<div class="column left">
<h1 class="label"><span id="info_placed"></span></h1>
<span class="subscript">Parts placed</span>
</div>
</div>
</div>
</div>
<div id="select">
<svg version="1.1" id="svg2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1147.592px" height="1397.27px" viewBox="0 0 1147.592 1397.27" enable-background="new 0 0 1147.592 1397.27" xml:space="preserve">
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="684.045,443.734 688.396,447.215
666.488,450.935 666.488,432.651 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="697.067,404.901 697.067,415.601
709.719,415.905 710.293,406.067 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="723.908,440.886 715.362,442.463
709.719,435.85 712.627,427.66 721.17,426.079 726.81,432.692 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="617.292,458.369 618.742,465.049
599.735,465.566 599.735,447.28 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="619.963,428.025 619.963,438.722
633.189,438.467 633.189,429.192 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="646.805,464.01 638.259,465.587
632.618,458.974 635.523,450.784 644.069,449.207 649.706,455.819 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="777.062,428.025 746.983,457.397
760.468,481.256 782.898,488.173 788.995,478.145 814.669,479.959 818.817,438.722 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="666.589,476.434 664.255,503.557
681.632,505.633 694.857,499.916 692.262,483.07 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="599.735,501.612 636.247,531.953
680.981,531.953 680.981,554.904 599.735,554.904 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="759.315,529.301 775.721,500.886
792.128,529.301 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="741.307,428.338 734.229,419.461
736.757,408.396 746.983,403.469 757.206,408.396 759.735,419.461 752.654,428.338 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="647.399,599.509 651.749,602.988
629.84,606.702 629.84,588.419 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="650.068,569.167 650.068,579.861
662.723,580.171 663.294,570.334 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="758.061,626.35 800.104,626.854
807.438,600.447 796.468,579.694 785.065,582.478 768.195,563.04 736.104,589.264 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="694.308,592.604 687.232,583.727
689.758,572.661 699.984,567.735 710.211,572.661 712.736,583.727 705.658,592.604 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="854.273,544.551 858.625,548.03
836.717,551.75 836.717,533.467 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="856.944,514.209 856.944,524.909
869.597,525.213 870.171,515.376 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="883.785,550.194 875.24,551.771
869.597,545.158 872.505,536.968 881.048,535.388 886.688,542.001 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="779.841,537.333 779.841,548.03
793.067,547.775 793.067,538.5 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="901.184,537.646 894.106,528.769
896.635,517.703 906.861,512.777 917.084,517.703 919.613,528.769 912.532,537.646 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="836.717,410.401 827.633,456.421
894.106,463.102 910.101,434.273 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="676.909,605.146 668.363,606.727
662.723,600.113 665.628,591.92 674.174,590.346 679.814,596.959 "/>
<rect x="21.066" y="439.913" width="30.913" height="18.155" fill="none" stroke="#010101"/>
<rect x="106.758" y="452.881" width="29.563" height="5.188" fill="none" stroke="#010101"/>
<rect x="184.038" y="464.809" width="25.963" height="17.637" fill="none" stroke="#010101"/>
<rect x="305.408" y="427.01" width="17.01" height="40.393" fill="none" stroke="#010101"/>
<rect x="262.876" y="458.068" width="17.004" height="17.633" fill="none" stroke="#010101"/>
<rect x="338.931" y="427.01" width="47.904" height="25.868" fill="none" stroke="#010101"/>
<polygon fill="none" stroke="#010101" points="66.82,475.701 47.112,507.86 101.95,510.972 140.64,493.336 90.161,496.448 87.322,473.625 "/>
<polygon fill="none" stroke="#010101" points="196.401,495.929 224.016,523.938 348.972,517.196 271.061,492.297 227.967,473.625 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1065.045,441.528 1069.396,445.009
1047.488,448.729 1047.488,430.445 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1078.067,402.694 1078.067,413.395
1090.719,413.699 1091.293,403.861 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1104.908,438.68 1096.362,440.257
1090.719,433.644 1093.627,425.454 1102.17,423.873 1107.811,430.486 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="998.292,456.163 999.742,462.843
980.735,463.359 980.735,445.074 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1000.963,425.818 1000.963,436.516
1014.189,436.261 1014.189,426.985 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1027.805,461.804 1019.259,463.381
1013.618,456.768 1016.523,448.578 1025.069,447.001 1030.706,453.613 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="601.531,174.639 571.451,204.011
584.936,227.87 607.366,234.787 613.463,224.759 639.137,226.573 643.285,185.336 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1047.589,474.228 1045.255,501.351
1062.632,503.427 1075.857,497.71 1073.262,480.864 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="980.735,499.406 1017.247,529.747
1061.98,529.747 1061.98,552.698 980.735,552.698 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="583.783,275.915 600.188,247.5
616.596,275.915 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="565.774,174.952 558.696,166.075
561.225,155.01 571.451,150.083 581.674,155.01 584.203,166.075 577.122,174.952 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1028.399,597.303 1032.749,600.782
1010.84,604.496 1010.84,586.213 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1031.068,566.961 1031.068,577.655
1043.723,577.965 1044.294,568.128 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="582.528,372.964 624.57,373.468
631.906,347.062 620.936,326.309 609.533,329.092 592.663,309.654 560.571,335.878 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1075.308,590.397 1068.232,581.521
1070.758,570.455 1080.984,565.529 1091.211,570.455 1093.736,581.521 1086.658,590.397 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="678.741,291.165 683.093,294.645
661.185,298.364 661.185,280.081 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="681.412,260.823 681.412,271.524
694.064,271.827 694.639,261.99 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="708.253,296.809 699.708,298.386
694.064,291.773 696.973,283.582 705.516,282.002 711.155,288.615 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="604.309,283.947 604.309,294.645
617.535,294.389 617.535,285.114 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="725.651,284.26 718.574,275.383
721.104,264.317 731.329,259.392 741.552,264.317 744.081,275.383 737,284.26 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="661.185,157.016 652.101,203.035
718.574,209.716 734.568,180.887 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1057.909,602.939 1049.363,604.521
1043.723,597.907 1046.628,589.714 1055.174,588.14 1060.814,594.753 "/>
<rect x="402.066" y="437.707" width="30.912" height="18.155" fill="none" stroke="#010101" />
<rect x="487.758" y="450.675" width="29.564" height="5.188" fill="none" stroke="#010101" />
<rect x="565.949" y="691.507" width="25.963" height="17.636" fill="none" stroke="#010101" />
<rect x="687.32" y="653.708" width="17.01" height="40.392" fill="none" stroke="#010101" />
<rect x="644.789" y="684.767" width="17.004" height="17.634" fill="none" stroke="#010101" />
<rect x="720.844" y="653.708" width="47.904" height="25.868" fill="none" stroke="#010101" />
<polygon fill="none" stroke="#010101" points="447.82,473.495 428.112,505.654 482.95,508.766 521.641,491.13 471.161,494.242 468.322,471.419 "/>
<polygon fill="none" stroke="#010101" points="578.312,722.627 605.928,750.635 730.885,743.895 652.973,718.995 609.879,700.323 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="903.513,195.164 907.864,198.645
885.956,202.364 885.956,184.081 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="916.535,156.33 916.535,167.03
929.188,167.335 929.761,157.497 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="943.376,192.316 934.83,193.893
929.188,187.279 932.095,179.09 940.638,177.509 946.277,184.122 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="836.76,209.799 838.209,216.479
819.203,216.995 819.203,198.71 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="839.432,179.454 839.432,190.151
852.657,189.897 852.657,180.621 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="866.271,215.44 857.727,217.017
852.086,210.403 854.991,202.214 863.537,200.637 869.174,207.249 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="996.531,179.454 966.451,208.826
979.936,232.686 1002.366,239.603 1008.463,229.574 1034.137,231.389 1038.285,190.151 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="886.057,227.863 883.723,254.986
901.1,257.063 914.325,251.346 911.729,234.5 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="819.203,253.042 855.715,283.383
900.449,283.383 900.449,306.334 819.203,306.334 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="978.783,280.731 995.188,252.316
1011.596,280.731 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="960.774,179.768 953.696,170.891
956.225,159.825 966.451,154.899 976.674,159.825 979.203,170.891 972.122,179.768 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="866.867,350.939 871.217,354.418
849.308,358.132 849.308,339.849 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="869.536,320.597 869.536,331.291
882.19,331.601 882.762,321.764 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="977.528,377.779 1019.57,378.283
1026.906,351.877 1015.936,331.124 1004.533,333.907 987.663,314.47 955.571,340.693 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="913.775,344.033 906.7,335.156
909.226,324.091 919.452,319.165 929.679,324.091 932.204,335.156 925.126,344.033 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1073.741,295.981 1078.093,299.46
1056.186,303.18 1056.186,284.897 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1076.412,265.639 1076.412,276.339
1089.064,276.643 1089.639,266.806 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1103.253,301.624 1094.708,303.201
1089.064,296.588 1091.973,288.398 1100.516,286.817 1106.155,293.431 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="999.309,288.763 999.309,299.46
1012.535,299.204 1012.535,289.93 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1120.651,289.075 1113.574,280.198
1116.104,269.133 1126.329,264.207 1136.552,269.133 1139.081,280.198 1132,289.075 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1056.186,161.831 1047.101,207.851
1113.574,214.531 1129.568,185.702 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="896.377,356.575 887.831,358.156
882.19,351.543 885.096,343.35 893.642,341.776 899.282,348.389 "/>
<rect x="797.979" y="671.427" width="30.912" height="18.155" fill="none" stroke="#010101" />
<rect x="883.67" y="684.395" width="29.564" height="5.188" fill="none" stroke="#010101" />
<rect x="916.535" y="655.978" width="25.963" height="17.637" fill="none" stroke="#010101" />
<rect x="1037.906" y="618.179" width="17.01" height="40.393" fill="none" stroke="#010101" />
<rect x="995.375" y="649.237" width="17.004" height="17.633" fill="none" stroke="#010101" />
<rect x="1071.43" y="618.179" width="47.904" height="25.868" fill="none" stroke="#010101" />
<polygon fill="none" stroke="#010101" points="843.732,707.215 824.023,739.374 878.861,742.484 917.553,724.85 867.072,727.962 864.234,705.139 "/>
<polygon fill="none" stroke="#010101" points="928.898,687.098 956.514,715.105 1081.471,708.365 1003.559,683.466 960.465,664.794 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="105.375,574.337 109.727,577.817
87.819,581.537 87.819,563.254 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="118.398,535.503 118.398,546.203
131.049,546.508 131.624,536.67 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="145.239,571.488 136.693,573.065
131.049,566.452 133.958,558.263 142.5,556.682 148.14,563.295 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="38.623,588.972 40.072,595.651
21.066,596.168 21.066,577.883 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="41.294,558.627 41.294,569.324
54.52,569.069 54.52,559.794 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="68.135,594.612 59.589,596.189
53.949,589.576 56.854,581.387 65.4,579.81 71.037,586.422 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="198.394,558.627 168.314,587.999
181.798,611.858 204.229,618.775 210.326,608.747 236,610.562 240.148,569.324 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="87.919,607.036 85.585,634.159
102.962,636.235 116.188,630.519 113.592,613.673 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="21.066,632.215 57.578,662.556
102.312,662.556 102.312,685.507 21.066,685.507 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="180.646,659.903 197.051,631.488
213.458,659.903 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="162.637,558.94 155.559,550.062
158.087,538.998 168.314,534.071 178.537,538.998 181.066,550.062 173.985,558.94 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="68.73,730.111 73.08,733.591
51.17,737.305 51.17,719.021 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="71.399,699.77 71.399,710.464
84.053,710.773 84.625,700.938 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="115.638,723.206 108.563,714.329
111.088,703.264 121.315,698.338 131.542,703.264 134.067,714.329 126.989,723.206 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="275.604,675.153 279.956,678.633
258.047,682.354 258.047,664.069 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="278.275,644.812 278.275,655.512
290.927,655.815 291.501,645.979 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="305.116,680.797 296.571,682.374
290.927,675.761 293.835,667.57 302.378,665.99 308.018,672.604 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="201.171,667.936 201.171,678.633
214.398,678.377 214.398,669.104 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="322.514,668.248 315.438,659.371
317.965,648.306 328.192,643.38 338.415,648.306 340.944,659.371 333.863,668.248 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="258.047,541.004 248.963,587.023
315.438,593.704 331.431,564.875 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="98.24,735.748 89.694,737.329
84.053,730.716 86.958,722.521 95.504,720.948 101.145,727.562 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="504.59,600.795 508.941,604.275
487.033,607.995 487.033,589.712 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="517.611,561.961 517.611,572.661
530.264,572.966 530.838,563.128 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="545.365,716.138 536.818,717.715
531.176,711.102 534.084,702.912 542.627,701.331 548.266,707.944 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="437.837,615.43 439.287,622.109
420.28,622.626 420.28,604.341 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="440.508,585.085 440.508,595.782
453.734,595.527 453.734,586.252 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="467.35,621.07 458.804,622.646
453.163,616.034 456.068,607.845 464.614,606.268 470.251,612.88 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="598.52,813.989 568.439,843.361
581.924,867.221 604.355,874.138 610.451,864.109 636.125,865.924 640.273,824.687 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="487.134,633.494 484.8,660.617
502.177,662.693 515.402,656.977 512.807,640.131 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="420.28,658.673 456.792,689.014
501.526,689.014 501.526,711.965 420.28,711.965 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="580.771,915.266 597.178,886.851
613.584,915.266 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="562.764,814.303 555.686,805.426
558.213,794.359 568.439,789.434 578.662,794.359 581.191,805.426 574.111,814.303 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="467.944,756.569 472.294,760.049
450.385,763.763 450.385,745.479 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="470.613,726.229 470.613,736.922
483.268,737.231 483.839,727.395 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="514.852,749.664 507.777,740.787
510.303,729.722 520.529,724.796 530.756,729.722 533.281,740.787 526.203,749.664 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="675.73,930.516 680.082,933.995
658.174,937.715 658.174,919.432 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="678.4,900.174 678.4,910.874
691.053,911.178 691.627,901.341 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="705.242,936.159 696.697,937.736
691.053,931.123 693.961,922.933 702.504,921.353 708.145,927.966 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="601.297,923.298 601.297,933.995
614.523,933.739 614.523,924.465 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="563.383,919.432 559.621,926.865
550.156,929.546 550.156,937.715 559.621,940.869 563.383,946.966 571.941,946.966 575.18,942.553 581.07,937.736 581.07,929.546
575.18,926.087 570.123,919.432 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="722.641,923.609 715.562,914.734
718.092,903.668 728.318,898.742 738.541,903.668 741.07,914.734 733.988,923.609 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="658.174,796.366 649.09,842.386
715.562,849.066 731.557,820.237 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="497.454,762.206 488.908,763.787
483.268,757.174 486.173,748.98 494.719,747.406 500.359,754.02 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="900.502,834.515 904.854,837.995
882.945,841.715 882.945,823.432 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="913.523,795.681 913.523,806.381
926.176,806.686 926.75,796.848 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="940.365,831.666 931.818,833.243
926.176,826.63 929.084,818.441 937.627,816.859 943.266,823.473 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="833.748,849.15 835.197,855.829
816.191,856.346 816.191,838.061 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="836.42,818.805 836.42,829.502
849.646,829.247 849.646,819.972 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="863.262,854.79 854.715,856.367
849.074,849.754 851.98,841.564 860.525,839.987 866.162,846.6 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="993.52,818.805 963.439,848.177
976.924,872.036 999.355,878.953 1005.451,868.925 1031.125,870.739 1035.273,829.502 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="957.764,819.118 950.686,810.241
953.213,799.176 963.439,794.249 973.662,799.176 976.191,810.241 969.111,819.118 "/>
<polygon fill="none" stroke="#010101" stroke-miterlimit="10" points="1053.174,801.182 1044.09,847.201
1110.562,853.882 1126.557,825.053 "/>
<rect width="511.822" height="339.235" fill="none" stroke="#010101" />
<path fill="none" stroke="#010101" d="M746.843,60.679c-2.465,1.232-7.395,2.465-13.711,2.465c-14.635,0-25.65-9.243-25.65-26.266
c0-16.252,11.016-27.268,27.113-27.268c6.471,0,10.553,1.387,12.324,2.311l-1.617,5.469c-2.542-1.232-6.162-2.157-10.476-2.157
c-12.17,0-20.258,7.78-20.258,21.414c0,12.709,7.317,20.874,19.95,20.874c4.082,0,8.241-0.848,10.938-2.157L746.843,60.679z"/>
<path fill="none" stroke="#010101" d="M755.008,7.685h6.778v54.688h-6.778V7.685z"/>
<path fill="none" stroke="#010101" d="M780.734,14.617c0.077,2.311-1.617,4.16-4.313,4.16c-2.388,0-4.082-1.849-4.082-4.16c0-2.388,1.771-4.236,4.236-4.236
C779.117,10.38,780.734,12.229,780.734,14.617z M773.187,62.373V25.092h6.777v37.281H773.187z"/>
<path fill="none" stroke="#010101" d="M817.784,60.986c-1.771,0.925-5.7,2.157-10.707,2.157c-11.246,0-18.563-7.626-18.563-19.026
c0-11.477,7.856-19.795,20.027-19.795c4.005,0,7.548,1.001,9.397,1.925l-1.541,5.238c-1.617-0.924-4.159-1.771-7.856-1.771
c-8.55,0-13.172,6.316-13.172,14.096c0,8.627,5.546,13.942,12.94,13.942c3.852,0,6.394-1.001,8.318-1.849L817.784,60.986z"/>
<path fill="none" stroke="#010101" d="M832.42,42.192h0.154c0.924-1.31,2.233-2.927,3.312-4.237l10.938-12.863h8.164L840.585,40.42l16.406,21.953h-8.241
l-12.864-17.87l-3.466,3.852v14.019h-6.701V7.685h6.701V42.192z"/>
<path fill="none" stroke="#010101" d="M885.107,16.157h-15.79v-5.7h38.437v5.7h-15.867v46.216h-6.779V16.157z"/>
<path fill="none" stroke="#010101" d="M912.762,7.685h6.778v23.262h0.154c1.078-1.926,2.772-3.62,4.853-4.775c2.002-1.156,4.391-1.926,6.932-1.926
c5.007,0,13.018,3.081,13.018,15.944v22.184h-6.778V40.96c0-6.008-2.233-11.092-8.627-11.092c-4.39,0-7.856,3.081-9.089,6.778
c-0.385,0.924-0.462,1.925-0.462,3.235v22.492h-6.778V7.685z"/>
<path fill="none" stroke="#010101" d="M963.062,14.617c0.077,2.311-1.618,4.16-4.313,4.16c-2.389,0-4.083-1.849-4.083-4.16c0-2.388,1.771-4.236,4.236-4.236
C961.443,10.38,963.062,12.229,963.062,14.617z M955.513,62.373V25.092h6.778v37.281H955.513z"/>
<path fill="none" stroke="#010101" d="M972.611,55.44c2.003,1.31,5.546,2.696,8.936,2.696c4.93,0,7.24-2.465,7.24-5.546c0-3.235-1.926-5.007-6.933-6.855
c-6.701-2.388-9.859-6.085-9.859-10.553c0-6.008,4.853-10.938,12.864-10.938c3.773,0,7.086,1.078,9.166,2.311l-1.695,4.93
c-1.463-0.924-4.159-2.157-7.625-2.157c-4.006,0-6.239,2.311-6.239,5.084c0,3.081,2.233,4.467,7.086,6.316
c6.471,2.465,9.782,5.7,9.782,11.246c0,6.547-5.083,11.169-13.941,11.169c-4.082,0-7.856-1.001-10.476-2.542L972.611,55.44z"/>
<polygon fill="none" stroke="#010101" points="676.149,23 616.756,23 616.756,1 558.443,34.667 616.756,68.333 616.756,46.333 676.149,46.333 "/>
</svg>
</div>
<div id="bins"></div>
<input id="fileinput" type="file" style="visibility:hidden" />
</div>
<div id="messagewrapper">
<div id="message"></div>
</div>
</body>
</html>

114
public/svgnest/readme.md Normal file
View file

@ -0,0 +1,114 @@
# ![SVGNest](http://svgnest.com/github/logo2.png)
**SVGNest**: A browser-based vector nesting tool.
**Demo:** http://svgnest.com
(requires SVG and webworker support). Mobile warning: running the demo is CPU intensive.
references (PDF):
- [López-Camacho *et al.* 2013](http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf)
- [Kendall 2000](http://www.graham-kendall.com/papers/k2001.pdf)
- [E.K. Burke *et al.* 2006](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.440.379&rep=rep1&type=pdf)
## What is "nesting"?
Given a square piece of material and some letters to be laser-cut:
![letter nesting](http://svgnest.com/github/letters.png)
We want to pack all the letters into the square, using as little material as possible. If a single square is not enough, we also want to minimize the number of squares used.
In the CNC world this is called "[nesting](http://sigmanest.com/)", and [software](http://www.mynesting.com/) that [does this](http://www.autodesk.com/products/trunest/overview) is typically targeted at [industrial customers](http://www.hypertherm.com/en/Products/Automated_cutting/Nesting_software/) and [very expensive](http://www.nestfab.com/pricing/).
SVGnest is a free and open-source alternative that solves this problem with the orbital approach outlined in [E.K. Burke *et al.* 2006], using a genetic algorithm for global optimization. It works for arbitrary containers and concave edge cases, and performs on-par with existing commercial software.
![non-rectangular shapes](http://svgnest.com/github/shapes.png)
It also features part-in-part support, for placing parts in the holes of other parts.
![non-rectangular shapes](http://svgnest.com/github/recursion.png)
## Usage
Make sure all parts have been converted to outlines, and that no outlines overlap. Upload the SVG file and select one of the outlines to be used as the bin.
All other outlines are automatically processed as parts for nesting.
## Outline of algorithm
While [good heuristics](http://cgi.csc.liv.ac.uk/~epa/surveyhtml.html) exist for the rectangular bin packing problem, in the real world we are concerned with irregular shapes.
The strategy is made of two parts:
- the placement strategy (ie. how do I insert each part into a bin?)
- and the optimization strategy (ie. what's the best order of insertions?)
### Placing the part
The key concept here is the "No Fit Polygon".
Given polygons A and B, we want to "orbit" B around A such that they always touch but do not intersect.
![No Fit Polygon example](http://svgnest.com/github/nfp.png)
The resulting orbit is the NFP. The NFP contains all possible placements of B that touches the previously placed parts. We can then choose a point on the NFP as the placement position using some heuristics.
Similarly we can construct an "Inner Fit Polygon" for the part and the bin. This is the same as the NFP, except the orbiting polygon is inside the stationary one.
When two or more parts have already been placed, we can take the union of the NFPs of the previously placed parts.
![No Fit Polygon example](http://svgnest.com/github/nfp2.png)
This means that we need to compute O(nlogn) NFPs to complete the first packing. While there are ways to mitigate this, we take the brute-force approach which has good properties for the optimization algo.
### Optimization
Now that we can place the parts, we need to optimize the insertion order. Here's an example of a bad insertion order:
![Bad insertion order](http://svgnest.com/github/badnest.png)
If the large "C" is placed last, the concave space inside it won't be utilized because all the parts that could have filled it have already been placed.
To solve this, we use the "first-fit-decreasing" heuristic. Larger parts are placed first, and smaller parts last. This is quite intuitive, as the smaller parts tend to act as "sand" to fill the gaps left by the larger parts.
![Good insertion order](http://svgnest.com/github/goodnest.png)
While this strategy gives us a good start, we want to explore more of the solution space. We could simply randomize the insertion order, but we can probably do better with a genetic algorithm. (If you don't know what a GA is, [this article](http://www.ai-junkie.com/ga/intro/gat1.html) is a very approachable read)
## Evaluating fitness
In our GA the insertion order and the rotation of the parts form the gene. The fitness function follows these rules:
1. Minimize the number of unplaceable parts (parts that cannot fit any bin due to its rotation)
2. Minimize the number of bins used
3. Minimize the *width* of all placed parts
The third one is rather arbitrary, as we can also optimize for rectangular bounds or a minimal concave hull. In real-world use the material to be cut tends to be rectangular, and those options tend to result in long slivers of un-used material.
Because small mutations in the gene cause potentially large changes in overall fitness, the individuals of the population can be very similar. By caching NFPs new individuals can be evaluated very quickly.
## Performance
![SVGnest comparison](http://svgnest.com/github/comparison1.png)
Performs similarly to commercial software, after both have run for about 5 minutes.
## Configuration parameters
- **Space between parts:** Minimum space between parts (eg. for laser kerf, CNC offset etc.)
- **Curve tolerance:** The maximum error allowed for linear approximations of Bezier paths and arcs, in SVG units or "pixels". Decrease this value if curved parts appear to slightly overlap.
- **Part rotations:** The *possible* number of rotations to evaluate for each part. eg. 4 for only the cardinal directions. Larger values may improve results, but will be slower to converge.
- **GA population:** The population size for the Genetic Algorithm
- **GA mutation rate:** The probability of mutation for each gene or part placement. Values from 1-50
- **Part in part:** When enabled, places parts in the holes of other parts. This is off by default as it can be resource intensive
- **Explore concave areas:** When enabled, solves the concave edge case at a cost of some performance and placement robustness:
![Concave flag example](http://svgnest.com/github/concave.png)
## To-do
- ~~Recursive placement (putting parts in holes of other parts)~~
- Customize fitness function (gravity direction, etc)
- kill worker threads when stop button is clicked
- fix certain edge cases in NFP generation

628
public/svgnest/style.css Normal file
View file

@ -0,0 +1,628 @@
/* =========================================================
SVGNest MakeArmy True-Gray Theme (final teal version)
- Neutral gray UI with Teal accents (#20C5B5)
- Fixed #svgnest top spacing (removed giant gap)
- Restores !important on SVG stroke/fill so uploaded parts display correctly
- Accent is consistent across links, buttons, tooltips, selection, progress
========================================================= */
:root{
/* Neutral grayscale foundation */
--bg: #0e0e0e; /* page background (near-black) */
--bg-2: #1a1a1a; /* panels / cards / buttons */
--bg-3: #121212; /* bins / work areas */
--text: #e0e0e0; /* primary text */
--muted: #a0a0a0; /* secondary text */
--border: #2a2a2a; /* subtle borders */
--shadow: rgba(0,0,0,0.40);
/* Teal accent */
--accent: #20C5B5; /* primary accent */
--accent-2: #1aa397; /* darker hover/active */
/* True-grays (replace any previous blues) */
--gray-1: #f0f0f0; /* headings, light text */
--gray-2: #cccccc; /* subheadings */
--gray-3: #888888; /* lines / subtle emphasis */
--gray-4: #555555; /* secondary fills */
}
/* ===== Base ===== */
html, body{
margin: 0;
padding: 0;
border: 0;
font: normal 22px/1.4 'LatoLatinWeb', helvetica, arial, verdana, sans-serif;
background-color: var(--bg);
color: var(--muted);
}
a{
color: var(--accent);
text-decoration: none;
transition: color .15s ease;
}
a:hover{
color: var(--accent-2);
text-decoration: underline;
}
h1{
font-size: 1.5em;
font-family: 'LatoLatinWebLight', helvetica, arial, verdana, sans-serif;
font-weight: normal;
margin: 1.5em 0 0.5em 0;
color: var(--gray-1);
}
h2{
font-size: 1.1em;
font-weight: bold;
margin: 0 0 0.5em 0;
color: var(--gray-2);
}
h3{
font-size: 1em;
font-weight: bold;
margin: 1em 0 0.2em 0;
color: var(--gray-2);
}
/* ===== Splash ===== */
#splash{
width: 28em;
margin: 8% auto 0 auto;
}
#splash .logo{
width: 50%;
margin: 0 0 0 25%;
height: auto;
}
#splash h1{ color: var(--accent); }
#splash h1.title{
font-size: 3.5em;
margin: 0;
padding: 0;
text-align: center;
}
.subscript{ font-size: 0.75em; }
#splash .subscript{
display: block;
color: var(--accent);
font-size: 1.45em;
text-align: center;
font-style: normal;
}
/* ===== Nav + Buttons ===== */
.nav{ margin: 0; padding: 0; }
li{ list-style: none; float: left; margin: 0; padding: 0; }
.button{
display: block;
margin: 0 0.5em;
padding: 0.6em 2.4em;
background-color: var(--bg-2);
border-radius: 5em;
border: 1px solid var(--border);
cursor: pointer;
color: var(--accent);
box-shadow: 0 1px 0 rgba(0,0,0,0.25);
transition: color .15s ease, box-shadow .15s ease, border-color .15s ease, background-color .15s ease;
}
.button a:hover{ text-decoration: none; }
.button.start{
background: var(--bg-2) url(img/start.svg) no-repeat 1.8em 50% / 1.4em 1.4em;
padding-left: 3.7em;
}
.button.spinner{
background: var(--bg-2) url(img/spin.svg) no-repeat 1.8em 50% / 1.4em 1.4em;
padding-left: 3.7em;
}
.button.upload{
background: var(--bg-2) url(img/upload.svg) no-repeat 2.2em 50% / 1em 1em;
padding-left: 4em;
}
.button.download{
background: var(--bg-2) url(img/download.svg) no-repeat 2.2em 50% / 1em 1em;
padding-left: 4em;
}
.button.code{
background: var(--bg-2) url(img/code.svg) no-repeat 2em 50% / 1.2em 1.2em;
padding-left: 3.9em;
}
.button.config{
background: var(--bg-2) url(img/settings.svg) no-repeat 2em 50% / 1.2em 1.2em;
padding-left: 3.9em;
}
.button.close{
background: var(--bg-2) url(img/close.svg) no-repeat 1.8em 50% / 2em 2em;
padding-left: 3.9em;
}
.button.zoomin{ background: var(--bg-2) url(img/zoomin.svg) no-repeat 50% 50% / 1.5em 1.5em; }
.button.zoomout{ background: var(--bg-2) url(img/zoomout.svg) no-repeat 50% 50% / 1.5em 1.5em; }
.button.exit{ background: var(--bg-2) url(img/close.svg) no-repeat 50% 50% / 1.5em 1.5em; }
.button:hover{
color: var(--accent-2);
border-color: var(--accent);
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
text-decoration: none;
}
.button:active{
background-color: #1f1f1f;
border-color: var(--accent-2);
box-shadow: inset 0 2px 2px rgba(0,0,0,0.35);
}
.button.disabled{
cursor: default; opacity: 0.5; color: #999;
-webkit-filter: saturate(0); filter: saturate(0);
}
.button.disabled:hover{ box-shadow: none; border-color: var(--border); }
.button.disabled:active{ background-color: var(--bg-2); box-shadow: none; }
#splash .nav{ float: left; width: 150%; margin: 4em 0 0 -20%; }
#faq{ display: none; float: left; margin-top: 2em; padding-bottom: 5em; }
/* ===== SVGNest layout ===== */
#svgnest, #messagewrapper{ width: 72em; }
/* FIXED: smaller, responsive top spacing (replaces 9em) */
#svgnest{
display: none;
margin: clamp(1rem, 3vh, 2.5rem) auto 0 auto;
}
#svgnest .logo, #svgnest .sidebar{ float: left; width: 22%; margin-right: 8%; }
#svgnest .sidebar h1{ font-size: 3em; color: var(--text); }
#svgnest .sidebar{ clear: both; width: 100%; margin-top: 3em; }
#svgnest .nav{ float: left; margin: 0 0 0 -0.5em; padding: 0; }
#controls{ margin-top: 0.5em; float: left; position: relative; }
/* ===== Info sidebar ===== */
#info, #info_placement{ display: none; }
h1.label{
font-size: 4em; margin: 0.2em 0 0 0; padding: 0;
line-height: 1; font-weight: normal; color: var(--text);
}
h1.label sup{ font-size: 0.5em; }
.column{ margin: 0.5em 4em 2em 0; float: left; }
/* ===== Progress ===== */
.progress{
width: 51%;
clear: both;
height: 1.2em;
background-color: var(--bg-2);
border: 1px solid var(--accent); /* accent border */
border-radius: 1em;
margin-bottom: 0.4em;
}
.progress_inner{
height: 100%;
background-color: var(--accent); /* accent fill */
border-radius: 1em;
}
/* ===== Config panel ===== */
#config{
max-height: 0;
overflow: hidden;
width: 20em;
position: absolute;
top: 0;
left: 24.5em;
background-color: var(--bg-2);
border-radius: 0.5em;
transition: max-height 0.5s;
box-shadow: 0 2px 8px var(--shadow);
}
#configwrapper{ float: left; padding: 3em 0 1em 2em; }
#config.active{ display: block; max-height: 50em; }
#configbutton{ position: relative; z-index: 2; width: 3em; padding: 0; height: 2.5em; background-position: 50%; }
#zoominbutton, #zoomoutbutton, #exitbutton{ width: 3em; padding: 0; height: 2.5em; background-position: 50%; }
#configbutton.close:hover{ box-shadow: none; }
#configsave{ margin-left: 7%; }
#config input, #config h3, #config .tooltip{ margin: 1em 0 0 0; height: 2em; padding: 0; }
#config input{
float: left; width: 13%; font-size: 1em;
border: 1px solid var(--accent);
color: var(--text);
background: #141414;
text-align: center; clear: left; border-radius: 0.4em;
transition: border-color .15s ease, background-color .15s ease;
}
#config input:hover{ background-color: #181818; border-color: var(--accent-2); }
#config input.checkbox{ width: 7%; margin-left: 4%; margin-right: 4%; border: 1px solid var(--border); }
#config h3{ float: left; width: 65%; margin-left: 5%; padding: 0; font-size: 0.8em; line-height: 3em; color: var(--muted); }
#config .tooltip{
float: left; max-width: 15%; width: 1.5em; height: 1.5em;
font-size: 0.8em; font-weight: bold; background-color: var(--accent);
color: #fff; text-align: center; line-height: 1.5; margin-top: 1.8em;
cursor: default; border-radius: 3em;
}
#config .button{ float: left; clear: both; margin-top: 2em; }
/* ===== SVG areas ===== */
#select{ margin-top: 2em; }
#select, #bins{ float: left; width: 69%; position: relative; }
#select svg, #bins svg{
width: 100%; height: auto; position: absolute; top: 0;
margin: 0; display: block; overflow: visible; pointer-events: none;
}
/* Selection strokes (use accent; override uploaded inline styles) */
#select svg *{
fill: #fff !important;
fill-opacity: 0 !important;
stroke: var(--accent) !important;
stroke-width: 2px !important;
vector-effect: non-scaling-stroke !important;
stroke-linejoin: round !important;
pointer-events: fill !important;
}
#select svg *.fullRect{
fill: #1b1b1b !important;
fill-opacity: 1 !important;
stroke: #1b1b1b !important;
stroke-width: 2px !important;
vector-effect: non-scaling-stroke !important;
stroke-linejoin: round !important;
}
#select svg *:hover{ stroke: var(--accent-2) !important; cursor: pointer !important; }
#select svg *.active{ stroke: var(--accent-2) !important; stroke-width: 3px !important; }
#select.disabled svg *, #select.disabled svg *:hover, #select.disabled svg *.active{
stroke: #6b6b6b !important; stroke-width: 2px !important; cursor: default !important;
}
/* Bins (neutral grays) */
#bins svg{ margin-bottom: 2em; }
#bins svg.grid{ float: left; width: 45%; margin-right: 5%; min-width: 20em; }
#bins svg *{
fill: var(--gray-4) !important;
stroke: var(--gray-3) !important;
stroke-width: 2px !important;
vector-effect: non-scaling-stroke !important;
stroke-linejoin: round !important;
}
#bins svg .bin{ fill: var(--bg-3) !important; stroke: var(--gray-4) !important; }
#bins svg .hole{ fill: var(--bg) !important; stroke: var(--gray-3) !important; }
/* ===== Messages ===== */
#messagewrapper{
width: 50em; overflow: hidden;
background: var(--gray-4) url(img/close.svg) no-repeat 99% 0.5em / 3em 3em;
line-height: 4em;
position: fixed; left: 50%; margin-left: -25em; bottom: 1em;
text-align: center; border-radius: 0.5em; color: #fff;
box-shadow: 0 6px 24px var(--shadow);
border-left: 4px solid var(--accent); /* subtle accent marker */
}
#messagewrapper:hover{ background-color: #666; border-left-color: var(--accent-2); }
#message{ overflow: hidden; height: 0; }
#message.active, #message.error{ height: 4em; cursor: pointer; }
#message.error{ color: #ff314e; font-weight: bold; }
/* ===== Animations (kept intact) ===== */
.animated{
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
@-webkit-keyframes bounce{
from,20%,53%,80%,to{
-webkit-animation-timing-function: cubic-bezier(0.215,0.610,0.355,1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%,43%{
-webkit-animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-30px,0);
transform: translate3d(0,-30px,0);
}
70%{
-webkit-animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-15px,0);
transform: translate3d(0,-15px,0);
}
90%{
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
@keyframes bounce{
from,20%,53%,80%,to{
animation-timing-function: cubic-bezier(0.215,0.610,0.355,1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%,43%{
animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-30px,0);
transform: translate3d(0,-30px,0);
}
70%{
animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-15px,0);
transform: translate3d(0,-15px,0);
}
90%{
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
.bounce{
-webkit-animation-name: bounce;
animation-name: bounce;
-webkit-transform-origin: center bottom;
transform-origin: center bottom;
}
@-webkit-keyframes slideInUp{
from{
-webkit-transform: translate3d(0,100%,0);
transform: translate3d(0,100%,0);
visibility: visible;
}
to{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
}
@keyframes slideInUp{
from{
-webkit-transform: translate3d(0,100%,0);
transform: translate3d(0,100%,0);
visibility: visible;
}
to{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
}
.slideInUp{
-webkit-animation-name: slideInUp;
animation-name: slideInUp;
}
/* ===== Responsive ===== */
@media only screen and (max-width: 1800px){
body { font-size: 20px; }
#svgnest, #messagewrapper{ width: 60em; }
.progress{ width: 61%; }
}
@media only screen and (max-width: 1500px){
body { font-size: 16px; }
#svgnest, #messagewrapper{ width: 50em; }
/* replaces old 5em top margin */
#svgnest{ margin-top: clamp(0.75rem, 2vh, 1.5rem); }
#svgnest .logo{ width: 25%; }
#controls{ margin-top: 0.75em; }
#splash .logo{ width: 60%; margin: 0 20%; }
#splash h1.title{ margin: 0; font-size: 2em; }
#splash .subscript{ font-size: 1em; }
h1.label{ font-size: 3em; }
.progress{ width: 75%; }
}
@media only screen and (max-width: 1300px){
body { font-size: 14px; }
}
@media only screen and (max-width: 790px){
#splash{ width: 100%; }
#splash .logo{ width: 40%; margin-left: 30%; float: left; }
#splash h1.title{ margin: 0; font-size: 2em; }
#splash .subscript{ font-size: 1em; }
body { font-size: 18px; }
#splash .nav{ width: 60%; margin-left: 20%; margin-top: 2em; }
#splash .nav li{ float: none; display: block; margin-top: 1em; }
#faq{ padding: 3em; }
}
/* ===== MakeArmy splash nav & buttons — one-row, mono font, less rounded ===== */
/* use Hack sitewide */
html, body {
font-family: "Hack", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}
/* kill the old float hack and center as a single flex row */
#splash .nav {
/* override old: float:left; width:150%; margin: 4em 0 0 -20%; */
float: none;
width: auto;
margin: 3rem auto 0 auto;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
/* keep them on one line on wide screens */
flex-wrap: nowrap;
gap: 18px;
}
/* list items no longer float */
#splash .nav li {
list-style: none;
float: none;
}
/* button shape: slimmer, less rounded, mono font keeps line-height crisp */
#splash .nav .button {
border-radius: 12px; /* was pill; now closer to your other UI */
padding: 0.55em 1.2em; /* a bit tighter so all fit in one row */
font-size: 0.95em;
line-height: 1.2;
white-space: nowrap; /* prevent label wrapping */
}
/* keep left padding for icon-capable buttons (no icon on Back is fine) */
#splash .nav .button.start { padding-left: 3.7em; }
#splash .nav .button.spinner { padding-left: 3.7em; }
#splash .nav .button.upload { padding-left: 4em; }
#splash .nav .button.download { padding-left: 4em; }
#splash .nav .button.code { padding-left: 3.9em; }
#splash .nav .button.config { padding-left: 3.9em; }
#splash .nav .button.close { padding-left: 3.9em; }
/* ensure the standalone Back button visually matches height/shape */
#splash .nav .button.back {
/* no extra left padding (no icon), but same height/roundedness */
}
/* responsive fallback: allow wrapping only on truly narrow screens */
@media (max-width: 860px) {
#splash .nav {
flex-wrap: wrap;
row-gap: 12px;
}
}
/* ===== Splash buttons: sharp corners, no icons, one row ===== */
/* one-line row stays the same */
#splash .nav{
display:flex; align-items:center; justify-content:center;
flex-wrap:nowrap; gap:14px; /* you can nudge this if you need space */
margin:3rem auto 0; padding:0; width:auto; float:none;
}
/* kill legacy floats */
#splash .nav li{ list-style:none; float:none; }
/* sharper buttons + uniform padding */
#splash .nav .button{
border-radius:6px; /* sharper (not quite square) */
padding:0.55em 1.2em; /* same for all buttons */
font-size:0.95em;
line-height:1.2;
white-space:nowrap;
}
/* nuke all icon pseudo-elements from the splash row */
#splash .nav .button::before{ display:none !important; }
/* remove the old “extra left padding for icons” */
#splash .nav .button.start,
#splash .nav .button.spinner,
#splash .nav .button.upload,
#splash .nav .button.download,
#splash .nav .button.code,
#splash .nav .button.config,
#splash .nav .button.close{
padding:0.55em 1.2em;
}
/* responsive: only wrap on narrow screens */
@media (max-width: 860px){
#splash .nav{ flex-wrap:wrap; row-gap:12px; }
}
/* ===== Powered-by footer on splash ===== */
#poweredby{
margin: 1.25rem auto 0;
text-align: center;
font-size: 0.95rem;
color: var(--muted);
max-width: 72ch;
}
#poweredby a{
color: var(--accent);
text-decoration: underline;
}
#poweredby a:hover{ color: var(--accent-2); }
/* Restore icon backgrounds for app controls (non-splash) */
.button.start{ background: #1a1a1a url(img/start.svg) no-repeat 1.8em 50%; background-size: 1.4em 1.4em; padding-left: 3.7em; }
.button.spinner{ background: #1a1a1a url(img/spin.svg) no-repeat 1.8em 50%; background-size: 1.4em 1.4em; padding-left: 3.7em; }
.button.upload{ background: #1a1a1a url(img/upload.svg) no-repeat 2.2em 50%; background-size: 1em 1em; padding-left: 4em; }
.button.download{ background: #1a1a1a url(img/download.svg) no-repeat 2.2em 50%; background-size: 1em 1em; padding-left: 4em; }
.button.code{ background: #1a1a1a url(img/code.svg) no-repeat 2em 50%; background-size: 1.2em 1.2em; padding-left: 3.9em; }
.button.config{ background: #1a1a1a url(img/settings.svg) no-repeat 2em 50%; background-size: 1.2em 1.2em; padding-left: 3.9em; }
.button.close{ background: #1a1a1a url(img/close.svg) no-repeat 1.8em 50%; background-size: 2em 2em; padding-left: 3.9em; }
.button.zoomin{ background: #1a1a1a url(img/zoomin.svg) no-repeat 50% 50%; background-size: 1.5em 1.5em; }
.button.zoomout{ background: #1a1a1a url(img/zoomout.svg) no-repeat 50% 50%; background-size: 1.5em 1.5em; }
.button.exit{ background: #1a1a1a url(img/close.svg) no-repeat 50% 50%; background-size: 1.5em 1.5em; }
/* Keep splash narrow; stretch only its nav/footer for centering */
#splash{ width:28em; margin:8% auto 0 auto; }
#splash .logo{ max-width: 220px; width: 50%; margin: 0 0 0 25%; height:auto; display:block; }
#splash .nav, #poweredby{ width:100%; text-align:center; }
/* --- Splash page buttons override: text-only, no icons --- */
#splash .nav .button {
border-radius: 6px;
padding: 0.55em 1.2em;
font-size: 0.95em;
line-height: 1.2;
white-space: nowrap;
background-image: none !important;
padding-left: 1.2em !important; /* uniform spacing */
}
/* --- FINAL stacking: toolbar above panel; both above SVGs --- */
#svgnest { position: relative; }
/* SVG layers lowest */
#select, #bins { position: relative; z-index: 0; }
#select svg, #bins svg { position: absolute; z-index: 0; }
/* Toolbar must beat the panel so the gear/X is clickable */
#controls { position: relative; z-index: 1000 !important; }
/* Panel above drawings but below toolbar */
#config { position: absolute; z-index: 900 !important; pointer-events: auto; }
/* Toast always on top */
#messagewrapper { position: fixed; z-index: 99999 !important; }
/* === FINAL STACKING ORDER (toolbar above panel; both above SVGs) === */
#svgnest { position: relative; }
/* SVG canvases lowest */
#select, #bins { position: relative; z-index: 0; }
#select svg, #bins svg { position: absolute; z-index: 0; }
/* Toolbar wins over panel so the gear/X can always be clicked */
#controls { position: relative; z-index: 1000 !important; }
/* Panel overlays drawings but stays below toolbar */
#config { position: absolute; z-index: 900 !important; pointer-events: auto; }
/* Toast message on top of everything */
#messagewrapper { position: fixed; z-index: 99999 !important; }
/* Panel should NOT intercept clicks when closed */
#config:not(.active) {
pointer-events: none; /* let clicks pass through */
}
/* Keep toolbar reliably above the panel when open */
#controls { position: relative; z-index: 1000 !important; }
#controls .button { position: relative; z-index: 1001; }
/* Panel above drawings but below toolbar */
#config { position: absolute; z-index: 900 !important; }

View file

@ -0,0 +1,584 @@
/* =========================================================
SVGNest MakeArmy True-Gray Theme (final teal version)
- Neutral gray UI with Teal accents (#20C5B5)
- Fixed #svgnest top spacing (removed giant gap)
- Restores !important on SVG stroke/fill so uploaded parts display correctly
- Accent is consistent across links, buttons, tooltips, selection, progress
========================================================= */
:root{
/* Neutral grayscale foundation */
--bg: #0e0e0e; /* page background (near-black) */
--bg-2: #1a1a1a; /* panels / cards / buttons */
--bg-3: #121212; /* bins / work areas */
--text: #e0e0e0; /* primary text */
--muted: #a0a0a0; /* secondary text */
--border: #2a2a2a; /* subtle borders */
--shadow: rgba(0,0,0,0.40);
/* Teal accent */
--accent: #20C5B5; /* primary accent */
--accent-2: #1aa397; /* darker hover/active */
/* True-grays (replace any previous blues) */
--gray-1: #f0f0f0; /* headings, light text */
--gray-2: #cccccc; /* subheadings */
--gray-3: #888888; /* lines / subtle emphasis */
--gray-4: #555555; /* secondary fills */
}
/* ===== Base ===== */
html, body{
margin: 0;
padding: 0;
border: 0;
font: normal 22px/1.4 'LatoLatinWeb', helvetica, arial, verdana, sans-serif;
background-color: var(--bg);
color: var(--muted);
}
a{
color: var(--accent);
text-decoration: none;
transition: color .15s ease;
}
a:hover{
color: var(--accent-2);
text-decoration: underline;
}
h1{
font-size: 1.5em;
font-family: 'LatoLatinWebLight', helvetica, arial, verdana, sans-serif;
font-weight: normal;
margin: 1.5em 0 0.5em 0;
color: var(--gray-1);
}
h2{
font-size: 1.1em;
font-weight: bold;
margin: 0 0 0.5em 0;
color: var(--gray-2);
}
h3{
font-size: 1em;
font-weight: bold;
margin: 1em 0 0.2em 0;
color: var(--gray-2);
}
/* ===== Splash ===== */
#splash{
width: 28em;
margin: 8% auto 0 auto;
}
#splash .logo{
width: 50%;
margin: 0 0 0 25%;
height: auto;
}
#splash h1{ color: var(--accent); }
#splash h1.title{
font-size: 3.5em;
margin: 0;
padding: 0;
text-align: center;
}
.subscript{ font-size: 0.75em; }
#splash .subscript{
display: block;
color: var(--accent);
font-size: 1.45em;
text-align: center;
font-style: normal;
}
/* ===== Nav + Buttons ===== */
.nav{ margin: 0; padding: 0; }
li{ list-style: none; float: left; margin: 0; padding: 0; }
.button{
display: block;
margin: 0 0.5em;
padding: 0.6em 2.4em;
background-color: var(--bg-2);
border-radius: 5em;
border: 1px solid var(--border);
cursor: pointer;
color: var(--accent);
box-shadow: 0 1px 0 rgba(0,0,0,0.25);
transition: color .15s ease, box-shadow .15s ease, border-color .15s ease, background-color .15s ease;
}
.button a:hover{ text-decoration: none; }
.button.start{
background: var(--bg-2) url(img/start.svg) no-repeat 1.8em 50% / 1.4em 1.4em;
padding-left: 3.7em;
}
.button.spinner{
background: var(--bg-2) url(img/spin.svg) no-repeat 1.8em 50% / 1.4em 1.4em;
padding-left: 3.7em;
}
.button.upload{
background: var(--bg-2) url(img/upload.svg) no-repeat 2.2em 50% / 1em 1em;
padding-left: 4em;
}
.button.download{
background: var(--bg-2) url(img/download.svg) no-repeat 2.2em 50% / 1em 1em;
padding-left: 4em;
}
.button.code{
background: var(--bg-2) url(img/code.svg) no-repeat 2em 50% / 1.2em 1.2em;
padding-left: 3.9em;
}
.button.config{
background: var(--bg-2) url(img/settings.svg) no-repeat 2em 50% / 1.2em 1.2em;
padding-left: 3.9em;
}
.button.close{
background: var(--bg-2) url(img/close.svg) no-repeat 1.8em 50% / 2em 2em;
padding-left: 3.9em;
}
.button.zoomin{ background: var(--bg-2) url(img/zoomin.svg) no-repeat 50% 50% / 1.5em 1.5em; }
.button.zoomout{ background: var(--bg-2) url(img/zoomout.svg) no-repeat 50% 50% / 1.5em 1.5em; }
.button.exit{ background: var(--bg-2) url(img/close.svg) no-repeat 50% 50% / 1.5em 1.5em; }
.button:hover{
color: var(--accent-2);
border-color: var(--accent);
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
text-decoration: none;
}
.button:active{
background-color: #1f1f1f;
border-color: var(--accent-2);
box-shadow: inset 0 2px 2px rgba(0,0,0,0.35);
}
.button.disabled{
cursor: default; opacity: 0.5; color: #999;
-webkit-filter: saturate(0); filter: saturate(0);
}
.button.disabled:hover{ box-shadow: none; border-color: var(--border); }
.button.disabled:active{ background-color: var(--bg-2); box-shadow: none; }
#splash .nav{ float: left; width: 150%; margin: 4em 0 0 -20%; }
#faq{ display: none; float: left; margin-top: 2em; padding-bottom: 5em; }
/* ===== SVGNest layout ===== */
#svgnest, #messagewrapper{ width: 72em; }
/* FIXED: smaller, responsive top spacing (replaces 9em) */
#svgnest{
display: none;
margin: clamp(1rem, 3vh, 2.5rem) auto 0 auto;
}
#svgnest .logo, #svgnest .sidebar{ float: left; width: 22%; margin-right: 8%; }
#svgnest .sidebar h1{ font-size: 3em; color: var(--text); }
#svgnest .sidebar{ clear: both; width: 100%; margin-top: 3em; }
#svgnest .nav{ float: left; margin: 0 0 0 -0.5em; padding: 0; }
#controls{ margin-top: 0.5em; float: left; position: relative; }
/* ===== Info sidebar ===== */
#info, #info_placement{ display: none; }
h1.label{
font-size: 4em; margin: 0.2em 0 0 0; padding: 0;
line-height: 1; font-weight: normal; color: var(--text);
}
h1.label sup{ font-size: 0.5em; }
.column{ margin: 0.5em 4em 2em 0; float: left; }
/* ===== Progress ===== */
.progress{
width: 51%;
clear: both;
height: 1.2em;
background-color: var(--bg-2);
border: 1px solid var(--accent); /* accent border */
border-radius: 1em;
margin-bottom: 0.4em;
}
.progress_inner{
height: 100%;
background-color: var(--accent); /* accent fill */
border-radius: 1em;
}
/* ===== Config panel ===== */
#config{
max-height: 0;
overflow: hidden;
width: 20em;
position: absolute;
top: 0;
left: 24.5em;
background-color: var(--bg-2);
border-radius: 0.5em;
transition: max-height 0.5s;
box-shadow: 0 2px 8px var(--shadow);
}
#configwrapper{ float: left; padding: 3em 0 1em 2em; }
#config.active{ display: block; max-height: 50em; }
#configbutton{ position: relative; z-index: 2; width: 3em; padding: 0; height: 2.5em; background-position: 50%; }
#zoominbutton, #zoomoutbutton, #exitbutton{ width: 3em; padding: 0; height: 2.5em; background-position: 50%; }
#configbutton.close:hover{ box-shadow: none; }
#configsave{ margin-left: 7%; }
#config input, #config h3, #config .tooltip{ margin: 1em 0 0 0; height: 2em; padding: 0; }
#config input{
float: left; width: 13%; font-size: 1em;
border: 1px solid var(--accent);
color: var(--text);
background: #141414;
text-align: center; clear: left; border-radius: 0.4em;
transition: border-color .15s ease, background-color .15s ease;
}
#config input:hover{ background-color: #181818; border-color: var(--accent-2); }
#config input.checkbox{ width: 7%; margin-left: 4%; margin-right: 4%; border: 1px solid var(--border); }
#config h3{ float: left; width: 65%; margin-left: 5%; padding: 0; font-size: 0.8em; line-height: 3em; color: var(--muted); }
#config .tooltip{
float: left; max-width: 15%; width: 1.5em; height: 1.5em;
font-size: 0.8em; font-weight: bold; background-color: var(--accent);
color: #fff; text-align: center; line-height: 1.5; margin-top: 1.8em;
cursor: default; border-radius: 3em;
}
#config .button{ float: left; clear: both; margin-top: 2em; }
/* ===== SVG areas ===== */
#select{ margin-top: 2em; }
#select, #bins{ float: left; width: 69%; position: relative; }
#select svg, #bins svg{
width: 100%; height: auto; position: absolute; top: 0;
margin: 0; display: block; overflow: visible; pointer-events: none;
}
/* Selection strokes (use accent; override uploaded inline styles) */
#select svg *{
fill: #fff !important;
fill-opacity: 0 !important;
stroke: var(--accent) !important;
stroke-width: 2px !important;
vector-effect: non-scaling-stroke !important;
stroke-linejoin: round !important;
pointer-events: fill !important;
}
#select svg *.fullRect{
fill: #1b1b1b !important;
fill-opacity: 1 !important;
stroke: #1b1b1b !important;
stroke-width: 2px !important;
vector-effect: non-scaling-stroke !important;
stroke-linejoin: round !important;
}
#select svg *:hover{ stroke: var(--accent-2) !important; cursor: pointer !important; }
#select svg *.active{ stroke: var(--accent-2) !important; stroke-width: 3px !important; }
#select.disabled svg *, #select.disabled svg *:hover, #select.disabled svg *.active{
stroke: #6b6b6b !important; stroke-width: 2px !important; cursor: default !important;
}
/* Bins (neutral grays) */
#bins svg{ margin-bottom: 2em; }
#bins svg.grid{ float: left; width: 45%; margin-right: 5%; min-width: 20em; }
#bins svg *{
fill: var(--gray-4) !important;
stroke: var(--gray-3) !important;
stroke-width: 2px !important;
vector-effect: non-scaling-stroke !important;
stroke-linejoin: round !important;
}
#bins svg .bin{ fill: var(--bg-3) !important; stroke: var(--gray-4) !important; }
#bins svg .hole{ fill: var(--bg) !important; stroke: var(--gray-3) !important; }
/* ===== Messages ===== */
#messagewrapper{
width: 50em; overflow: hidden;
background: var(--gray-4) url(img/close.svg) no-repeat 99% 0.5em / 3em 3em;
line-height: 4em;
position: fixed; left: 50%; margin-left: -25em; bottom: 1em;
text-align: center; border-radius: 0.5em; color: #fff;
box-shadow: 0 6px 24px var(--shadow);
border-left: 4px solid var(--accent); /* subtle accent marker */
}
#messagewrapper:hover{ background-color: #666; border-left-color: var(--accent-2); }
#message{ overflow: hidden; height: 0; }
#message.active, #message.error{ height: 4em; cursor: pointer; }
#message.error{ color: #ff314e; font-weight: bold; }
/* ===== Animations (kept intact) ===== */
.animated{
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
@-webkit-keyframes bounce{
from,20%,53%,80%,to{
-webkit-animation-timing-function: cubic-bezier(0.215,0.610,0.355,1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%,43%{
-webkit-animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-30px,0);
transform: translate3d(0,-30px,0);
}
70%{
-webkit-animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-15px,0);
transform: translate3d(0,-15px,0);
}
90%{
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
@keyframes bounce{
from,20%,53%,80%,to{
animation-timing-function: cubic-bezier(0.215,0.610,0.355,1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%,43%{
animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-30px,0);
transform: translate3d(0,-30px,0);
}
70%{
animation-timing-function: cubic-bezier(0.755,0.050,0.855,0.060);
-webkit-transform: translate3d(0,-15px,0);
transform: translate3d(0,-15px,0);
}
90%{
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
.bounce{
-webkit-animation-name: bounce;
animation-name: bounce;
-webkit-transform-origin: center bottom;
transform-origin: center bottom;
}
@-webkit-keyframes slideInUp{
from{
-webkit-transform: translate3d(0,100%,0);
transform: translate3d(0,100%,0);
visibility: visible;
}
to{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
}
@keyframes slideInUp{
from{
-webkit-transform: translate3d(0,100%,0);
transform: translate3d(0,100%,0);
visibility: visible;
}
to{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
}
.slideInUp{
-webkit-animation-name: slideInUp;
animation-name: slideInUp;
}
/* ===== Responsive ===== */
@media only screen and (max-width: 1800px){
body { font-size: 20px; }
#svgnest, #messagewrapper{ width: 60em; }
.progress{ width: 61%; }
}
@media only screen and (max-width: 1500px){
body { font-size: 16px; }
#svgnest, #messagewrapper{ width: 50em; }
/* replaces old 5em top margin */
#svgnest{ margin-top: clamp(0.75rem, 2vh, 1.5rem); }
#svgnest .logo{ width: 25%; }
#controls{ margin-top: 0.75em; }
#splash .logo{ width: 60%; margin: 0 20%; }
#splash h1.title{ margin: 0; font-size: 2em; }
#splash .subscript{ font-size: 1em; }
h1.label{ font-size: 3em; }
.progress{ width: 75%; }
}
@media only screen and (max-width: 1300px){
body { font-size: 14px; }
}
@media only screen and (max-width: 790px){
#splash{ width: 100%; }
#splash .logo{ width: 40%; margin-left: 30%; float: left; }
#splash h1.title{ margin: 0; font-size: 2em; }
#splash .subscript{ font-size: 1em; }
body { font-size: 18px; }
#splash .nav{ width: 60%; margin-left: 20%; margin-top: 2em; }
#splash .nav li{ float: none; display: block; margin-top: 1em; }
#faq{ padding: 3em; }
}
/* ===== MakeArmy splash nav & buttons — one-row, mono font, less rounded ===== */
/* use Hack sitewide */
html, body {
font-family: "Hack", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}
/* kill the old float hack and center as a single flex row */
#splash .nav {
/* override old: float:left; width:150%; margin: 4em 0 0 -20%; */
float: none;
width: auto;
margin: 3rem auto 0 auto;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
/* keep them on one line on wide screens */
flex-wrap: nowrap;
gap: 18px;
}
/* list items no longer float */
#splash .nav li {
list-style: none;
float: none;
}
/* button shape: slimmer, less rounded, mono font keeps line-height crisp */
#splash .nav .button {
border-radius: 12px; /* was pill; now closer to your other UI */
padding: 0.55em 1.2em; /* a bit tighter so all fit in one row */
font-size: 0.95em;
line-height: 1.2;
white-space: nowrap; /* prevent label wrapping */
}
/* keep left padding for icon-capable buttons (no icon on Back is fine) */
#splash .nav .button.start { padding-left: 3.7em; }
#splash .nav .button.spinner { padding-left: 3.7em; }
#splash .nav .button.upload { padding-left: 4em; }
#splash .nav .button.download { padding-left: 4em; }
#splash .nav .button.code { padding-left: 3.9em; }
#splash .nav .button.config { padding-left: 3.9em; }
#splash .nav .button.close { padding-left: 3.9em; }
/* ensure the standalone Back button visually matches height/shape */
#splash .nav .button.back {
/* no extra left padding (no icon), but same height/roundedness */
}
/* responsive fallback: allow wrapping only on truly narrow screens */
@media (max-width: 860px) {
#splash .nav {
flex-wrap: wrap;
row-gap: 12px;
}
}
/* ===== Splash buttons: sharp corners, no icons, one row ===== */
/* one-line row stays the same */
#splash .nav{
display:flex; align-items:center; justify-content:center;
flex-wrap:nowrap; gap:14px; /* you can nudge this if you need space */
margin:3rem auto 0; padding:0; width:auto; float:none;
}
/* kill legacy floats */
#splash .nav li{ list-style:none; float:none; }
/* sharper buttons + uniform padding */
#splash .nav .button{
border-radius:6px; /* sharper (not quite square) */
padding:0.55em 1.2em; /* same for all buttons */
font-size:0.95em;
line-height:1.2;
white-space:nowrap;
}
/* nuke all icon pseudo-elements from the splash row */
#splash .nav .button::before{ display:none !important; }
/* remove the old “extra left padding for icons” */
#splash .nav .button.start,
#splash .nav .button.spinner,
#splash .nav .button.upload,
#splash .nav .button.download,
#splash .nav .button.code,
#splash .nav .button.config,
#splash .nav .button.close{
padding:0.55em 1.2em;
}
/* responsive: only wrap on narrow screens */
@media (max-width: 860px){
#splash .nav{ flex-wrap:wrap; row-gap:12px; }
}
/* ===== Powered-by footer on splash ===== */
#poweredby{
margin: 1.25rem auto 0;
text-align: center;
font-size: 0.95rem;
color: var(--muted);
max-width: 72ch;
}
#poweredby a{
color: var(--accent);
text-decoration: underline;
}
#poweredby a:hover{ color: var(--accent-2); }
/* Restore icon backgrounds for app controls (non-splash) */
.button.start{ background: #1a1a1a url(img/start.svg) no-repeat 1.8em 50%; background-size: 1.4em 1.4em; padding-left: 3.7em; }
.button.spinner{ background: #1a1a1a url(img/spin.svg) no-repeat 1.8em 50%; background-size: 1.4em 1.4em; padding-left: 3.7em; }
.button.upload{ background: #1a1a1a url(img/upload.svg) no-repeat 2.2em 50%; background-size: 1em 1em; padding-left: 4em; }
.button.download{ background: #1a1a1a url(img/download.svg) no-repeat 2.2em 50%; background-size: 1em 1em; padding-left: 4em; }
.button.code{ background: #1a1a1a url(img/code.svg) no-repeat 2em 50%; background-size: 1.2em 1.2em; padding-left: 3.9em; }
.button.config{ background: #1a1a1a url(img/settings.svg) no-repeat 2em 50%; background-size: 1.2em 1.2em; padding-left: 3.9em; }
.button.close{ background: #1a1a1a url(img/close.svg) no-repeat 1.8em 50%; background-size: 2em 2em; padding-left: 3.9em; }
.button.zoomin{ background: #1a1a1a url(img/zoomin.svg) no-repeat 50% 50%; background-size: 1.5em 1.5em; }
.button.zoomout{ background: #1a1a1a url(img/zoomout.svg) no-repeat 50% 50%; background-size: 1.5em 1.5em; }
.button.exit{ background: #1a1a1a url(img/close.svg) no-repeat 50% 50%; background-size: 1.5em 1.5em; }
/* Keep splash narrow; stretch only its nav/footer for centering */
#splash{ width:28em; margin:8% auto 0 auto; }
#splash .logo{ max-width: 220px; width: 50%; margin: 0 0 0 25%; height:auto; display:block; }
#splash .nav, #poweredby{ width:100%; text-align:center; }
/* --- Splash page buttons override: text-only, no icons --- */
#splash .nav .button {
border-radius: 6px;
padding: 0.55em 1.2em;
font-size: 0.95em;
line-height: 1.2;
white-space: nowrap;
background-image: none !important;
padding-left: 1.2em !important; /* uniform spacing */
}

988
public/svgnest/svgnest.js Normal file
View file

@ -0,0 +1,988 @@
/*!
* SvgNest
* Licensed under the MIT license
*/
(function(root){
'use strict';
root.SvgNest = new SvgNest();
function SvgNest(){
var self = this;
var svg = null;
// keep a reference to any style nodes, to maintain color/fill info
this.style = null;
var parts = null;
var tree = null;
var bin = null;
var binPolygon = null;
var binBounds = null;
var nfpCache = {};
var config = {
clipperScale: 10000000,
curveTolerance: 0.3,
spacing: 0,
rotations: 4,
populationSize: 10,
mutationRate: 10,
useHoles: false,
exploreConcave: false
};
this.working = false;
var GA = null;
var best = null;
var workerTimer = null;
var progress = 0;
this.parsesvg = function(svgstring){
// reset if in progress
this.stop();
bin = null;
binPolygon = null;
tree = null;
// parse svg
svg = SvgParser.load(svgstring);
this.style = SvgParser.getStyle();
svg = SvgParser.clean();
tree = this.getParts(svg.childNodes);
//re-order elements such that deeper elements are on top, so they can be moused over
function zorder(paths){
// depth-first
var length = paths.length;
for(var i=0; i<length; i++){
if(paths[i].children && paths[i].children.length > 0){
zorder(paths[i].children);
}
}
}
return svg;
}
this.setbin = function(element){
if(!svg){
return;
}
bin = element;
}
this.config = function(c){
// clean up inputs
if(!c){
return config;
}
if(c.curveTolerance && !GeometryUtil.almostEqual(parseFloat(c.curveTolerance), 0)){
config.curveTolerance = parseFloat(c.curveTolerance);
}
if('spacing' in c){
config.spacing = parseFloat(c.spacing);
}
if(c.rotations && parseInt(c.rotations) > 0){
config.rotations = parseInt(c.rotations);
}
if(c.populationSize && parseInt(c.populationSize) > 2){
config.populationSize = parseInt(c.populationSize);
}
if(c.mutationRate && parseInt(c.mutationRate) > 0){
config.mutationRate = parseInt(c.mutationRate);
}
if('useHoles' in c){
config.useHoles = !!c.useHoles;
}
if('exploreConcave' in c){
config.exploreConcave = !!c.exploreConcave;
}
SvgParser.config({ tolerance: config.curveTolerance});
best = null;
nfpCache = {};
binPolygon = null;
GA = null;
return config;
}
// progressCallback is called when progress is made
// displayCallback is called when a new placement has been made
this.start = function(progressCallback, displayCallback){
if(!svg || !bin){
return false;
}
parts = Array.prototype.slice.call(svg.childNodes);
var binindex = parts.indexOf(bin);
if(binindex >= 0){
// don't process bin as a part of the tree
parts.splice(binindex, 1);
}
// build tree without bin
tree = this.getParts(parts.slice(0));
offsetTree(tree, 0.5*config.spacing, this.polygonOffset.bind(this));
// offset tree recursively
function offsetTree(t, offset, offsetFunction){
for(var i=0; i<t.length; i++){
var offsetpaths = offsetFunction(t[i], offset);
if(offsetpaths.length == 1){
// replace array items in place
Array.prototype.splice.apply(t[i], [0, t[i].length].concat(offsetpaths[0]));
}
if(t[i].childNodes && t[i].childNodes.length > 0){
offsetTree(t[i].childNodes, -offset, offsetFunction);
}
}
}
binPolygon = SvgParser.polygonify(bin);
binPolygon = this.cleanPolygon(binPolygon);
if(!binPolygon || binPolygon.length < 3){
return false;
}
binBounds = GeometryUtil.getPolygonBounds(binPolygon);
if(config.spacing > 0){
var offsetBin = this.polygonOffset(binPolygon, -0.5*config.spacing);
if(offsetBin.length == 1){
// if the offset contains 0 or more than 1 path, something went wrong.
binPolygon = offsetBin.pop();
}
}
binPolygon.id = -1;
// put bin on origin
var xbinmax = binPolygon[0].x;
var xbinmin = binPolygon[0].x;
var ybinmax = binPolygon[0].y;
var ybinmin = binPolygon[0].y;
for(var i=1; i<binPolygon.length; i++){
if(binPolygon[i].x > xbinmax){
xbinmax = binPolygon[i].x;
}
else if(binPolygon[i].x < xbinmin){
xbinmin = binPolygon[i].x;
}
if(binPolygon[i].y > ybinmax){
ybinmax = binPolygon[i].y;
}
else if(binPolygon[i].y < ybinmin){
ybinmin = binPolygon[i].y;
}
}
for(i=0; i<binPolygon.length; i++){
binPolygon[i].x -= xbinmin;
binPolygon[i].y -= ybinmin;
}
binPolygon.width = xbinmax-xbinmin;
binPolygon.height = ybinmax-ybinmin;
// all paths need to have the same winding direction
if(GeometryUtil.polygonArea(binPolygon) > 0){
binPolygon.reverse();
}
// remove duplicate endpoints, ensure counterclockwise winding direction
for(i=0; i<tree.length; i++){
var start = tree[i][0];
var end = tree[i][tree[i].length-1];
if(start == end || (GeometryUtil.almostEqual(start.x,end.x) && GeometryUtil.almostEqual(start.y,end.y))){
tree[i].pop();
}
if(GeometryUtil.polygonArea(tree[i]) > 0){
tree[i].reverse();
}
}
var self = this;
this.working = false;
workerTimer = setInterval(function(){
if(!self.working){
self.launchWorkers.call(self, tree, binPolygon, config, progressCallback, displayCallback);
self.working = true;
}
progressCallback(progress);
}, 100);
}
this.launchWorkers = function(tree, binPolygon, config, progressCallback, displayCallback){
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex ;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
var i,j;
if(GA === null){
// initiate new GA
var adam = tree.slice(0);
// seed with decreasing area
adam.sort(function(a, b){
return Math.abs(GeometryUtil.polygonArea(b)) - Math.abs(GeometryUtil.polygonArea(a));
});
GA = new GeneticAlgorithm(adam, binPolygon, config);
}
var individual = null;
// evaluate all members of the population
for(i=0; i<GA.population.length; i++){
if(!GA.population[i].fitness){
individual = GA.population[i];
break;
}
}
if(individual === null){
// all individuals have been evaluated, start next generation
GA.generation();
individual = GA.population[1];
}
var placelist = individual.placement;
var rotations = individual.rotation;
var ids = [];
for(i=0; i<placelist.length; i++){
ids.push(placelist[i].id);
placelist[i].rotation = rotations[i];
}
var nfpPairs = [];
var key;
var newCache = {};
for(i=0; i<placelist.length; i++){
var part = placelist[i];
key = {A: binPolygon.id, B: part.id, inside: true, Arotation: 0, Brotation: rotations[i]};
if(!nfpCache[JSON.stringify(key)]){
nfpPairs.push({A: binPolygon, B: part, key: key});
}
else{
newCache[JSON.stringify(key)] = nfpCache[JSON.stringify(key)]
}
for(j=0; j<i; j++){
var placed = placelist[j];
key = {A: placed.id, B: part.id, inside: false, Arotation: rotations[j], Brotation: rotations[i]};
if(!nfpCache[JSON.stringify(key)]){
nfpPairs.push({A: placed, B: part, key: key});
}
else{
newCache[JSON.stringify(key)] = nfpCache[JSON.stringify(key)]
}
}
}
// only keep cache for one cycle
nfpCache = newCache;
var worker = new PlacementWorker(binPolygon, placelist.slice(0), ids, rotations, config, nfpCache);
var p = new Parallel(nfpPairs, {
env: {
binPolygon: binPolygon,
searchEdges: config.exploreConcave,
useHoles: config.useHoles
},
evalPath: 'util/eval.js'
});
p.require('matrix.js');
p.require('geometryutil.js');
p.require('placementworker.js');
p.require('clipper.js');
var self = this;
var spawncount = 0;
p._spawnMapWorker = function (i, cb, done, env, wrk){
// hijack the worker call to check progress
progress = spawncount++/nfpPairs.length;
return Parallel.prototype._spawnMapWorker.call(p, i, cb, done, env, wrk);
}
p.map(function(pair){
if(!pair || pair.length == 0){
return null;
}
var searchEdges = global.env.searchEdges;
var useHoles = global.env.useHoles;
var A = rotatePolygon(pair.A, pair.key.Arotation);
var B = rotatePolygon(pair.B, pair.key.Brotation);
var nfp;
if(pair.key.inside){
if(GeometryUtil.isRectangle(A, 0.001)){
nfp = GeometryUtil.noFitPolygonRectangle(A,B);
}
else{
nfp = GeometryUtil.noFitPolygon(A,B,true,searchEdges);
}
// ensure all interior NFPs have the same winding direction
if(nfp && nfp.length > 0){
for(var i=0; i<nfp.length; i++){
if(GeometryUtil.polygonArea(nfp[i]) > 0){
nfp[i].reverse();
}
}
}
else{
// warning on null inner NFP
// this is not an error, as the part may simply be larger than the bin or otherwise unplaceable due to geometry
log('NFP Warning: ', pair.key);
}
}
else{
if(searchEdges){
nfp = GeometryUtil.noFitPolygon(A,B,false,searchEdges);
}
else{
nfp = minkowskiDifference(A,B);
}
// sanity check
if(!nfp || nfp.length == 0){
log('NFP Error: ', pair.key);
log('A: ',JSON.stringify(A));
log('B: ',JSON.stringify(B));
return null;
}
for(var i=0; i<nfp.length; i++){
if(!searchEdges || i==0){ // if searchedges is active, only the first NFP is guaranteed to pass sanity check
if(Math.abs(GeometryUtil.polygonArea(nfp[i])) < Math.abs(GeometryUtil.polygonArea(A))){
log('NFP Area Error: ', Math.abs(GeometryUtil.polygonArea(nfp[i])), pair.key);
log('NFP:', JSON.stringify(nfp[i]));
log('A: ',JSON.stringify(A));
log('B: ',JSON.stringify(B));
nfp.splice(i,1);
return null;
}
}
}
if(nfp.length == 0){
return null;
}
// for outer NFPs, the first is guaranteed to be the largest. Any subsequent NFPs that lie inside the first are holes
for(var i=0; i<nfp.length; i++){
if(GeometryUtil.polygonArea(nfp[i]) > 0){
nfp[i].reverse();
}
if(i > 0){
if(GeometryUtil.pointInPolygon(nfp[i][0], nfp[0])){
if(GeometryUtil.polygonArea(nfp[i]) < 0){
nfp[i].reverse();
}
}
}
}
// generate nfps for children (holes of parts) if any exist
if(useHoles && A.childNodes && A.childNodes.length > 0){
var Bbounds = GeometryUtil.getPolygonBounds(B);
for(var i=0; i<A.childNodes.length; i++){
var Abounds = GeometryUtil.getPolygonBounds(A.childNodes[i]);
// no need to find nfp if B's bounding box is too big
if(Abounds.width > Bbounds.width && Abounds.height > Bbounds.height){
var cnfp = GeometryUtil.noFitPolygon(A.childNodes[i],B,true,searchEdges);
// ensure all interior NFPs have the same winding direction
if(cnfp && cnfp.length > 0){
for(var j=0; j<cnfp.length; j++){
if(GeometryUtil.polygonArea(cnfp[j]) < 0){
cnfp[j].reverse();
}
nfp.push(cnfp[j]);
}
}
}
}
}
}
function log(){
if(typeof console !== "undefined") {
console.log.apply(console,arguments);
}
}
function toClipperCoordinates(polygon){
var clone = [];
for(var i=0; i<polygon.length; i++){
clone.push({
X: polygon[i].x,
Y: polygon[i].y
});
}
return clone;
};
function toNestCoordinates(polygon, scale){
var clone = [];
for(var i=0; i<polygon.length; i++){
clone.push({
x: polygon[i].X/scale,
y: polygon[i].Y/scale
});
}
return clone;
};
function minkowskiDifference(A, B){
var Ac = toClipperCoordinates(A);
ClipperLib.JS.ScaleUpPath(Ac, 10000000);
var Bc = toClipperCoordinates(B);
ClipperLib.JS.ScaleUpPath(Bc, 10000000);
for(var i=0; i<Bc.length; i++){
Bc[i].X *= -1;
Bc[i].Y *= -1;
}
var solution = ClipperLib.Clipper.MinkowskiSum(Ac, Bc, true);
var clipperNfp;
var largestArea = null;
for(i=0; i<solution.length; i++){
var n = toNestCoordinates(solution[i], 10000000);
var sarea = GeometryUtil.polygonArea(n);
if(largestArea === null || largestArea > sarea){
clipperNfp = n;
largestArea = sarea;
}
}
for(var i=0; i<clipperNfp.length; i++){
clipperNfp[i].x += B[0].x;
clipperNfp[i].y += B[0].y;
}
return [clipperNfp];
}
return {key: pair.key, value: nfp};
}).then(function(generatedNfp){
if(generatedNfp){
for(var i=0; i<generatedNfp.length; i++){
var Nfp = generatedNfp[i];
if(Nfp){
// a null nfp means the nfp could not be generated, either because the parts simply don't fit or an error in the nfp algo
var key = JSON.stringify(Nfp.key);
nfpCache[key] = Nfp.value;
}
}
}
worker.nfpCache = nfpCache;
// can't use .spawn because our data is an array
var p2 = new Parallel([placelist.slice(0)], {
env: {
self: worker
},
evalPath: 'util/eval.js'
});
p2.require('json.js');
p2.require('clipper.js');
p2.require('matrix.js');
p2.require('geometryutil.js');
p2.require('placementworker.js');
p2.map(worker.placePaths).then(function(placements){
if(!placements || placements.length == 0){
return;
}
individual.fitness = placements[0].fitness;
var bestresult = placements[0];
for(var i=1; i<placements.length; i++){
if(placements[i].fitness < bestresult.fitness){
bestresult = placements[i];
}
}
if(!best || bestresult.fitness < best.fitness){
best = bestresult;
var placedArea = 0;
var totalArea = 0;
var numParts = placelist.length;
var numPlacedParts = 0;
for(i=0; i<best.placements.length; i++){
totalArea += Math.abs(GeometryUtil.polygonArea(binPolygon));
for(var j=0; j<best.placements[i].length; j++){
placedArea += Math.abs(GeometryUtil.polygonArea(tree[best.placements[i][j].id]));
numPlacedParts++;
}
}
displayCallback(self.applyPlacement(best.placements), placedArea/totalArea, numPlacedParts, numParts);
}
else{
displayCallback();
}
self.working = false;
}, function (err) {
console.log(err);
});
}, function (err) {
console.log(err);
});
}
// assuming no intersections, return a tree where odd leaves are parts and even ones are holes
// might be easier to use the DOM, but paths can't have paths as children. So we'll just make our own tree.
this.getParts = function(paths){
var i, j;
var polygons = [];
var numChildren = paths.length;
for(i=0; i<numChildren; i++){
var poly = SvgParser.polygonify(paths[i]);
poly = this.cleanPolygon(poly);
// todo: warn user if poly could not be processed and is excluded from the nest
if(poly && poly.length > 2 && Math.abs(GeometryUtil.polygonArea(poly)) > config.curveTolerance*config.curveTolerance){
poly.source = i;
polygons.push(poly);
}
}
// turn the list into a tree
toTree(polygons);
function toTree(list, idstart){
var parents = [];
var i,j;
// assign a unique id to each leaf
var id = idstart || 0;
for(i=0; i<list.length; i++){
var p = list[i];
var ischild = false;
for(j=0; j<list.length; j++){
if(j==i){
continue;
}
if(GeometryUtil.pointInPolygon(p[0], list[j]) === true){
if(!list[j].children){
list[j].children = [];
}
list[j].children.push(p);
p.parent = list[j];
ischild = true;
break;
}
}
if(!ischild){
parents.push(p);
}
}
for(i=0; i<list.length; i++){
if(parents.indexOf(list[i]) < 0){
list.splice(i, 1);
i--;
}
}
for(i=0; i<parents.length; i++){
parents[i].id = id;
id++;
}
for(i=0; i<parents.length; i++){
if(parents[i].children){
id = toTree(parents[i].children, id);
}
}
return id;
};
return polygons;
};
// use the clipper library to return an offset to the given polygon. Positive offset expands the polygon, negative contracts
// note that this returns an array of polygons
this.polygonOffset = function(polygon, offset){
if(!offset || offset == 0 || GeometryUtil.almostEqual(offset, 0)){
return polygon;
}
var p = this.svgToClipper(polygon);
var miterLimit = 2;
var co = new ClipperLib.ClipperOffset(miterLimit, config.curveTolerance*config.clipperScale);
co.AddPath(p, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
var newpaths = new ClipperLib.Paths();
co.Execute(newpaths, offset*config.clipperScale);
var result = [];
for(var i=0; i<newpaths.length; i++){
result.push(this.clipperToSvg(newpaths[i]));
}
return result;
};
// returns a less complex polygon that satisfies the curve tolerance
this.cleanPolygon = function(polygon){
var p = this.svgToClipper(polygon);
// remove self-intersections and find the biggest polygon that's left
var simple = ClipperLib.Clipper.SimplifyPolygon(p, ClipperLib.PolyFillType.pftNonZero);
if(!simple || simple.length == 0){
return null;
}
var biggest = simple[0];
var biggestarea = Math.abs(ClipperLib.Clipper.Area(biggest));
for(var i=1; i<simple.length; i++){
var area = Math.abs(ClipperLib.Clipper.Area(simple[i]));
if(area > biggestarea){
biggest = simple[i];
biggestarea = area;
}
}
// clean up singularities, coincident points and edges
var clean = ClipperLib.Clipper.CleanPolygon(biggest, config.curveTolerance*config.clipperScale);
if(!clean || clean.length == 0){
return null;
}
return this.clipperToSvg(clean);
}
// converts a polygon from normal float coordinates to integer coordinates used by clipper, as well as x/y -> X/Y
this.svgToClipper = function(polygon){
var clip = [];
for(var i=0; i<polygon.length; i++){
clip.push({X: polygon[i].x, Y: polygon[i].y});
}
ClipperLib.JS.ScaleUpPath(clip, config.clipperScale);
return clip;
}
this.clipperToSvg = function(polygon){
var normal = [];
for(var i=0; i<polygon.length; i++){
normal.push({x: polygon[i].X/config.clipperScale, y: polygon[i].Y/config.clipperScale});
}
return normal;
}
// returns an array of SVG elements that represent the placement, for export or rendering
this.applyPlacement = function(placement){
var i, j, k;
var clone = [];
for(i=0; i<parts.length; i++){
clone.push(parts[i].cloneNode(false));
}
var svglist = [];
for(i=0; i<placement.length; i++){
var newsvg = svg.cloneNode(false);
newsvg.setAttribute('viewBox', '0 0 '+binBounds.width+' '+binBounds.height);
newsvg.setAttribute('width',binBounds.width + 'px');
newsvg.setAttribute('height',binBounds.height + 'px');
var binclone = bin.cloneNode(false);
binclone.setAttribute('class','bin');
binclone.setAttribute('transform','translate('+(-binBounds.x)+' '+(-binBounds.y)+')');
newsvg.appendChild(binclone);
for(j=0; j<placement[i].length; j++){
var p = placement[i][j];
var part = tree[p.id];
// the original path could have transforms and stuff on it, so apply our transforms on a group
var partgroup = document.createElementNS(svg.namespaceURI, 'g');
partgroup.setAttribute('transform','translate('+p.x+' '+p.y+') rotate('+p.rotation+')');
partgroup.appendChild(clone[part.source]);
if(part.children && part.children.length > 0){
var flattened = _flattenTree(part.children, true);
for(k=0; k<flattened.length; k++){
var c = clone[flattened[k].source];
// add class to indicate hole
if(flattened[k].hole && (!c.getAttribute('class') || c.getAttribute('class').indexOf('hole') < 0)){
c.setAttribute('class',c.getAttribute('class')+' hole');
}
partgroup.appendChild(c);
}
}
newsvg.appendChild(partgroup);
}
svglist.push(newsvg);
}
// flatten the given tree into a list
function _flattenTree(t, hole){
var flat = [];
for(var i=0; i<t.length; i++){
flat.push(t[i]);
t[i].hole = hole;
if(t[i].children && t[i].children.length > 0){
flat = flat.concat(_flattenTree(t[i].children, !hole));
}
}
return flat;
}
return svglist;
}
this.stop = function(){
this.working = false;
if(workerTimer){
clearInterval(workerTimer);
}
};
}
function GeneticAlgorithm(adam, bin, config){
this.config = config || { populationSize: 10, mutationRate: 10, rotations: 4 };
this.binBounds = GeometryUtil.getPolygonBounds(bin);
// population is an array of individuals. Each individual is a object representing the order of insertion and the angle each part is rotated
var angles = [];
for(var i=0; i<adam.length; i++){
angles.push(this.randomAngle(adam[i]));
}
this.population = [{placement: adam, rotation: angles}];
while(this.population.length < config.populationSize){
var mutant = this.mutate(this.population[0]);
this.population.push(mutant);
}
}
// returns a random angle of insertion
GeneticAlgorithm.prototype.randomAngle = function(part){
var angleList = [];
for(var i=0; i<Math.max(this.config.rotations,1); i++){
angleList.push(i*(360/this.config.rotations));
}
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
angleList = shuffleArray(angleList);
for(i=0; i<angleList.length; i++){
var rotatedPart = GeometryUtil.rotatePolygon(part, angleList[i]);
// don't use obviously bad angles where the part doesn't fit in the bin
if(rotatedPart.width < this.binBounds.width && rotatedPart.height < this.binBounds.height){
return angleList[i];
}
}
return 0;
}
// returns a mutated individual with the given mutation rate
GeneticAlgorithm.prototype.mutate = function(individual){
var clone = {placement: individual.placement.slice(0), rotation: individual.rotation.slice(0)};
for(var i=0; i<clone.placement.length; i++){
var rand = Math.random();
if(rand < 0.01*this.config.mutationRate){
// swap current part with next part
var j = i+1;
if(j < clone.placement.length){
var temp = clone.placement[i];
clone.placement[i] = clone.placement[j];
clone.placement[j] = temp;
}
}
rand = Math.random();
if(rand < 0.01*this.config.mutationRate){
clone.rotation[i] = this.randomAngle(clone.placement[i]);
}
}
return clone;
}
// single point crossover
GeneticAlgorithm.prototype.mate = function(male, female){
var cutpoint = Math.round(Math.min(Math.max(Math.random(), 0.1), 0.9)*(male.placement.length-1));
var gene1 = male.placement.slice(0,cutpoint);
var rot1 = male.rotation.slice(0,cutpoint);
var gene2 = female.placement.slice(0,cutpoint);
var rot2 = female.rotation.slice(0,cutpoint);
var i;
for(i=0; i<female.placement.length; i++){
if(!contains(gene1, female.placement[i].id)){
gene1.push(female.placement[i]);
rot1.push(female.rotation[i]);
}
}
for(i=0; i<male.placement.length; i++){
if(!contains(gene2, male.placement[i].id)){
gene2.push(male.placement[i]);
rot2.push(male.rotation[i]);
}
}
function contains(gene, id){
for(var i=0; i<gene.length; i++){
if(gene[i].id == id){
return true;
}
}
return false;
}
return [{placement: gene1, rotation: rot1},{placement: gene2, rotation: rot2}];
}
GeneticAlgorithm.prototype.generation = function(){
// Individuals with higher fitness are more likely to be selected for mating
this.population.sort(function(a, b){
return a.fitness - b.fitness;
});
// fittest individual is preserved in the new generation (elitism)
var newpopulation = [this.population[0]];
while(newpopulation.length < this.population.length){
var male = this.randomWeightedIndividual();
var female = this.randomWeightedIndividual(male);
// each mating produces two children
var children = this.mate(male, female);
// slightly mutate children
newpopulation.push(this.mutate(children[0]));
if(newpopulation.length < this.population.length){
newpopulation.push(this.mutate(children[1]));
}
}
this.population = newpopulation;
}
// returns a random individual from the population, weighted to the front of the list (lower fitness value is more likely to be selected)
GeneticAlgorithm.prototype.randomWeightedIndividual = function(exclude){
var pop = this.population.slice(0);
if(exclude && pop.indexOf(exclude) >= 0){
pop.splice(pop.indexOf(exclude),1);
}
var rand = Math.random();
var lower = 0;
var weight = 1/pop.length;
var upper = weight;
for(var i=0; i<pop.length; i++){
// if the random number falls between lower and upper bounds, select this individual
if(rand > lower && rand < upper){
return pop[i];
}
lower = upper;
upper += 2*weight * ((pop.length-i)/pop.length);
}
return pop[0];
}
})(window);

793
public/svgnest/svgparser.js Normal file
View file

@ -0,0 +1,793 @@
/*!
* SvgParser
* A library to convert an SVG string to parse-able segments for CAD/CAM use
* Licensed under the MIT license
*/
(function(root){
'use strict';
function SvgParser(){
// the SVG document
this.svg;
// the top level SVG element of the SVG document
this.svgRoot;
this.allowedElements = ['svg','circle','ellipse','path','polygon','polyline','rect', 'line'];
this.conf = {
tolerance: 2, // max bound for bezier->line segment conversion, in native SVG units
toleranceSvg: 0.005 // fudge factor for browser inaccuracy in SVG unit handling
};
}
SvgParser.prototype.config = function(config){
this.conf.tolerance = config.tolerance;
}
SvgParser.prototype.load = function(svgString){
if(!svgString || typeof svgString !== 'string'){
throw Error('invalid SVG string');
}
var parser = new DOMParser();
var svg = parser.parseFromString(svgString, "image/svg+xml");
this.svgRoot = false;
if(svg){
this.svg = svg;
for(var i=0; i<svg.childNodes.length; i++){
// svg document may start with comments or text nodes
var child = svg.childNodes[i];
if(child.tagName && child.tagName == 'svg'){
this.svgRoot = child;
break;
}
}
} else {
throw new Error("Failed to parse SVG string");
}
if(!this.svgRoot){
throw new Error("SVG has no children");
}
return this.svgRoot;
}
// use the utility functions in this class to prepare the svg for CAD-CAM/nest related operations
SvgParser.prototype.cleanInput = function(){
// apply any transformations, so that all path positions etc will be in the same coordinate space
this.applyTransform(this.svgRoot);
// remove any g elements and bring all elements to the top level
this.flatten(this.svgRoot);
// remove any non-contour elements like text
this.filter(this.allowedElements);
// split any compound paths into individual path elements
this.recurse(this.svgRoot, this.splitPath);
return this.svgRoot;
}
// return style node, if any
SvgParser.prototype.getStyle = function(){
if(!this.svgRoot){
return false;
}
for(var i=0; i<this.svgRoot.childNodes.length; i++){
var el = this.svgRoot.childNodes[i];
if(el.tagName == 'style'){
return el;
}
}
return false;
}
// set the given path as absolute coords (capital commands)
// from http://stackoverflow.com/a/9677915/433888
SvgParser.prototype.pathToAbsolute = function(path){
if(!path || path.tagName != 'path'){
throw Error('invalid path');
}
var seglist = path.pathSegList;
var x=0, y=0, x0=0, y0=0, x1=0, y1=0, x2=0, y2=0;
for(var i=0; i<seglist.numberOfItems; i++){
var command = seglist.getItem(i).pathSegTypeAsLetter;
var s = seglist.getItem(i);
if (/[MLHVCSQTA]/.test(command)){
if ('x' in s) x=s.x;
if ('y' in s) y=s.y;
}
else{
if ('x1' in s) x1=x+s.x1;
if ('x2' in s) x2=x+s.x2;
if ('y1' in s) y1=y+s.y1;
if ('y2' in s) y2=y+s.y2;
if ('x' in s) x+=s.x;
if ('y' in s) y+=s.y;
switch(command){
case 'm': seglist.replaceItem(path.createSVGPathSegMovetoAbs(x,y),i); break;
case 'l': seglist.replaceItem(path.createSVGPathSegLinetoAbs(x,y),i); break;
case 'h': seglist.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x),i); break;
case 'v': seglist.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y),i); break;
case 'c': seglist.replaceItem(path.createSVGPathSegCurvetoCubicAbs(x,y,x1,y1,x2,y2),i); break;
case 's': seglist.replaceItem(path.createSVGPathSegCurvetoCubicSmoothAbs(x,y,x2,y2),i); break;
case 'q': seglist.replaceItem(path.createSVGPathSegCurvetoQuadraticAbs(x,y,x1,y1),i); break;
case 't': seglist.replaceItem(path.createSVGPathSegCurvetoQuadraticSmoothAbs(x,y),i); break;
case 'a': seglist.replaceItem(path.createSVGPathSegArcAbs(x,y,s.r1,s.r2,s.angle,s.largeArcFlag,s.sweepFlag),i); break;
case 'z': case 'Z': x=x0; y=y0; break;
}
}
// Record the start of a subpath
if (command=='M' || command=='m') x0=x, y0=y;
}
};
// takes an SVG transform string and returns corresponding SVGMatrix
// from https://github.com/fontello/svgpath
SvgParser.prototype.transformParse = function(transformString){
var operations = {
matrix: true,
scale: true,
rotate: true,
translate: true,
skewX: true,
skewY: true
};
var CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
var PARAMS_SPLIT_RE = /[\s,]+/;
var matrix = new Matrix();
var cmd, params;
// Split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
transformString.split(CMD_SPLIT_RE).forEach(function (item) {
// Skip empty elements
if (!item.length) { return; }
// remember operation
if (typeof operations[item] !== 'undefined') {
cmd = item;
return;
}
// extract params & att operation to matrix
params = item.split(PARAMS_SPLIT_RE).map(function (i) {
return +i || 0;
});
// If params count is not correct - ignore command
switch (cmd) {
case 'matrix':
if (params.length === 6) {
matrix.matrix(params);
}
return;
case 'scale':
if (params.length === 1) {
matrix.scale(params[0], params[0]);
} else if (params.length === 2) {
matrix.scale(params[0], params[1]);
}
return;
case 'rotate':
if (params.length === 1) {
matrix.rotate(params[0], 0, 0);
} else if (params.length === 3) {
matrix.rotate(params[0], params[1], params[2]);
}
return;
case 'translate':
if (params.length === 1) {
matrix.translate(params[0], 0);
} else if (params.length === 2) {
matrix.translate(params[0], params[1]);
}
return;
case 'skewX':
if (params.length === 1) {
matrix.skewX(params[0]);
}
return;
case 'skewY':
if (params.length === 1) {
matrix.skewY(params[0]);
}
return;
}
});
return matrix;
}
// recursively apply the transform property to the given element
SvgParser.prototype.applyTransform = function(element, globalTransform){
globalTransform = globalTransform || '';
var transformString = element.getAttribute('transform') || '';
transformString = globalTransform + transformString;
var transform, scale, rotate;
if(transformString && transformString.length > 0){
var transform = this.transformParse(transformString);
}
if(!transform){
transform = new Matrix();
}
var tarray = transform.toArray();
// decompose affine matrix to rotate, scale components (translate is just the 3rd column)
var rotate = Math.atan2(tarray[1], tarray[3])*180/Math.PI;
var scale = Math.sqrt(tarray[0]*tarray[0]+tarray[2]*tarray[2]);
if(element.tagName == 'g' || element.tagName == 'svg' || element.tagName == 'defs' || element.tagName == 'clipPath'){
element.removeAttribute('transform');
var children = Array.prototype.slice.call(element.childNodes);
for(var i=0; i<children.length; i++){
if(children[i].tagName){ // skip text nodes
this.applyTransform(children[i], transformString);
}
}
}
else if(transform && !transform.isIdentity()){
const id = element.getAttribute('id')
const className = element.getAttribute('class')
switch(element.tagName){
case 'ellipse':
// the goal is to remove the transform property, but an ellipse without a transform will have no rotation
// for the sake of simplicity, we will replace the ellipse with a path, and apply the transform to that path
var path = this.svg.createElementNS(element.namespaceURI, 'path');
var move = path.createSVGPathSegMovetoAbs(parseFloat(element.getAttribute('cx'))-parseFloat(element.getAttribute('rx')),element.getAttribute('cy'));
var arc1 = path.createSVGPathSegArcAbs(parseFloat(element.getAttribute('cx'))+parseFloat(element.getAttribute('rx')),element.getAttribute('cy'),element.getAttribute('rx'),element.getAttribute('ry'),0,1,0);
var arc2 = path.createSVGPathSegArcAbs(parseFloat(element.getAttribute('cx'))-parseFloat(element.getAttribute('rx')),element.getAttribute('cy'),element.getAttribute('rx'),element.getAttribute('ry'),0,1,0);
path.pathSegList.appendItem(move);
path.pathSegList.appendItem(arc1);
path.pathSegList.appendItem(arc2);
path.pathSegList.appendItem(path.createSVGPathSegClosePath());
var transformProperty = element.getAttribute('transform');
if(transformProperty){
path.setAttribute('transform', transformProperty);
}
element.parentElement.replaceChild(path, element);
element = path;
case 'path':
this.pathToAbsolute(element);
var seglist = element.pathSegList;
var prevx = 0;
var prevy = 0;
let transformedPath = '';
for(var i=0; i<seglist.numberOfItems; i++){
var s = seglist.getItem(i);
var command = s.pathSegTypeAsLetter;
if(command == 'H'){
seglist.replaceItem(element.createSVGPathSegLinetoAbs(s.x,prevy),i);
s = seglist.getItem(i);
}
else if(command == 'V'){
seglist.replaceItem(element.createSVGPathSegLinetoAbs(prevx,s.y),i);
s = seglist.getItem(i);
}
// currently only works for uniform scale, no skew
// todo: fully support arbitrary affine transforms...
else if(command == 'A'){
seglist.replaceItem(element.createSVGPathSegArcAbs(s.x,s.y,s.r1*scale,s.r2*scale,s.angle+rotate,s.largeArcFlag,s.sweepFlag),i);
s = seglist.getItem(i);
}
const transPoints = {};
if('x' in s && 'y' in s){
var transformed = transform.calc(s.x, s.y);
prevx = s.x;
prevy = s.y;
transPoints.x = transformed[0];
transPoints.y = transformed[1];
}
if('x1' in s && 'y1' in s){
var transformed = transform.calc(s.x1, s.y1);
transPoints.x1 = transformed[0];
transPoints.y1 = transformed[1];
}
if('x2' in s && 'y2' in s){
var transformed = transform.calc(s.x2, s.y2);
transPoints.x2 = transformed[0];
transPoints.y2 = transformed[1];
}
let commandStringTransformed = ``;
//MLHVCSQTA
//H and V are transformed to "L" commands above so we don't need to handle them. All lowercase (relative) are already handled too (converted to absolute)
switch(command) {
case 'M':
commandStringTransformed += `${command} ${transPoints.x} ${transPoints.y}`;
break;
case 'L':
commandStringTransformed += `${command} ${transPoints.x} ${transPoints.y}`;
break;
case 'C':
commandStringTransformed += `${command} ${transPoints.x1} ${transPoints.y1} ${transPoints.x2} ${transPoints.y2} ${transPoints.x} ${transPoints.y}`;
break;
case 'S':
commandStringTransformed += `${command} ${transPoints.x2} ${transPoints.y2} ${transPoints.x} ${transPoints.y}`;
break;
case 'Q':
commandStringTransformed += `${command} ${transPoints.x1} ${transPoints.y1} ${transPoints.x} ${transPoints.y}`;
break;
case 'T':
commandStringTransformed += `${command} ${transPoints.x} ${transPoints.y}`;
break;
case 'A':
const largeArcFlag = s.largeArcFlag ? 1 : 0;
const sweepFlag = s.sweepFlag ? 1 : 0;
commandStringTransformed += `${command} ${s.r1} ${s.r2} ${s.angle} ${largeArcFlag} ${sweepFlag} ${transPoints.x} ${transPoints.y}`
break;
case 'H':
commandStringTransformed += `L ${transPoints.x} ${transPoints.y}`
break;
case 'V':
commandStringTransformed += `L ${transPoints.x} ${transPoints.y}`
break;
case 'Z':
case 'z':
commandStringTransformed += command;
break;
default:
console.log('FOUND COMMAND NOT HANDLED BY COMMAND STRING BUILDER', command);
break;
}
transformedPath += commandStringTransformed;
}
element.setAttribute('d', transformedPath);
element.removeAttribute('transform');
break;
case 'circle':
var transformed = transform.calc(element.getAttribute('cx'), element.getAttribute('cy'));
element.setAttribute('cx', transformed[0]);
element.setAttribute('cy', transformed[1]);
// skew not supported
element.setAttribute('r', element.getAttribute('r')*scale);
break;
case 'line':
const transformedStartPt = transform.calc(element.getAttribute('x1'), element.getAttribute('y1'));
const transformedEndPt = transform.calc(element.getAttribute('x2'), element.getAttribute('y2'));
element.setAttribute('x1', transformedStartPt[0].toString());
element.setAttribute('y1', transformedStartPt[1].toString());
element.setAttribute('x2', transformedEndPt[0].toString());
element.setAttribute('y2', transformedEndPt[1].toString());
break;
case 'rect':
// similar to the ellipse, we'll replace rect with polygon
var polygon = this.svg.createElementNS(element.namespaceURI, 'polygon');
var p1 = this.svgRoot.createSVGPoint();
var p2 = this.svgRoot.createSVGPoint();
var p3 = this.svgRoot.createSVGPoint();
var p4 = this.svgRoot.createSVGPoint();
p1.x = parseFloat(element.getAttribute('x')) || 0;
p1.y = parseFloat(element.getAttribute('y')) || 0;
p2.x = p1.x + parseFloat(element.getAttribute('width'));
p2.y = p1.y;
p3.x = p2.x;
p3.y = p1.y + parseFloat(element.getAttribute('height'));
p4.x = p1.x;
p4.y = p3.y;
polygon.points.appendItem(p1);
polygon.points.appendItem(p2);
polygon.points.appendItem(p3);
polygon.points.appendItem(p4);
var transformProperty = element.getAttribute('transform');
if(transformProperty){
polygon.setAttribute('transform', transformProperty);
}
element.parentElement.replaceChild(polygon, element);
element = polygon;
case 'polygon':
case 'polyline':
let transformedPoly = ''
for(var i=0; i<element.points.numberOfItems; i++){
var point = element.points.getItem(i);
var transformed = transform.calc(point.x, point.y);
const pointPairString = `${transformed[0]},${transformed[1]} `;
transformedPoly += pointPairString;
}
element.setAttribute('points', transformedPoly);
element.removeAttribute('transform');
break;
}
if(id) {
element.setAttribute('id', id);
}
if(className){
element.setAttribute('class', className);
}
}
}
// bring all child elements to the top level
SvgParser.prototype.flatten = function(element){
for(var i=0; i<element.childNodes.length; i++){
this.flatten(element.childNodes[i]);
}
if(element.tagName != 'svg'){
while(element.childNodes.length > 0){
element.parentElement.appendChild(element.childNodes[0]);
}
}
}
// remove all elements with tag name not in the whitelist
// use this to remove <text>, <g> etc that don't represent shapes
SvgParser.prototype.filter = function(whitelist, element){
if(!whitelist || whitelist.length == 0){
throw Error('invalid whitelist');
}
element = element || this.svgRoot;
for(var i=0; i<element.childNodes.length; i++){
this.filter(whitelist, element.childNodes[i]);
}
if(element.childNodes.length == 0 && whitelist.indexOf(element.tagName) < 0){
element.parentElement.removeChild(element);
}
}
// split a compound path (paths with M, m commands) into an array of paths
SvgParser.prototype.splitPath = function(path){
if(!path || path.tagName != 'path' || !path.parentElement){
return false;
}
var seglist = [];
// make copy of seglist (appending to new path removes it from the original pathseglist)
for(var i=0; i<path.pathSegList.numberOfItems; i++){
seglist.push(path.pathSegList.getItem(i));
}
var x=0, y=0, x0=0, y0=0;
var paths = [];
var p;
var lastM = 0;
for(var i=seglist.length-1; i>=0; i--){
if(i > 0 && seglist[i].pathSegTypeAsLetter == 'M' || seglist[i].pathSegTypeAsLetter == 'm'){
lastM = i;
break;
}
}
if(lastM == 0){
return false; // only 1 M command, no need to split
}
for( i=0; i<seglist.length; i++){
var s = seglist[i];
var command = s.pathSegTypeAsLetter;
if(command == 'M' || command == 'm'){
p = path.cloneNode();
p.setAttribute('d','');
paths.push(p);
}
if (/[MLHVCSQTA]/.test(command)){
if ('x' in s) x=s.x;
if ('y' in s) y=s.y;
p.pathSegList.appendItem(s);
}
else{
if ('x' in s) x+=s.x;
if ('y' in s) y+=s.y;
if(command == 'm'){
p.pathSegList.appendItem(path.createSVGPathSegMovetoAbs(x,y));
}
else{
if(command == 'Z' || command == 'z'){
x = x0;
y = y0;
}
p.pathSegList.appendItem(s);
}
}
// Record the start of a subpath
if (command=='M' || command=='m'){
x0=x, y0=y;
}
}
var addedPaths = [];
for(i=0; i<paths.length; i++){
// don't add trivial paths from sequential M commands
if(paths[i].pathSegList.numberOfItems > 1){
path.parentElement.insertBefore(paths[i], path);
addedPaths.push(paths[i]);
}
}
path.remove();
return addedPaths;
}
// recursively run the given function on the given element
SvgParser.prototype.recurse = function(element, func){
// only operate on original DOM tree, ignore any children that are added. Avoid infinite loops
var children = Array.prototype.slice.call(element.childNodes);
for(var i=0; i<children.length; i++){
this.recurse(children[i], func);
}
func(element);
}
// return a polygon from the given SVG element in the form of an array of points
SvgParser.prototype.polygonify = function(element){
var poly = [];
var i;
switch(element.tagName){
case 'polygon':
case 'polyline':
for(i=0; i<element.points.numberOfItems; i++){
var point = element.points.getItem(i);
poly.push({ x: point.x, y: point.y });
}
break;
case 'rect':
var p1 = {};
var p2 = {};
var p3 = {};
var p4 = {};
p1.x = parseFloat(element.getAttribute('x')) || 0;
p1.y = parseFloat(element.getAttribute('y')) || 0;
p2.x = p1.x + parseFloat(element.getAttribute('width'));
p2.y = p1.y;
p3.x = p2.x;
p3.y = p1.y + parseFloat(element.getAttribute('height'));
p4.x = p1.x;
p4.y = p3.y;
poly.push(p1);
poly.push(p2);
poly.push(p3);
poly.push(p4);
break;
case 'circle':
var radius = parseFloat(element.getAttribute('r'));
var cx = parseFloat(element.getAttribute('cx'));
var cy = parseFloat(element.getAttribute('cy'));
// num is the smallest number of segments required to approximate the circle to the given tolerance
var num = Math.ceil((2*Math.PI)/Math.acos(1 - (this.conf.tolerance/radius)));
if(num < 3){
num = 3;
}
for(var i=0; i<num; i++){
var theta = i * ( (2*Math.PI) / num);
var point = {};
point.x = radius*Math.cos(theta) + cx;
point.y = radius*Math.sin(theta) + cy;
poly.push(point);
}
break;
case 'ellipse':
// same as circle case. There is probably a way to reduce points but for convenience we will just flatten the equivalent circular polygon
var rx = parseFloat(element.getAttribute('rx'))
var ry = parseFloat(element.getAttribute('ry'));
var maxradius = Math.max(rx, ry);
var cx = parseFloat(element.getAttribute('cx'));
var cy = parseFloat(element.getAttribute('cy'));
var num = Math.ceil((2*Math.PI)/Math.acos(1 - (this.conf.tolerance/maxradius)));
if(num < 3){
num = 3;
}
for(var i=0; i<num; i++){
var theta = i * ( (2*Math.PI) / num);
var point = {};
point.x = rx*Math.cos(theta) + cx;
point.y = ry*Math.sin(theta) + cy;
poly.push(point);
}
break;
case 'path':
// we'll assume that splitpath has already been run on this path, and it only has one M/m command
var seglist = element.pathSegList;
var firstCommand = seglist.getItem(0);
var lastCommand = seglist.getItem(seglist.numberOfItems-1);
var x=0, y=0, x0=0, y0=0, x1=0, y1=0, x2=0, y2=0, prevx=0, prevy=0, prevx1=0, prevy1=0, prevx2=0, prevy2=0;
for(var i=0; i<seglist.numberOfItems; i++){
var s = seglist.getItem(i);
var command = s.pathSegTypeAsLetter;
prevx = x;
prevy = y;
prevx1 = x1;
prevy1 = y1;
prevx2 = x2;
prevy2 = y2;
if (/[MLHVCSQTA]/.test(command)){
if ('x1' in s) x1=s.x1;
if ('x2' in s) x2=s.x2;
if ('y1' in s) y1=s.y1;
if ('y2' in s) y2=s.y2;
if ('x' in s) x=s.x;
if ('y' in s) y=s.y;
}
else{
if ('x1' in s) x1=x+s.x1;
if ('x2' in s) x2=x+s.x2;
if ('y1' in s) y1=y+s.y1;
if ('y2' in s) y2=y+s.y2;
if ('x' in s) x+=s.x;
if ('y' in s) y+=s.y;
}
switch(command){
// linear line types
case 'm':
case 'M':
case 'l':
case 'L':
case 'h':
case 'H':
case 'v':
case 'V':
var point = {};
point.x = x;
point.y = y;
poly.push(point);
break;
// Quadratic Beziers
case 't':
case 'T':
// implicit control point
if(i > 0 && /[QqTt]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){
x1 = prevx + (prevx-prevx1);
y1 = prevy + (prevy-prevy1);
}
else{
x1 = prevx;
y1 = prevy;
}
case 'q':
case 'Q':
var pointlist = GeometryUtil.QuadraticBezier.linearize({x: prevx, y: prevy}, {x: x, y: y}, {x: x1, y: y1}, this.conf.tolerance);
pointlist.shift(); // firstpoint would already be in the poly
for(var j=0; j<pointlist.length; j++){
var point = {};
point.x = pointlist[j].x;
point.y = pointlist[j].y;
poly.push(point);
}
break;
case 's':
case 'S':
if(i > 0 && /[CcSs]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){
x1 = prevx + (prevx-prevx2);
y1 = prevy + (prevy-prevy2);
}
else{
x1 = prevx;
y1 = prevy;
}
case 'c':
case 'C':
var pointlist = GeometryUtil.CubicBezier.linearize({x: prevx, y: prevy}, {x: x, y: y}, {x: x1, y: y1}, {x: x2, y: y2}, this.conf.tolerance);
pointlist.shift(); // firstpoint would already be in the poly
for(var j=0; j<pointlist.length; j++){
var point = {};
point.x = pointlist[j].x;
point.y = pointlist[j].y;
poly.push(point);
}
break;
case 'a':
case 'A':
var pointlist = GeometryUtil.Arc.linearize({x: prevx, y: prevy}, {x: x, y: y}, s.r1, s.r2, s.angle, s.largeArcFlag,s.sweepFlag, this.conf.tolerance);
pointlist.shift();
for(var j=0; j<pointlist.length; j++){
var point = {};
point.x = pointlist[j].x;
point.y = pointlist[j].y;
poly.push(point);
}
break;
case 'z': case 'Z': x=x0; y=y0; break;
}
// Record the start of a subpath
if (command=='M' || command=='m') x0=x, y0=y;
}
break;
}
// do not include last point if coincident with starting point
while(poly.length > 0 && GeometryUtil.almostEqual(poly[0].x,poly[poly.length-1].x, this.conf.toleranceSvg) && GeometryUtil.almostEqual(poly[0].y,poly[poly.length-1].y, this.conf.toleranceSvg)){
poly.pop();
}
return poly;
};
// expose public methods
var parser = new SvgParser();
root.SvgParser = {
config: parser.config.bind(parser),
load: parser.load.bind(parser),
getStyle: parser.getStyle.bind(parser),
clean: parser.cleanInput.bind(parser),
polygonify: parser.polygonify.bind(parser)
};
}(window));

View file

@ -0,0 +1,244 @@
// rev 452
/********************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
/*******************************************************************************
* *
* Author : Timo *
* Version : 6.1.3.2 *
* Date : 1 February 2014 *
* *
* This is a translation of the C# Clipper library to Javascript. *
* Int128 struct of C# is implemented using JSBN of Tom Wu. *
* Because Javascript lacks support for 64-bit integers, the space *
* is a little more restricted than in C# version. *
* *
* C# version has support for coordinate space: *
* +-4611686018427387903 ( sqrt(2^127 -1)/2 ) *
* while Javascript version has support for space: *
* +-4503599627370495 ( sqrt(2^106 -1)/2 ) *
* *
* Tom Wu's JSBN proved to be the fastest big integer library: *
* http://jsperf.com/big-integer-library-test *
* *
* This class can be made simpler when (if ever) 64-bit integer support comes. *
* *
*******************************************************************************/
/*******************************************************************************
* *
* Basic JavaScript BN library - subset useful for RSA encryption. *
* http://www-cs-students.stanford.edu/~tjw/jsbn/ *
* Copyright (c) 2005 Tom Wu *
* All Rights Reserved. *
* See "LICENSE" for details: *
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE *
* *
*******************************************************************************/
(function(){function k(a,b,c){d.biginteger_used=1;null!=a&&("number"==typeof a&&"undefined"==typeof b?this.fromInt(a):"number"==typeof a?this.fromNumber(a,b,c):null==b&&"string"!=typeof a?this.fromString(a,256):this.fromString(a,b))}function q(){return new k(null)}function Q(a,b,c,e,d,g){for(;0<=--g;){var h=b*this[a++]+c[e]+d;d=Math.floor(h/67108864);c[e++]=h&67108863}return d}function R(a,b,c,e,d,g){var h=b&32767;for(b>>=15;0<=--g;){var l=this[a]&32767,k=this[a++]>>15,n=b*l+k*h,l=h*l+((n&32767)<<
15)+c[e]+(d&1073741823);d=(l>>>30)+(n>>>15)+b*k+(d>>>30);c[e++]=l&1073741823}return d}function S(a,b,c,e,d,g){var h=b&16383;for(b>>=14;0<=--g;){var l=this[a]&16383,k=this[a++]>>14,n=b*l+k*h,l=h*l+((n&16383)<<14)+c[e]+d;d=(l>>28)+(n>>14)+b*k;c[e++]=l&268435455}return d}function L(a,b){var c=B[a.charCodeAt(b)];return null==c?-1:c}function v(a){var b=q();b.fromInt(a);return b}function C(a){var b=1,c;0!=(c=a>>>16)&&(a=c,b+=16);0!=(c=a>>8)&&(a=c,b+=8);0!=(c=a>>4)&&(a=c,b+=4);0!=(c=a>>2)&&(a=c,b+=2);0!=
a>>1&&(b+=1);return b}function x(a){this.m=a}function y(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<a.DB-15)-1;this.mt2=2*a.t}function T(a,b){return a&b}function I(a,b){return a|b}function M(a,b){return a^b}function N(a,b){return a&~b}function A(){}function O(a){return a}function w(a){this.r2=q();this.q3=q();k.ONE.dlShiftTo(2*a.t,this.r2);this.mu=this.r2.divide(a);this.m=a}var d={},D=!1;"undefined"!==typeof module&&module.exports?(module.exports=d,D=!0):
"undefined"!==typeof document?window.ClipperLib=d:self.ClipperLib=d;var r;if(D)p="chrome",r="Netscape";else{var p=navigator.userAgent.toString().toLowerCase();r=navigator.appName}var E,J,F,G,H,P;E=-1!=p.indexOf("chrome")&&-1==p.indexOf("chromium")?1:0;D=-1!=p.indexOf("chromium")?1:0;J=-1!=p.indexOf("safari")&&-1==p.indexOf("chrome")&&-1==p.indexOf("chromium")?1:0;F=-1!=p.indexOf("firefox")?1:0;p.indexOf("firefox/17");p.indexOf("firefox/15");p.indexOf("firefox/3");G=-1!=p.indexOf("opera")?1:0;p.indexOf("msie 10");
p.indexOf("msie 9");H=-1!=p.indexOf("msie 8")?1:0;P=-1!=p.indexOf("msie 7")?1:0;p=-1!=p.indexOf("msie ")?1:0;d.biginteger_used=null;"Microsoft Internet Explorer"==r?(k.prototype.am=R,r=30):"Netscape"!=r?(k.prototype.am=Q,r=26):(k.prototype.am=S,r=28);k.prototype.DB=r;k.prototype.DM=(1<<r)-1;k.prototype.DV=1<<r;k.prototype.FV=Math.pow(2,52);k.prototype.F1=52-r;k.prototype.F2=2*r-52;var B=[],u;r=48;for(u=0;9>=u;++u)B[r++]=u;r=97;for(u=10;36>u;++u)B[r++]=u;r=65;for(u=10;36>u;++u)B[r++]=u;x.prototype.convert=
function(a){return 0>a.s||0<=a.compareTo(this.m)?a.mod(this.m):a};x.prototype.revert=function(a){return a};x.prototype.reduce=function(a){a.divRemTo(this.m,null,a)};x.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};x.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};y.prototype.convert=function(a){var b=q();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);0>a.s&&0<b.compareTo(k.ZERO)&&this.m.subTo(b,b);return b};y.prototype.revert=function(a){var b=q();a.copyTo(b);
this.reduce(b);return b};y.prototype.reduce=function(a){for(;a.t<=this.mt2;)a[a.t++]=0;for(var b=0;b<this.m.t;++b){var c=a[b]&32767,e=c*this.mpl+((c*this.mph+(a[b]>>15)*this.mpl&this.um)<<15)&a.DM,c=b+this.m.t;for(a[c]+=this.m.am(0,e,a,b,0,this.m.t);a[c]>=a.DV;)a[c]-=a.DV,a[++c]++}a.clamp();a.drShiftTo(this.m.t,a);0<=a.compareTo(this.m)&&a.subTo(this.m,a)};y.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};y.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};k.prototype.copyTo=
function(a){for(var b=this.t-1;0<=b;--b)a[b]=this[b];a.t=this.t;a.s=this.s};k.prototype.fromInt=function(a){this.t=1;this.s=0>a?-1:0;0<a?this[0]=a:-1>a?this[0]=a+this.DV:this.t=0};k.prototype.fromString=function(a,b){var c;if(16==b)c=4;else if(8==b)c=3;else if(256==b)c=8;else if(2==b)c=1;else if(32==b)c=5;else if(4==b)c=2;else{this.fromRadix(a,b);return}this.s=this.t=0;for(var e=a.length,d=!1,g=0;0<=--e;){var h=8==c?a[e]&255:L(a,e);0>h?"-"==a.charAt(e)&&(d=!0):(d=!1,0==g?this[this.t++]=h:g+c>this.DB?
(this[this.t-1]|=(h&(1<<this.DB-g)-1)<<g,this[this.t++]=h>>this.DB-g):this[this.t-1]|=h<<g,g+=c,g>=this.DB&&(g-=this.DB))}8==c&&0!=(a[0]&128)&&(this.s=-1,0<g&&(this[this.t-1]|=(1<<this.DB-g)-1<<g));this.clamp();d&&k.ZERO.subTo(this,this)};k.prototype.clamp=function(){for(var a=this.s&this.DM;0<this.t&&this[this.t-1]==a;)--this.t};k.prototype.dlShiftTo=function(a,b){var c;for(c=this.t-1;0<=c;--c)b[c+a]=this[c];for(c=a-1;0<=c;--c)b[c]=0;b.t=this.t+a;b.s=this.s};k.prototype.drShiftTo=function(a,b){for(var c=
a;c<this.t;++c)b[c-a]=this[c];b.t=Math.max(this.t-a,0);b.s=this.s};k.prototype.lShiftTo=function(a,b){var c=a%this.DB,e=this.DB-c,d=(1<<e)-1,g=Math.floor(a/this.DB),h=this.s<<c&this.DM,l;for(l=this.t-1;0<=l;--l)b[l+g+1]=this[l]>>e|h,h=(this[l]&d)<<c;for(l=g-1;0<=l;--l)b[l]=0;b[g]=h;b.t=this.t+g+1;b.s=this.s;b.clamp()};k.prototype.rShiftTo=function(a,b){b.s=this.s;var c=Math.floor(a/this.DB);if(c>=this.t)b.t=0;else{var e=a%this.DB,d=this.DB-e,g=(1<<e)-1;b[0]=this[c]>>e;for(var h=c+1;h<this.t;++h)b[h-
c-1]|=(this[h]&g)<<d,b[h-c]=this[h]>>e;0<e&&(b[this.t-c-1]|=(this.s&g)<<d);b.t=this.t-c;b.clamp()}};k.prototype.subTo=function(a,b){for(var c=0,e=0,d=Math.min(a.t,this.t);c<d;)e+=this[c]-a[c],b[c++]=e&this.DM,e>>=this.DB;if(a.t<this.t){for(e-=a.s;c<this.t;)e+=this[c],b[c++]=e&this.DM,e>>=this.DB;e+=this.s}else{for(e+=this.s;c<a.t;)e-=a[c],b[c++]=e&this.DM,e>>=this.DB;e-=a.s}b.s=0>e?-1:0;-1>e?b[c++]=this.DV+e:0<e&&(b[c++]=e);b.t=c;b.clamp()};k.prototype.multiplyTo=function(a,b){var c=this.abs(),e=
a.abs(),d=c.t;for(b.t=d+e.t;0<=--d;)b[d]=0;for(d=0;d<e.t;++d)b[d+c.t]=c.am(0,e[d],b,d,0,c.t);b.s=0;b.clamp();this.s!=a.s&&k.ZERO.subTo(b,b)};k.prototype.squareTo=function(a){for(var b=this.abs(),c=a.t=2*b.t;0<=--c;)a[c]=0;for(c=0;c<b.t-1;++c){var e=b.am(c,b[c],a,2*c,0,1);(a[c+b.t]+=b.am(c+1,2*b[c],a,2*c+1,e,b.t-c-1))>=b.DV&&(a[c+b.t]-=b.DV,a[c+b.t+1]=1)}0<a.t&&(a[a.t-1]+=b.am(c,b[c],a,2*c,0,1));a.s=0;a.clamp()};k.prototype.divRemTo=function(a,b,c){var e=a.abs();if(!(0>=e.t)){var d=this.abs();if(d.t<
e.t)null!=b&&b.fromInt(0),null!=c&&this.copyTo(c);else{null==c&&(c=q());var g=q(),h=this.s;a=a.s;var l=this.DB-C(e[e.t-1]);0<l?(e.lShiftTo(l,g),d.lShiftTo(l,c)):(e.copyTo(g),d.copyTo(c));e=g.t;d=g[e-1];if(0!=d){var z=d*(1<<this.F1)+(1<e?g[e-2]>>this.F2:0),n=this.FV/z,z=(1<<this.F1)/z,U=1<<this.F2,m=c.t,p=m-e,s=null==b?q():b;g.dlShiftTo(p,s);0<=c.compareTo(s)&&(c[c.t++]=1,c.subTo(s,c));k.ONE.dlShiftTo(e,s);for(s.subTo(g,g);g.t<e;)g[g.t++]=0;for(;0<=--p;){var r=c[--m]==d?this.DM:Math.floor(c[m]*n+(c[m-
1]+U)*z);if((c[m]+=g.am(0,r,c,p,0,e))<r)for(g.dlShiftTo(p,s),c.subTo(s,c);c[m]<--r;)c.subTo(s,c)}null!=b&&(c.drShiftTo(e,b),h!=a&&k.ZERO.subTo(b,b));c.t=e;c.clamp();0<l&&c.rShiftTo(l,c);0>h&&k.ZERO.subTo(c,c)}}}};k.prototype.invDigit=function(){if(1>this.t)return 0;var a=this[0];if(0==(a&1))return 0;var b=a&3,b=b*(2-(a&15)*b)&15,b=b*(2-(a&255)*b)&255,b=b*(2-((a&65535)*b&65535))&65535,b=b*(2-a*b%this.DV)%this.DV;return 0<b?this.DV-b:-b};k.prototype.isEven=function(){return 0==(0<this.t?this[0]&1:this.s)};
k.prototype.exp=function(a,b){if(4294967295<a||1>a)return k.ONE;var c=q(),e=q(),d=b.convert(this),g=C(a)-1;for(d.copyTo(c);0<=--g;)if(b.sqrTo(c,e),0<(a&1<<g))b.mulTo(e,d,c);else var h=c,c=e,e=h;return b.revert(c)};k.prototype.toString=function(a){if(0>this.s)return"-"+this.negate().toString(a);if(16==a)a=4;else if(8==a)a=3;else if(2==a)a=1;else if(32==a)a=5;else if(4==a)a=2;else return this.toRadix(a);var b=(1<<a)-1,c,e=!1,d="",g=this.t,h=this.DB-g*this.DB%a;if(0<g--)for(h<this.DB&&0<(c=this[g]>>
h)&&(e=!0,d="0123456789abcdefghijklmnopqrstuvwxyz".charAt(c));0<=g;)h<a?(c=(this[g]&(1<<h)-1)<<a-h,c|=this[--g]>>(h+=this.DB-a)):(c=this[g]>>(h-=a)&b,0>=h&&(h+=this.DB,--g)),0<c&&(e=!0),e&&(d+="0123456789abcdefghijklmnopqrstuvwxyz".charAt(c));return e?d:"0"};k.prototype.negate=function(){var a=q();k.ZERO.subTo(this,a);return a};k.prototype.abs=function(){return 0>this.s?this.negate():this};k.prototype.compareTo=function(a){var b=this.s-a.s;if(0!=b)return b;var c=this.t,b=c-a.t;if(0!=b)return 0>this.s?
-b:b;for(;0<=--c;)if(0!=(b=this[c]-a[c]))return b;return 0};k.prototype.bitLength=function(){return 0>=this.t?0:this.DB*(this.t-1)+C(this[this.t-1]^this.s&this.DM)};k.prototype.mod=function(a){var b=q();this.abs().divRemTo(a,null,b);0>this.s&&0<b.compareTo(k.ZERO)&&a.subTo(b,b);return b};k.prototype.modPowInt=function(a,b){var c;c=256>a||b.isEven()?new x(b):new y(b);return this.exp(a,c)};k.ZERO=v(0);k.ONE=v(1);A.prototype.convert=O;A.prototype.revert=O;A.prototype.mulTo=function(a,b,c){a.multiplyTo(b,
c)};A.prototype.sqrTo=function(a,b){a.squareTo(b)};w.prototype.convert=function(a){if(0>a.s||a.t>2*this.m.t)return a.mod(this.m);if(0>a.compareTo(this.m))return a;var b=q();a.copyTo(b);this.reduce(b);return b};w.prototype.revert=function(a){return a};w.prototype.reduce=function(a){a.drShiftTo(this.m.t-1,this.r2);a.t>this.m.t+1&&(a.t=this.m.t+1,a.clamp());this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);for(this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);0>a.compareTo(this.r2);)a.dAddOffset(1,
this.m.t+1);for(a.subTo(this.r2,a);0<=a.compareTo(this.m);)a.subTo(this.m,a)};w.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};w.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};var t=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,
409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],V=67108864/t[t.length-1];k.prototype.chunkSize=function(a){return Math.floor(Math.LN2*this.DB/Math.log(a))};k.prototype.toRadix=function(a){null==
a&&(a=10);if(0==this.signum()||2>a||36<a)return"0";var b=this.chunkSize(a),b=Math.pow(a,b),c=v(b),e=q(),d=q(),g="";for(this.divRemTo(c,e,d);0<e.signum();)g=(b+d.intValue()).toString(a).substr(1)+g,e.divRemTo(c,e,d);return d.intValue().toString(a)+g};k.prototype.fromRadix=function(a,b){this.fromInt(0);null==b&&(b=10);for(var c=this.chunkSize(b),e=Math.pow(b,c),d=!1,g=0,h=0,l=0;l<a.length;++l){var z=L(a,l);0>z?"-"==a.charAt(l)&&0==this.signum()&&(d=!0):(h=b*h+z,++g>=c&&(this.dMultiply(e),this.dAddOffset(h,
0),h=g=0))}0<g&&(this.dMultiply(Math.pow(b,g)),this.dAddOffset(h,0));d&&k.ZERO.subTo(this,this)};k.prototype.fromNumber=function(a,b,c){if("number"==typeof b)if(2>a)this.fromInt(1);else for(this.fromNumber(a,c),this.testBit(a-1)||this.bitwiseTo(k.ONE.shiftLeft(a-1),I,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(k.ONE.shiftLeft(a-1),this);else{c=[];var e=a&7;c.length=(a>>3)+1;b.nextBytes(c);c[0]=0<e?c[0]&(1<<e)-1:0;this.fromString(c,
256)}};k.prototype.bitwiseTo=function(a,b,c){var e,d,g=Math.min(a.t,this.t);for(e=0;e<g;++e)c[e]=b(this[e],a[e]);if(a.t<this.t){d=a.s&this.DM;for(e=g;e<this.t;++e)c[e]=b(this[e],d);c.t=this.t}else{d=this.s&this.DM;for(e=g;e<a.t;++e)c[e]=b(d,a[e]);c.t=a.t}c.s=b(this.s,a.s);c.clamp()};k.prototype.changeBit=function(a,b){var c=k.ONE.shiftLeft(a);this.bitwiseTo(c,b,c);return c};k.prototype.addTo=function(a,b){for(var c=0,e=0,d=Math.min(a.t,this.t);c<d;)e+=this[c]+a[c],b[c++]=e&this.DM,e>>=this.DB;if(a.t<
this.t){for(e+=a.s;c<this.t;)e+=this[c],b[c++]=e&this.DM,e>>=this.DB;e+=this.s}else{for(e+=this.s;c<a.t;)e+=a[c],b[c++]=e&this.DM,e>>=this.DB;e+=a.s}b.s=0>e?-1:0;0<e?b[c++]=e:-1>e&&(b[c++]=this.DV+e);b.t=c;b.clamp()};k.prototype.dMultiply=function(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()};k.prototype.dAddOffset=function(a,b){if(0!=a){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}};k.prototype.multiplyLowerTo=
function(a,b,c){var e=Math.min(this.t+a.t,b);c.s=0;for(c.t=e;0<e;)c[--e]=0;var d;for(d=c.t-this.t;e<d;++e)c[e+this.t]=this.am(0,a[e],c,e,0,this.t);for(d=Math.min(a.t,b);e<d;++e)this.am(0,a[e],c,e,0,b-e);c.clamp()};k.prototype.multiplyUpperTo=function(a,b,c){--b;var e=c.t=this.t+a.t-b;for(c.s=0;0<=--e;)c[e]=0;for(e=Math.max(b-this.t,0);e<a.t;++e)c[this.t+e-b]=this.am(b-e,a[e],c,0,0,this.t+e-b);c.clamp();c.drShiftTo(1,c)};k.prototype.modInt=function(a){if(0>=a)return 0;var b=this.DV%a,c=0>this.s?a-
1:0;if(0<this.t)if(0==b)c=this[0]%a;else for(var e=this.t-1;0<=e;--e)c=(b*c+this[e])%a;return c};k.prototype.millerRabin=function(a){var b=this.subtract(k.ONE),c=b.getLowestSetBit();if(0>=c)return!1;var e=b.shiftRight(c);a=a+1>>1;a>t.length&&(a=t.length);for(var d=q(),g=0;g<a;++g){d.fromInt(t[Math.floor(Math.random()*t.length)]);var h=d.modPow(e,this);if(0!=h.compareTo(k.ONE)&&0!=h.compareTo(b)){for(var l=1;l++<c&&0!=h.compareTo(b);)if(h=h.modPowInt(2,this),0==h.compareTo(k.ONE))return!1;if(0!=h.compareTo(b))return!1}}return!0};
k.prototype.clone=function(){var a=q();this.copyTo(a);return a};k.prototype.intValue=function(){if(0>this.s){if(1==this.t)return this[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this[0];if(0==this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]};k.prototype.byteValue=function(){return 0==this.t?this.s:this[0]<<24>>24};k.prototype.shortValue=function(){return 0==this.t?this.s:this[0]<<16>>16};k.prototype.signum=function(){return 0>this.s?-1:0>=this.t||1==this.t&&0>=this[0]?
0:1};k.prototype.toByteArray=function(){var a=this.t,b=[];b[0]=this.s;var c=this.DB-a*this.DB%8,e,d=0;if(0<a--)for(c<this.DB&&(e=this[a]>>c)!=(this.s&this.DM)>>c&&(b[d++]=e|this.s<<this.DB-c);0<=a;)if(8>c?(e=(this[a]&(1<<c)-1)<<8-c,e|=this[--a]>>(c+=this.DB-8)):(e=this[a]>>(c-=8)&255,0>=c&&(c+=this.DB,--a)),0!=(e&128)&&(e|=-256),0==d&&(this.s&128)!=(e&128)&&++d,0<d||e!=this.s)b[d++]=e;return b};k.prototype.equals=function(a){return 0==this.compareTo(a)};k.prototype.min=function(a){return 0>this.compareTo(a)?
this:a};k.prototype.max=function(a){return 0<this.compareTo(a)?this:a};k.prototype.and=function(a){var b=q();this.bitwiseTo(a,T,b);return b};k.prototype.or=function(a){var b=q();this.bitwiseTo(a,I,b);return b};k.prototype.xor=function(a){var b=q();this.bitwiseTo(a,M,b);return b};k.prototype.andNot=function(a){var b=q();this.bitwiseTo(a,N,b);return b};k.prototype.not=function(){for(var a=q(),b=0;b<this.t;++b)a[b]=this.DM&~this[b];a.t=this.t;a.s=~this.s;return a};k.prototype.shiftLeft=function(a){var b=
q();0>a?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b};k.prototype.shiftRight=function(a){var b=q();0>a?this.lShiftTo(-a,b):this.rShiftTo(a,b);return b};k.prototype.getLowestSetBit=function(){for(var a=0;a<this.t;++a)if(0!=this[a]){var b=a*this.DB;a=this[a];if(0==a)a=-1;else{var c=0;0==(a&65535)&&(a>>=16,c+=16);0==(a&255)&&(a>>=8,c+=8);0==(a&15)&&(a>>=4,c+=4);0==(a&3)&&(a>>=2,c+=2);0==(a&1)&&++c;a=c}return b+a}return 0>this.s?this.t*this.DB:-1};k.prototype.bitCount=function(){for(var a=0,b=this.s&
this.DM,c=0;c<this.t;++c){for(var e=this[c]^b,d=0;0!=e;)e&=e-1,++d;a+=d}return a};k.prototype.testBit=function(a){var b=Math.floor(a/this.DB);return b>=this.t?0!=this.s:0!=(this[b]&1<<a%this.DB)};k.prototype.setBit=function(a){return this.changeBit(a,I)};k.prototype.clearBit=function(a){return this.changeBit(a,N)};k.prototype.flipBit=function(a){return this.changeBit(a,M)};k.prototype.add=function(a){var b=q();this.addTo(a,b);return b};k.prototype.subtract=function(a){var b=q();this.subTo(a,b);return b};
k.prototype.multiply=function(a){var b=q();this.multiplyTo(a,b);return b};k.prototype.divide=function(a){var b=q();this.divRemTo(a,b,null);return b};k.prototype.remainder=function(a){var b=q();this.divRemTo(a,null,b);return b};k.prototype.divideAndRemainder=function(a){var b=q(),c=q();this.divRemTo(a,b,c);return[b,c]};k.prototype.modPow=function(a,b){var c=a.bitLength(),e,d=v(1),g;if(0>=c)return d;e=18>c?1:48>c?3:144>c?4:768>c?5:6;g=8>c?new x(b):b.isEven()?new w(b):new y(b);var h=[],l=3,k=e-1,n=(1<<
e)-1;h[1]=g.convert(this);if(1<e)for(c=q(),g.sqrTo(h[1],c);l<=n;)h[l]=q(),g.mulTo(c,h[l-2],h[l]),l+=2;for(var m=a.t-1,p,r=!0,s=q(),c=C(a[m])-1;0<=m;){c>=k?p=a[m]>>c-k&n:(p=(a[m]&(1<<c+1)-1)<<k-c,0<m&&(p|=a[m-1]>>this.DB+c-k));for(l=e;0==(p&1);)p>>=1,--l;0>(c-=l)&&(c+=this.DB,--m);if(r)h[p].copyTo(d),r=!1;else{for(;1<l;)g.sqrTo(d,s),g.sqrTo(s,d),l-=2;0<l?g.sqrTo(d,s):(l=d,d=s,s=l);g.mulTo(s,h[p],d)}for(;0<=m&&0==(a[m]&1<<c);)g.sqrTo(d,s),l=d,d=s,s=l,0>--c&&(c=this.DB-1,--m)}return g.revert(d)};k.prototype.modInverse=
function(a){var b=a.isEven();if(this.isEven()&&b||0==a.signum())return k.ZERO;for(var c=a.clone(),e=this.clone(),d=v(1),g=v(0),h=v(0),l=v(1);0!=c.signum();){for(;c.isEven();)c.rShiftTo(1,c),b?(d.isEven()&&g.isEven()||(d.addTo(this,d),g.subTo(a,g)),d.rShiftTo(1,d)):g.isEven()||g.subTo(a,g),g.rShiftTo(1,g);for(;e.isEven();)e.rShiftTo(1,e),b?(h.isEven()&&l.isEven()||(h.addTo(this,h),l.subTo(a,l)),h.rShiftTo(1,h)):l.isEven()||l.subTo(a,l),l.rShiftTo(1,l);0<=c.compareTo(e)?(c.subTo(e,c),b&&d.subTo(h,d),
g.subTo(l,g)):(e.subTo(c,e),b&&h.subTo(d,h),l.subTo(g,l))}if(0!=e.compareTo(k.ONE))return k.ZERO;if(0<=l.compareTo(a))return l.subtract(a);if(0>l.signum())l.addTo(a,l);else return l;return 0>l.signum()?l.add(a):l};k.prototype.pow=function(a){return this.exp(a,new A)};k.prototype.gcd=function(a){var b=0>this.s?this.negate():this.clone();a=0>a.s?a.negate():a.clone();if(0>b.compareTo(a)){var c=b,b=a;a=c}var c=b.getLowestSetBit(),e=a.getLowestSetBit();if(0>e)return b;c<e&&(e=c);0<e&&(b.rShiftTo(e,b),
a.rShiftTo(e,a));for(;0<b.signum();)0<(c=b.getLowestSetBit())&&b.rShiftTo(c,b),0<(c=a.getLowestSetBit())&&a.rShiftTo(c,a),0<=b.compareTo(a)?(b.subTo(a,b),b.rShiftTo(1,b)):(a.subTo(b,a),a.rShiftTo(1,a));0<e&&a.lShiftTo(e,a);return a};k.prototype.isProbablePrime=function(a){var b,c=this.abs();if(1==c.t&&c[0]<=t[t.length-1]){for(b=0;b<t.length;++b)if(c[0]==t[b])return!0;return!1}if(c.isEven())return!1;for(b=1;b<t.length;){for(var e=t[b],d=b+1;d<t.length&&e<V;)e*=t[d++];for(e=c.modInt(e);b<d;)if(0==e%
t[b++])return!1}return c.millerRabin(a)};k.prototype.square=function(){var a=q();this.squareTo(a);return a};var m=k;m.prototype.IsNegative=function(){return-1==this.compareTo(m.ZERO)?!0:!1};m.op_Equality=function(a,b){return 0==a.compareTo(b)?!0:!1};m.op_Inequality=function(a,b){return 0!=a.compareTo(b)?!0:!1};m.op_GreaterThan=function(a,b){return 0<a.compareTo(b)?!0:!1};m.op_LessThan=function(a,b){return 0>a.compareTo(b)?!0:!1};m.op_Addition=function(a,b){return(new m(a)).add(new m(b))};m.op_Subtraction=
function(a,b){return(new m(a)).subtract(new m(b))};m.Int128Mul=function(a,b){return(new m(a)).multiply(new m(b))};m.op_Division=function(a,b){return a.divide(b)};m.prototype.ToDouble=function(){return parseFloat(this.toString())};if("undefined"==typeof K)var K=function(a,b){var c;if("undefined"==typeof Object.getOwnPropertyNames)for(c in b.prototype){if("undefined"==typeof a.prototype[c]||a.prototype[c]==Object.prototype[c])a.prototype[c]=b.prototype[c]}else for(var e=Object.getOwnPropertyNames(b.prototype),
d=0;d<e.length;d++)"undefined"==typeof Object.getOwnPropertyDescriptor(a.prototype,e[d])&&Object.defineProperty(a.prototype,e[d],Object.getOwnPropertyDescriptor(b.prototype,e[d]));for(c in b)"undefined"==typeof a[c]&&(a[c]=b[c]);a.$baseCtor=b};d.Path=function(){return[]};d.Paths=function(){return[]};d.DoublePoint=function(){var a=arguments;this.Y=this.X=0;1==a.length?(this.X=a[0].X,this.Y=a[0].Y):2==a.length&&(this.X=a[0],this.Y=a[1])};d.DoublePoint0=function(){this.Y=this.X=0};d.DoublePoint1=function(a){this.X=
a.X;this.Y=a.Y};d.DoublePoint2=function(a,b){this.X=a;this.Y=b};d.PolyNode=function(){this.m_Parent=null;this.m_polygon=new d.Path;this.m_endtype=this.m_jointype=this.m_Index=0;this.m_Childs=[];this.IsOpen=!1};d.PolyNode.prototype.IsHoleNode=function(){for(var a=!0,b=this.m_Parent;null!==b;)a=!a,b=b.m_Parent;return a};d.PolyNode.prototype.ChildCount=function(){return this.m_Childs.length};d.PolyNode.prototype.Contour=function(){return this.m_polygon};d.PolyNode.prototype.AddChild=function(a){var b=
this.m_Childs.length;this.m_Childs.push(a);a.m_Parent=this;a.m_Index=b};d.PolyNode.prototype.GetNext=function(){return 0<this.m_Childs.length?this.m_Childs[0]:this.GetNextSiblingUp()};d.PolyNode.prototype.GetNextSiblingUp=function(){return null===this.m_Parent?null:this.m_Index==this.m_Parent.m_Childs.length-1?this.m_Parent.GetNextSiblingUp():this.m_Parent.m_Childs[this.m_Index+1]};d.PolyNode.prototype.Childs=function(){return this.m_Childs};d.PolyNode.prototype.Parent=function(){return this.m_Parent};
d.PolyNode.prototype.IsHole=function(){return this.IsHoleNode()};d.PolyTree=function(){this.m_AllPolys=[];d.PolyNode.call(this)};d.PolyTree.prototype.Clear=function(){for(var a=0,b=this.m_AllPolys.length;a<b;a++)this.m_AllPolys[a]=null;this.m_AllPolys.length=0;this.m_Childs.length=0};d.PolyTree.prototype.GetFirst=function(){return 0<this.m_Childs.length?this.m_Childs[0]:null};d.PolyTree.prototype.Total=function(){return this.m_AllPolys.length};K(d.PolyTree,d.PolyNode);d.Math_Abs_Int64=d.Math_Abs_Int32=
d.Math_Abs_Double=function(a){return Math.abs(a)};d.Math_Max_Int32_Int32=function(a,b){return Math.max(a,b)};d.Cast_Int32=p||G||J?function(a){return a|0}:function(a){return~~a};d.Cast_Int64=E?function(a){return-2147483648>a||2147483647<a?0>a?Math.ceil(a):Math.floor(a):~~a}:F&&"function"==typeof Number.toInteger?function(a){return Number.toInteger(a)}:P||H?function(a){return parseInt(a,10)}:p?function(a){return-2147483648>a||2147483647<a?0>a?Math.ceil(a):Math.floor(a):a|0}:function(a){return 0>a?Math.ceil(a):
Math.floor(a)};d.Clear=function(a){a.length=0};d.PI=3.141592653589793;d.PI2=6.283185307179586;d.IntPoint=function(){var a;a=arguments;var b=a.length;this.Y=this.X=0;2==b?(this.X=a[0],this.Y=a[1]):1==b?a[0]instanceof d.DoublePoint?(a=a[0],this.X=d.Clipper.Round(a.X),this.Y=d.Clipper.Round(a.Y)):(a=a[0],this.X=a.X,this.Y=a.Y):this.Y=this.X=0};d.IntPoint.op_Equality=function(a,b){return a.X==b.X&&a.Y==b.Y};d.IntPoint.op_Inequality=function(a,b){return a.X!=b.X||a.Y!=b.Y};d.IntPoint0=function(){this.Y=
this.X=0};d.IntPoint1=function(a){this.X=a.X;this.Y=a.Y};d.IntPoint1dp=function(a){this.X=d.Clipper.Round(a.X);this.Y=d.Clipper.Round(a.Y)};d.IntPoint2=function(a,b){this.X=a;this.Y=b};d.IntRect=function(){var a=arguments,b=a.length;4==b?(this.left=a[0],this.top=a[1],this.right=a[2],this.bottom=a[3]):1==b?(this.left=ir.left,this.top=ir.top,this.right=ir.right,this.bottom=ir.bottom):this.bottom=this.right=this.top=this.left=0};d.IntRect0=function(){this.bottom=this.right=this.top=this.left=0};d.IntRect1=
function(a){this.left=a.left;this.top=a.top;this.right=a.right;this.bottom=a.bottom};d.IntRect4=function(a,b,c,e){this.left=a;this.top=b;this.right=c;this.bottom=e};d.ClipType={ctIntersection:0,ctUnion:1,ctDifference:2,ctXor:3};d.PolyType={ptSubject:0,ptClip:1};d.PolyFillType={pftEvenOdd:0,pftNonZero:1,pftPositive:2,pftNegative:3};d.JoinType={jtSquare:0,jtRound:1,jtMiter:2};d.EndType={etOpenSquare:0,etOpenRound:1,etOpenButt:2,etClosedLine:3,etClosedPolygon:4};d.EdgeSide={esLeft:0,esRight:1};d.Direction=
{dRightToLeft:0,dLeftToRight:1};d.TEdge=function(){this.Bot=new d.IntPoint;this.Curr=new d.IntPoint;this.Top=new d.IntPoint;this.Delta=new d.IntPoint;this.Dx=0;this.PolyTyp=d.PolyType.ptSubject;this.Side=d.EdgeSide.esLeft;this.OutIdx=this.WindCnt2=this.WindCnt=this.WindDelta=0;this.PrevInSEL=this.NextInSEL=this.PrevInAEL=this.NextInAEL=this.NextInLML=this.Prev=this.Next=null};d.IntersectNode=function(){this.Edge2=this.Edge1=null;this.Pt=new d.IntPoint};d.MyIntersectNodeSort=function(){};d.MyIntersectNodeSort.Compare=
function(a,b){return b.Pt.Y-a.Pt.Y};d.LocalMinima=function(){this.Y=0;this.Next=this.RightBound=this.LeftBound=null};d.Scanbeam=function(){this.Y=0;this.Next=null};d.OutRec=function(){this.Idx=0;this.IsOpen=this.IsHole=!1;this.PolyNode=this.BottomPt=this.Pts=this.FirstLeft=null};d.OutPt=function(){this.Idx=0;this.Pt=new d.IntPoint;this.Prev=this.Next=null};d.Join=function(){this.OutPt2=this.OutPt1=null;this.OffPt=new d.IntPoint};d.ClipperBase=function(){this.m_CurrentLM=this.m_MinimaList=null;this.m_edges=
[];this.PreserveCollinear=this.m_HasOpenPaths=this.m_UseFullRange=!1;this.m_CurrentLM=this.m_MinimaList=null;this.m_HasOpenPaths=this.m_UseFullRange=!1};d.ClipperBase.horizontal=-9007199254740992;d.ClipperBase.Skip=-2;d.ClipperBase.Unassigned=-1;d.ClipperBase.tolerance=1E-20;d.ClipperBase.loRange=47453132;d.ClipperBase.hiRange=0xfffffffffffff;d.ClipperBase.near_zero=function(a){return a>-d.ClipperBase.tolerance&&a<d.ClipperBase.tolerance};d.ClipperBase.IsHorizontal=function(a){return 0===a.Delta.Y};
d.ClipperBase.prototype.PointIsVertex=function(a,b){var c=b;do{if(d.IntPoint.op_Equality(c.Pt,a))return!0;c=c.Next}while(c!=b);return!1};d.ClipperBase.prototype.PointOnLineSegment=function(a,b,c,e){return e?a.X==b.X&&a.Y==b.Y||a.X==c.X&&a.Y==c.Y||a.X>b.X==a.X<c.X&&a.Y>b.Y==a.Y<c.Y&&m.op_Equality(m.Int128Mul(a.X-b.X,c.Y-b.Y),m.Int128Mul(c.X-b.X,a.Y-b.Y)):a.X==b.X&&a.Y==b.Y||a.X==c.X&&a.Y==c.Y||a.X>b.X==a.X<c.X&&a.Y>b.Y==a.Y<c.Y&&(a.X-b.X)*(c.Y-b.Y)==(c.X-b.X)*(a.Y-b.Y)};d.ClipperBase.prototype.PointOnPolygon=
function(a,b,c){for(var e=b;;){if(this.PointOnLineSegment(a,e.Pt,e.Next.Pt,c))return!0;e=e.Next;if(e==b)break}return!1};d.ClipperBase.prototype.SlopesEqual=d.ClipperBase.SlopesEqual=function(){var a=arguments,b=a.length,c,e,f;if(3==b)return b=a[0],c=a[1],(a=a[2])?m.op_Equality(m.Int128Mul(b.Delta.Y,c.Delta.X),m.Int128Mul(b.Delta.X,c.Delta.Y)):d.Cast_Int64(b.Delta.Y*c.Delta.X)==d.Cast_Int64(b.Delta.X*c.Delta.Y);if(4==b)return b=a[0],c=a[1],e=a[2],(a=a[3])?m.op_Equality(m.Int128Mul(b.Y-c.Y,c.X-e.X),
m.Int128Mul(b.X-c.X,c.Y-e.Y)):0===d.Cast_Int64((b.Y-c.Y)*(c.X-e.X))-d.Cast_Int64((b.X-c.X)*(c.Y-e.Y));b=a[0];c=a[1];e=a[2];f=a[3];return(a=a[4])?m.op_Equality(m.Int128Mul(b.Y-c.Y,e.X-f.X),m.Int128Mul(b.X-c.X,e.Y-f.Y)):0===d.Cast_Int64((b.Y-c.Y)*(e.X-f.X))-d.Cast_Int64((b.X-c.X)*(e.Y-f.Y))};d.ClipperBase.SlopesEqual3=function(a,b,c){return c?m.op_Equality(m.Int128Mul(a.Delta.Y,b.Delta.X),m.Int128Mul(a.Delta.X,b.Delta.Y)):d.Cast_Int64(a.Delta.Y*b.Delta.X)==d.Cast_Int64(a.Delta.X*b.Delta.Y)};d.ClipperBase.SlopesEqual4=
function(a,b,c,e){return e?m.op_Equality(m.Int128Mul(a.Y-b.Y,b.X-c.X),m.Int128Mul(a.X-b.X,b.Y-c.Y)):0===d.Cast_Int64((a.Y-b.Y)*(b.X-c.X))-d.Cast_Int64((a.X-b.X)*(b.Y-c.Y))};d.ClipperBase.SlopesEqual5=function(a,b,c,e,f){return f?m.op_Equality(m.Int128Mul(a.Y-b.Y,c.X-e.X),m.Int128Mul(a.X-b.X,c.Y-e.Y)):0===d.Cast_Int64((a.Y-b.Y)*(c.X-e.X))-d.Cast_Int64((a.X-b.X)*(c.Y-e.Y))};d.ClipperBase.prototype.Clear=function(){this.DisposeLocalMinimaList();for(var a=0,b=this.m_edges.length;a<b;++a){for(var c=0,
e=this.m_edges[a].length;c<e;++c)this.m_edges[a][c]=null;d.Clear(this.m_edges[a])}d.Clear(this.m_edges);this.m_HasOpenPaths=this.m_UseFullRange=!1};d.ClipperBase.prototype.DisposeLocalMinimaList=function(){for(;null!==this.m_MinimaList;){var a=this.m_MinimaList.Next;this.m_MinimaList=null;this.m_MinimaList=a}this.m_CurrentLM=null};d.ClipperBase.prototype.RangeTest=function(a,b){if(b.Value)(a.X>d.ClipperBase.hiRange||a.Y>d.ClipperBase.hiRange||-a.X>d.ClipperBase.hiRange||-a.Y>d.ClipperBase.hiRange)&&
d.Error("Coordinate outside allowed range in RangeTest().");else if(a.X>d.ClipperBase.loRange||a.Y>d.ClipperBase.loRange||-a.X>d.ClipperBase.loRange||-a.Y>d.ClipperBase.loRange)b.Value=!0,this.RangeTest(a,b)};d.ClipperBase.prototype.InitEdge=function(a,b,c,e){a.Next=b;a.Prev=c;a.Curr.X=e.X;a.Curr.Y=e.Y;a.OutIdx=-1};d.ClipperBase.prototype.InitEdge2=function(a,b){a.Curr.Y>=a.Next.Curr.Y?(a.Bot.X=a.Curr.X,a.Bot.Y=a.Curr.Y,a.Top.X=a.Next.Curr.X,a.Top.Y=a.Next.Curr.Y):(a.Top.X=a.Curr.X,a.Top.Y=a.Curr.Y,
a.Bot.X=a.Next.Curr.X,a.Bot.Y=a.Next.Curr.Y);this.SetDx(a);a.PolyTyp=b};d.ClipperBase.prototype.FindNextLocMin=function(a){for(var b;;){for(;d.IntPoint.op_Inequality(a.Bot,a.Prev.Bot)||d.IntPoint.op_Equality(a.Curr,a.Top);)a=a.Next;if(a.Dx!=d.ClipperBase.horizontal&&a.Prev.Dx!=d.ClipperBase.horizontal)break;for(;a.Prev.Dx==d.ClipperBase.horizontal;)a=a.Prev;for(b=a;a.Dx==d.ClipperBase.horizontal;)a=a.Next;if(a.Top.Y!=a.Prev.Bot.Y){b.Prev.Bot.X<a.Bot.X&&(a=b);break}}return a};d.ClipperBase.prototype.ProcessBound=
function(a,b){var c=a,e=a,f;a.Dx==d.ClipperBase.horizontal&&(f=b?a.Prev.Bot.X:a.Next.Bot.X,a.Bot.X!=f&&this.ReverseHorizontal(a));if(e.OutIdx!=d.ClipperBase.Skip)if(b){for(;e.Top.Y==e.Next.Bot.Y&&e.Next.OutIdx!=d.ClipperBase.Skip;)e=e.Next;if(e.Dx==d.ClipperBase.horizontal&&e.Next.OutIdx!=d.ClipperBase.Skip){for(f=e;f.Prev.Dx==d.ClipperBase.horizontal;)f=f.Prev;f.Prev.Top.X==e.Next.Top.X?b||(e=f.Prev):f.Prev.Top.X>e.Next.Top.X&&(e=f.Prev)}for(;a!=e;)a.NextInLML=a.Next,a.Dx==d.ClipperBase.horizontal&&
a!=c&&a.Bot.X!=a.Prev.Top.X&&this.ReverseHorizontal(a),a=a.Next;a.Dx==d.ClipperBase.horizontal&&a!=c&&a.Bot.X!=a.Prev.Top.X&&this.ReverseHorizontal(a);e=e.Next}else{for(;e.Top.Y==e.Prev.Bot.Y&&e.Prev.OutIdx!=d.ClipperBase.Skip;)e=e.Prev;if(e.Dx==d.ClipperBase.horizontal&&e.Prev.OutIdx!=d.ClipperBase.Skip){for(f=e;f.Next.Dx==d.ClipperBase.horizontal;)f=f.Next;f.Next.Top.X==e.Prev.Top.X?b||(e=f.Next):f.Next.Top.X>e.Prev.Top.X&&(e=f.Next)}for(;a!=e;)a.NextInLML=a.Prev,a.Dx==d.ClipperBase.horizontal&&
a!=c&&a.Bot.X!=a.Next.Top.X&&this.ReverseHorizontal(a),a=a.Prev;a.Dx==d.ClipperBase.horizontal&&a!=c&&a.Bot.X!=a.Next.Top.X&&this.ReverseHorizontal(a);e=e.Prev}if(e.OutIdx==d.ClipperBase.Skip){a=e;if(b){for(;a.Top.Y==a.Next.Bot.Y;)a=a.Next;for(;a!=e&&a.Dx==d.ClipperBase.horizontal;)a=a.Prev}else{for(;a.Top.Y==a.Prev.Bot.Y;)a=a.Prev;for(;a!=e&&a.Dx==d.ClipperBase.horizontal;)a=a.Next}a==e?e=b?a.Next:a.Prev:(a=b?e.Next:e.Prev,c=new d.LocalMinima,c.Next=null,c.Y=a.Bot.Y,c.LeftBound=null,c.RightBound=
a,c.RightBound.WindDelta=0,e=this.ProcessBound(c.RightBound,b),this.InsertLocalMinima(c))}return e};d.ClipperBase.prototype.AddPath=function(a,b,c){c||b!=d.PolyType.ptClip||d.Error("AddPath: Open paths must be subject.");var e=a.length-1;if(c)for(;0<e&&d.IntPoint.op_Equality(a[e],a[0]);)--e;for(;0<e&&d.IntPoint.op_Equality(a[e],a[e-1]);)--e;if(c&&2>e||!c&&1>e)return!1;for(var f=[],g=0;g<=e;g++)f.push(new d.TEdge);var h=!0;f[1].Curr.X=a[1].X;f[1].Curr.Y=a[1].Y;var l={Value:this.m_UseFullRange};this.RangeTest(a[0],
l);this.m_UseFullRange=l.Value;l.Value=this.m_UseFullRange;this.RangeTest(a[e],l);this.m_UseFullRange=l.Value;this.InitEdge(f[0],f[1],f[e],a[0]);this.InitEdge(f[e],f[0],f[e-1],a[e]);for(g=e-1;1<=g;--g)l.Value=this.m_UseFullRange,this.RangeTest(a[g],l),this.m_UseFullRange=l.Value,this.InitEdge(f[g],f[g+1],f[g-1],a[g]);for(g=a=e=f[0];;)if(d.IntPoint.op_Equality(a.Curr,a.Next.Curr)){if(a==a.Next)break;a==e&&(e=a.Next);g=a=this.RemoveEdge(a)}else{if(a.Prev==a.Next)break;else if(c&&d.ClipperBase.SlopesEqual(a.Prev.Curr,
a.Curr,a.Next.Curr,this.m_UseFullRange)&&(!this.PreserveCollinear||!this.Pt2IsBetweenPt1AndPt3(a.Prev.Curr,a.Curr,a.Next.Curr))){a==e&&(e=a.Next);a=this.RemoveEdge(a);g=a=a.Prev;continue}a=a.Next;if(a==g)break}if(!c&&a==a.Next||c&&a.Prev==a.Next)return!1;c||(this.m_HasOpenPaths=!0,e.Prev.OutIdx=d.ClipperBase.Skip);a=e;do this.InitEdge2(a,b),a=a.Next,h&&a.Curr.Y!=e.Curr.Y&&(h=!1);while(a!=e);if(h){if(c)return!1;a.Prev.OutIdx=d.ClipperBase.Skip;a.Prev.Bot.X<a.Prev.Top.X&&this.ReverseHorizontal(a.Prev);
b=new d.LocalMinima;b.Next=null;b.Y=a.Bot.Y;b.LeftBound=null;b.RightBound=a;b.RightBound.Side=d.EdgeSide.esRight;for(b.RightBound.WindDelta=0;a.Next.OutIdx!=d.ClipperBase.Skip;)a.NextInLML=a.Next,a.Bot.X!=a.Prev.Top.X&&this.ReverseHorizontal(a),a=a.Next;this.InsertLocalMinima(b);this.m_edges.push(f);return!0}this.m_edges.push(f);for(h=null;;){a=this.FindNextLocMin(a);if(a==h)break;else null==h&&(h=a);b=new d.LocalMinima;b.Next=null;b.Y=a.Bot.Y;a.Dx<a.Prev.Dx?(b.LeftBound=a.Prev,b.RightBound=a,f=!1):
(b.LeftBound=a,b.RightBound=a.Prev,f=!0);b.LeftBound.Side=d.EdgeSide.esLeft;b.RightBound.Side=d.EdgeSide.esRight;b.LeftBound.WindDelta=c?b.LeftBound.Next==b.RightBound?-1:1:0;b.RightBound.WindDelta=-b.LeftBound.WindDelta;a=this.ProcessBound(b.LeftBound,f);e=this.ProcessBound(b.RightBound,!f);b.LeftBound.OutIdx==d.ClipperBase.Skip?b.LeftBound=null:b.RightBound.OutIdx==d.ClipperBase.Skip&&(b.RightBound=null);this.InsertLocalMinima(b);f||(a=e)}return!0};d.ClipperBase.prototype.AddPaths=function(a,b,
c){for(var e=!1,d=0,g=a.length;d<g;++d)this.AddPath(a[d],b,c)&&(e=!0);return e};d.ClipperBase.prototype.Pt2IsBetweenPt1AndPt3=function(a,b,c){return d.IntPoint.op_Equality(a,c)||d.IntPoint.op_Equality(a,b)||d.IntPoint.op_Equality(c,b)?!1:a.X!=c.X?b.X>a.X==b.X<c.X:b.Y>a.Y==b.Y<c.Y};d.ClipperBase.prototype.RemoveEdge=function(a){a.Prev.Next=a.Next;a.Next.Prev=a.Prev;var b=a.Next;a.Prev=null;return b};d.ClipperBase.prototype.SetDx=function(a){a.Delta.X=a.Top.X-a.Bot.X;a.Delta.Y=a.Top.Y-a.Bot.Y;a.Dx=
0===a.Delta.Y?d.ClipperBase.horizontal:a.Delta.X/a.Delta.Y};d.ClipperBase.prototype.InsertLocalMinima=function(a){if(null===this.m_MinimaList)this.m_MinimaList=a;else if(a.Y>=this.m_MinimaList.Y)a.Next=this.m_MinimaList,this.m_MinimaList=a;else{for(var b=this.m_MinimaList;null!==b.Next&&a.Y<b.Next.Y;)b=b.Next;a.Next=b.Next;b.Next=a}};d.ClipperBase.prototype.PopLocalMinima=function(){null!==this.m_CurrentLM&&(this.m_CurrentLM=this.m_CurrentLM.Next)};d.ClipperBase.prototype.ReverseHorizontal=function(a){var b=
a.Top.X;a.Top.X=a.Bot.X;a.Bot.X=b};d.ClipperBase.prototype.Reset=function(){this.m_CurrentLM=this.m_MinimaList;if(null!=this.m_CurrentLM)for(var a=this.m_MinimaList;null!=a;){var b=a.LeftBound;null!=b&&(b.Curr.X=b.Bot.X,b.Curr.Y=b.Bot.Y,b.Side=d.EdgeSide.esLeft,b.OutIdx=d.ClipperBase.Unassigned);b=a.RightBound;null!=b&&(b.Curr.X=b.Bot.X,b.Curr.Y=b.Bot.Y,b.Side=d.EdgeSide.esRight,b.OutIdx=d.ClipperBase.Unassigned);a=a.Next}};d.Clipper=function(a){"undefined"==typeof a&&(a=0);this.m_PolyOuts=null;this.m_ClipType=
d.ClipType.ctIntersection;this.m_IntersectNodeComparer=this.m_IntersectList=this.m_SortedEdges=this.m_ActiveEdges=this.m_Scanbeam=null;this.m_ExecuteLocked=!1;this.m_SubjFillType=this.m_ClipFillType=d.PolyFillType.pftEvenOdd;this.m_GhostJoins=this.m_Joins=null;this.StrictlySimple=this.ReverseSolution=this.m_UsingPolyTree=!1;d.ClipperBase.call(this);this.m_SortedEdges=this.m_ActiveEdges=this.m_Scanbeam=null;this.m_IntersectList=[];this.m_IntersectNodeComparer=d.MyIntersectNodeSort.Compare;this.m_UsingPolyTree=
this.m_ExecuteLocked=!1;this.m_PolyOuts=[];this.m_Joins=[];this.m_GhostJoins=[];this.ReverseSolution=0!==(1&a);this.StrictlySimple=0!==(2&a);this.PreserveCollinear=0!==(4&a)};d.Clipper.ioReverseSolution=1;d.Clipper.ioStrictlySimple=2;d.Clipper.ioPreserveCollinear=4;d.Clipper.prototype.Clear=function(){0!==this.m_edges.length&&(this.DisposeAllPolyPts(),d.ClipperBase.prototype.Clear.call(this))};d.Clipper.prototype.DisposeScanbeamList=function(){for(;null!==this.m_Scanbeam;){var a=this.m_Scanbeam.Next;
this.m_Scanbeam=null;this.m_Scanbeam=a}};d.Clipper.prototype.Reset=function(){d.ClipperBase.prototype.Reset.call(this);this.m_SortedEdges=this.m_ActiveEdges=this.m_Scanbeam=null;for(var a=this.m_MinimaList;null!==a;)this.InsertScanbeam(a.Y),a=a.Next};d.Clipper.prototype.InsertScanbeam=function(a){if(null===this.m_Scanbeam)this.m_Scanbeam=new d.Scanbeam,this.m_Scanbeam.Next=null,this.m_Scanbeam.Y=a;else if(a>this.m_Scanbeam.Y){var b=new d.Scanbeam;b.Y=a;b.Next=this.m_Scanbeam;this.m_Scanbeam=b}else{for(var c=
this.m_Scanbeam;null!==c.Next&&a<=c.Next.Y;)c=c.Next;a!=c.Y&&(b=new d.Scanbeam,b.Y=a,b.Next=c.Next,c.Next=b)}};d.Clipper.prototype.Execute=function(){var a=arguments,b=a.length,c=a[1]instanceof d.PolyTree;if(4!=b||c){if(4==b&&c){var b=a[0],e=a[1],c=a[2],a=a[3];if(this.m_ExecuteLocked)return!1;this.m_ExecuteLocked=!0;this.m_SubjFillType=c;this.m_ClipFillType=a;this.m_ClipType=b;this.m_UsingPolyTree=!0;try{(f=this.ExecuteInternal())&&this.BuildResult2(e)}finally{this.DisposeAllPolyPts(),this.m_ExecuteLocked=
!1}return f}if(2==b&&!c||2==b&&c)return b=a[0],e=a[1],this.Execute(b,e,d.PolyFillType.pftEvenOdd,d.PolyFillType.pftEvenOdd)}else{b=a[0];e=a[1];c=a[2];a=a[3];if(this.m_ExecuteLocked)return!1;this.m_HasOpenPaths&&d.Error("Error: PolyTree struct is need for open path clipping.");this.m_ExecuteLocked=!0;d.Clear(e);this.m_SubjFillType=c;this.m_ClipFillType=a;this.m_ClipType=b;this.m_UsingPolyTree=!1;try{var f=this.ExecuteInternal();f&&this.BuildResult(e)}finally{this.DisposeAllPolyPts(),this.m_ExecuteLocked=
!1}return f}};d.Clipper.prototype.FixHoleLinkage=function(a){if(null!==a.FirstLeft&&(a.IsHole==a.FirstLeft.IsHole||null===a.FirstLeft.Pts)){for(var b=a.FirstLeft;null!==b&&(b.IsHole==a.IsHole||null===b.Pts);)b=b.FirstLeft;a.FirstLeft=b}};d.Clipper.prototype.ExecuteInternal=function(){try{this.Reset();if(null===this.m_CurrentLM)return!1;var a=this.PopScanbeam();do{this.InsertLocalMinimaIntoAEL(a);d.Clear(this.m_GhostJoins);this.ProcessHorizontals(!1);if(null===this.m_Scanbeam)break;var b=this.PopScanbeam();
if(!this.ProcessIntersections(a,b))return!1;this.ProcessEdgesAtTopOfScanbeam(b);a=b}while(null!==this.m_Scanbeam||null!==this.m_CurrentLM);for(var a=0,c=this.m_PolyOuts.length;a<c;a++){var e=this.m_PolyOuts[a];null===e.Pts||e.IsOpen||(e.IsHole^this.ReverseSolution)==0<this.Area(e)&&this.ReversePolyPtLinks(e.Pts)}this.JoinCommonEdges();a=0;for(c=this.m_PolyOuts.length;a<c;a++)e=this.m_PolyOuts[a],null===e.Pts||e.IsOpen||this.FixupOutPolygon(e);this.StrictlySimple&&this.DoSimplePolygons();return!0}finally{d.Clear(this.m_Joins),
d.Clear(this.m_GhostJoins)}};d.Clipper.prototype.PopScanbeam=function(){var a=this.m_Scanbeam.Y;this.m_Scanbeam=this.m_Scanbeam.Next;return a};d.Clipper.prototype.DisposeAllPolyPts=function(){for(var a=0,b=this.m_PolyOuts.length;a<b;++a)this.DisposeOutRec(a);d.Clear(this.m_PolyOuts)};d.Clipper.prototype.DisposeOutRec=function(a){var b=this.m_PolyOuts[a];null!==b.Pts&&this.DisposeOutPts(b.Pts);this.m_PolyOuts[a]=null};d.Clipper.prototype.DisposeOutPts=function(a){if(null!==a)for(a.Prev.Next=null;null!==
a;)a=a.Next};d.Clipper.prototype.AddJoin=function(a,b,c){var e=new d.Join;e.OutPt1=a;e.OutPt2=b;e.OffPt.X=c.X;e.OffPt.Y=c.Y;this.m_Joins.push(e)};d.Clipper.prototype.AddGhostJoin=function(a,b){var c=new d.Join;c.OutPt1=a;c.OffPt.X=b.X;c.OffPt.Y=b.Y;this.m_GhostJoins.push(c)};d.Clipper.prototype.InsertLocalMinimaIntoAEL=function(a){for(;null!==this.m_CurrentLM&&this.m_CurrentLM.Y==a;){var b=this.m_CurrentLM.LeftBound,c=this.m_CurrentLM.RightBound;this.PopLocalMinima();var e=null;null===b?(this.InsertEdgeIntoAEL(c,
null),this.SetWindingCount(c),this.IsContributing(c)&&(e=this.AddOutPt(c,c.Bot))):(null==c?(this.InsertEdgeIntoAEL(b,null),this.SetWindingCount(b),this.IsContributing(b)&&(e=this.AddOutPt(b,b.Bot))):(this.InsertEdgeIntoAEL(b,null),this.InsertEdgeIntoAEL(c,b),this.SetWindingCount(b),c.WindCnt=b.WindCnt,c.WindCnt2=b.WindCnt2,this.IsContributing(b)&&(e=this.AddLocalMinPoly(b,c,b.Bot))),this.InsertScanbeam(b.Top.Y));null!=c&&(d.ClipperBase.IsHorizontal(c)?this.AddEdgeToSEL(c):this.InsertScanbeam(c.Top.Y));
if(null!=b&&null!=c){if(null!==e&&d.ClipperBase.IsHorizontal(c)&&0<this.m_GhostJoins.length&&0!==c.WindDelta)for(var f=0,g=this.m_GhostJoins.length;f<g;f++){var h=this.m_GhostJoins[f];this.HorzSegmentsOverlap(h.OutPt1.Pt,h.OffPt,c.Bot,c.Top)&&this.AddJoin(h.OutPt1,e,h.OffPt)}0<=b.OutIdx&&null!==b.PrevInAEL&&b.PrevInAEL.Curr.X==b.Bot.X&&0<=b.PrevInAEL.OutIdx&&d.ClipperBase.SlopesEqual(b.PrevInAEL,b,this.m_UseFullRange)&&0!==b.WindDelta&&0!==b.PrevInAEL.WindDelta&&(f=this.AddOutPt(b.PrevInAEL,b.Bot),
this.AddJoin(e,f,b.Top));if(b.NextInAEL!=c&&(0<=c.OutIdx&&0<=c.PrevInAEL.OutIdx&&d.ClipperBase.SlopesEqual(c.PrevInAEL,c,this.m_UseFullRange)&&0!==c.WindDelta&&0!==c.PrevInAEL.WindDelta&&(f=this.AddOutPt(c.PrevInAEL,c.Bot),this.AddJoin(e,f,c.Top)),e=b.NextInAEL,null!==e))for(;e!=c;)this.IntersectEdges(c,e,b.Curr,!1),e=e.NextInAEL}}};d.Clipper.prototype.InsertEdgeIntoAEL=function(a,b){if(null===this.m_ActiveEdges)a.PrevInAEL=null,a.NextInAEL=null,this.m_ActiveEdges=a;else if(null===b&&this.E2InsertsBeforeE1(this.m_ActiveEdges,
a))a.PrevInAEL=null,a.NextInAEL=this.m_ActiveEdges,this.m_ActiveEdges=this.m_ActiveEdges.PrevInAEL=a;else{null===b&&(b=this.m_ActiveEdges);for(;null!==b.NextInAEL&&!this.E2InsertsBeforeE1(b.NextInAEL,a);)b=b.NextInAEL;a.NextInAEL=b.NextInAEL;null!==b.NextInAEL&&(b.NextInAEL.PrevInAEL=a);a.PrevInAEL=b;b.NextInAEL=a}};d.Clipper.prototype.E2InsertsBeforeE1=function(a,b){return b.Curr.X==a.Curr.X?b.Top.Y>a.Top.Y?b.Top.X<d.Clipper.TopX(a,b.Top.Y):a.Top.X>d.Clipper.TopX(b,a.Top.Y):b.Curr.X<a.Curr.X};d.Clipper.prototype.IsEvenOddFillType=
function(a){return a.PolyTyp==d.PolyType.ptSubject?this.m_SubjFillType==d.PolyFillType.pftEvenOdd:this.m_ClipFillType==d.PolyFillType.pftEvenOdd};d.Clipper.prototype.IsEvenOddAltFillType=function(a){return a.PolyTyp==d.PolyType.ptSubject?this.m_ClipFillType==d.PolyFillType.pftEvenOdd:this.m_SubjFillType==d.PolyFillType.pftEvenOdd};d.Clipper.prototype.IsContributing=function(a){var b,c;a.PolyTyp==d.PolyType.ptSubject?(b=this.m_SubjFillType,c=this.m_ClipFillType):(b=this.m_ClipFillType,c=this.m_SubjFillType);
switch(b){case d.PolyFillType.pftEvenOdd:if(0===a.WindDelta&&1!=a.WindCnt)return!1;break;case d.PolyFillType.pftNonZero:if(1!=Math.abs(a.WindCnt))return!1;break;case d.PolyFillType.pftPositive:if(1!=a.WindCnt)return!1;break;default:if(-1!=a.WindCnt)return!1}switch(this.m_ClipType){case d.ClipType.ctIntersection:switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0!==a.WindCnt2;case d.PolyFillType.pftPositive:return 0<a.WindCnt2;default:return 0>a.WindCnt2}case d.ClipType.ctUnion:switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0===
a.WindCnt2;case d.PolyFillType.pftPositive:return 0>=a.WindCnt2;default:return 0<=a.WindCnt2}case d.ClipType.ctDifference:if(a.PolyTyp==d.PolyType.ptSubject)switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0===a.WindCnt2;case d.PolyFillType.pftPositive:return 0>=a.WindCnt2;default:return 0<=a.WindCnt2}else switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0!==a.WindCnt2;case d.PolyFillType.pftPositive:return 0<a.WindCnt2;default:return 0>
a.WindCnt2}case d.ClipType.ctXor:if(0===a.WindDelta)switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0===a.WindCnt2;case d.PolyFillType.pftPositive:return 0>=a.WindCnt2;default:return 0<=a.WindCnt2}}return!0};d.Clipper.prototype.SetWindingCount=function(a){for(var b=a.PrevInAEL;null!==b&&(b.PolyTyp!=a.PolyTyp||0===b.WindDelta);)b=b.PrevInAEL;if(null===b)a.WindCnt=0===a.WindDelta?1:a.WindDelta,a.WindCnt2=0,b=this.m_ActiveEdges;else{if(0===a.WindDelta&&this.m_ClipType!=
d.ClipType.ctUnion)a.WindCnt=1;else if(this.IsEvenOddFillType(a))if(0===a.WindDelta){for(var c=!0,e=b.PrevInAEL;null!==e;)e.PolyTyp==b.PolyTyp&&0!==e.WindDelta&&(c=!c),e=e.PrevInAEL;a.WindCnt=c?0:1}else a.WindCnt=a.WindDelta;else 0>b.WindCnt*b.WindDelta?1<Math.abs(b.WindCnt)?a.WindCnt=0>b.WindDelta*a.WindDelta?b.WindCnt:b.WindCnt+a.WindDelta:a.WindCnt=0===a.WindDelta?1:a.WindDelta:a.WindCnt=0===a.WindDelta?0>b.WindCnt?b.WindCnt-1:b.WindCnt+1:0>b.WindDelta*a.WindDelta?b.WindCnt:b.WindCnt+a.WindDelta;
a.WindCnt2=b.WindCnt2;b=b.NextInAEL}if(this.IsEvenOddAltFillType(a))for(;b!=a;)0!==b.WindDelta&&(a.WindCnt2=0===a.WindCnt2?1:0),b=b.NextInAEL;else for(;b!=a;)a.WindCnt2+=b.WindDelta,b=b.NextInAEL};d.Clipper.prototype.AddEdgeToSEL=function(a){null===this.m_SortedEdges?(this.m_SortedEdges=a,a.PrevInSEL=null,a.NextInSEL=null):(a.NextInSEL=this.m_SortedEdges,a.PrevInSEL=null,this.m_SortedEdges=this.m_SortedEdges.PrevInSEL=a)};d.Clipper.prototype.CopyAELToSEL=function(){var a=this.m_ActiveEdges;for(this.m_SortedEdges=
a;null!==a;)a.PrevInSEL=a.PrevInAEL,a=a.NextInSEL=a.NextInAEL};d.Clipper.prototype.SwapPositionsInAEL=function(a,b){if(a.NextInAEL!=a.PrevInAEL&&b.NextInAEL!=b.PrevInAEL){if(a.NextInAEL==b){var c=b.NextInAEL;null!==c&&(c.PrevInAEL=a);var e=a.PrevInAEL;null!==e&&(e.NextInAEL=b);b.PrevInAEL=e;b.NextInAEL=a;a.PrevInAEL=b;a.NextInAEL=c}else b.NextInAEL==a?(c=a.NextInAEL,null!==c&&(c.PrevInAEL=b),e=b.PrevInAEL,null!==e&&(e.NextInAEL=a),a.PrevInAEL=e,a.NextInAEL=b,b.PrevInAEL=a,b.NextInAEL=c):(c=a.NextInAEL,
e=a.PrevInAEL,a.NextInAEL=b.NextInAEL,null!==a.NextInAEL&&(a.NextInAEL.PrevInAEL=a),a.PrevInAEL=b.PrevInAEL,null!==a.PrevInAEL&&(a.PrevInAEL.NextInAEL=a),b.NextInAEL=c,null!==b.NextInAEL&&(b.NextInAEL.PrevInAEL=b),b.PrevInAEL=e,null!==b.PrevInAEL&&(b.PrevInAEL.NextInAEL=b));null===a.PrevInAEL?this.m_ActiveEdges=a:null===b.PrevInAEL&&(this.m_ActiveEdges=b)}};d.Clipper.prototype.SwapPositionsInSEL=function(a,b){if(null!==a.NextInSEL||null!==a.PrevInSEL)if(null!==b.NextInSEL||null!==b.PrevInSEL){if(a.NextInSEL==
b){var c=b.NextInSEL;null!==c&&(c.PrevInSEL=a);var e=a.PrevInSEL;null!==e&&(e.NextInSEL=b);b.PrevInSEL=e;b.NextInSEL=a;a.PrevInSEL=b;a.NextInSEL=c}else b.NextInSEL==a?(c=a.NextInSEL,null!==c&&(c.PrevInSEL=b),e=b.PrevInSEL,null!==e&&(e.NextInSEL=a),a.PrevInSEL=e,a.NextInSEL=b,b.PrevInSEL=a,b.NextInSEL=c):(c=a.NextInSEL,e=a.PrevInSEL,a.NextInSEL=b.NextInSEL,null!==a.NextInSEL&&(a.NextInSEL.PrevInSEL=a),a.PrevInSEL=b.PrevInSEL,null!==a.PrevInSEL&&(a.PrevInSEL.NextInSEL=a),b.NextInSEL=c,null!==b.NextInSEL&&
(b.NextInSEL.PrevInSEL=b),b.PrevInSEL=e,null!==b.PrevInSEL&&(b.PrevInSEL.NextInSEL=b));null===a.PrevInSEL?this.m_SortedEdges=a:null===b.PrevInSEL&&(this.m_SortedEdges=b)}};d.Clipper.prototype.AddLocalMaxPoly=function(a,b,c){this.AddOutPt(a,c);0==b.WindDelta&&this.AddOutPt(b,c);a.OutIdx==b.OutIdx?(a.OutIdx=-1,b.OutIdx=-1):a.OutIdx<b.OutIdx?this.AppendPolygon(a,b):this.AppendPolygon(b,a)};d.Clipper.prototype.AddLocalMinPoly=function(a,b,c){var e,f;d.ClipperBase.IsHorizontal(b)||a.Dx>b.Dx?(e=this.AddOutPt(a,
c),b.OutIdx=a.OutIdx,a.Side=d.EdgeSide.esLeft,b.Side=d.EdgeSide.esRight,f=a,a=f.PrevInAEL==b?b.PrevInAEL:f.PrevInAEL):(e=this.AddOutPt(b,c),a.OutIdx=b.OutIdx,a.Side=d.EdgeSide.esRight,b.Side=d.EdgeSide.esLeft,f=b,a=f.PrevInAEL==a?a.PrevInAEL:f.PrevInAEL);null!==a&&0<=a.OutIdx&&d.Clipper.TopX(a,c.Y)==d.Clipper.TopX(f,c.Y)&&d.ClipperBase.SlopesEqual(f,a,this.m_UseFullRange)&&0!==f.WindDelta&&0!==a.WindDelta&&(c=this.AddOutPt(a,c),this.AddJoin(e,c,f.Top));return e};d.Clipper.prototype.CreateOutRec=function(){var a=
new d.OutRec;a.Idx=-1;a.IsHole=!1;a.IsOpen=!1;a.FirstLeft=null;a.Pts=null;a.BottomPt=null;a.PolyNode=null;this.m_PolyOuts.push(a);a.Idx=this.m_PolyOuts.length-1;return a};d.Clipper.prototype.AddOutPt=function(a,b){var c=a.Side==d.EdgeSide.esLeft;if(0>a.OutIdx){var e=this.CreateOutRec();e.IsOpen=0===a.WindDelta;var f=new d.OutPt;e.Pts=f;f.Idx=e.Idx;f.Pt.X=b.X;f.Pt.Y=b.Y;f.Next=f;f.Prev=f;e.IsOpen||this.SetHoleState(a,e);a.OutIdx=e.Idx}else{var e=this.m_PolyOuts[a.OutIdx],g=e.Pts;if(c&&d.IntPoint.op_Equality(b,
g.Pt))return g;if(!c&&d.IntPoint.op_Equality(b,g.Prev.Pt))return g.Prev;f=new d.OutPt;f.Idx=e.Idx;f.Pt.X=b.X;f.Pt.Y=b.Y;f.Next=g;f.Prev=g.Prev;f.Prev.Next=f;g.Prev=f;c&&(e.Pts=f)}return f};d.Clipper.prototype.SwapPoints=function(a,b){var c=new d.IntPoint(a.Value);a.Value.X=b.Value.X;a.Value.Y=b.Value.Y;b.Value.X=c.X;b.Value.Y=c.Y};d.Clipper.prototype.HorzSegmentsOverlap=function(a,b,c,e){return a.X>c.X==a.X<e.X?!0:b.X>c.X==b.X<e.X?!0:c.X>a.X==c.X<b.X?!0:e.X>a.X==e.X<b.X?!0:a.X==c.X&&b.X==e.X?!0:a.X==
e.X&&b.X==c.X?!0:!1};d.Clipper.prototype.InsertPolyPtBetween=function(a,b,c){var e=new d.OutPt;e.Pt.X=c.X;e.Pt.Y=c.Y;b==a.Next?(a.Next=e,b.Prev=e,e.Next=b,e.Prev=a):(b.Next=e,a.Prev=e,e.Next=a,e.Prev=b);return e};d.Clipper.prototype.SetHoleState=function(a,b){for(var c=!1,e=a.PrevInAEL;null!==e;)0<=e.OutIdx&&0!=e.WindDelta&&(c=!c,null===b.FirstLeft&&(b.FirstLeft=this.m_PolyOuts[e.OutIdx])),e=e.PrevInAEL;c&&(b.IsHole=!0)};d.Clipper.prototype.GetDx=function(a,b){return a.Y==b.Y?d.ClipperBase.horizontal:
(b.X-a.X)/(b.Y-a.Y)};d.Clipper.prototype.FirstIsBottomPt=function(a,b){for(var c=a.Prev;d.IntPoint.op_Equality(c.Pt,a.Pt)&&c!=a;)c=c.Prev;for(var e=Math.abs(this.GetDx(a.Pt,c.Pt)),c=a.Next;d.IntPoint.op_Equality(c.Pt,a.Pt)&&c!=a;)c=c.Next;for(var f=Math.abs(this.GetDx(a.Pt,c.Pt)),c=b.Prev;d.IntPoint.op_Equality(c.Pt,b.Pt)&&c!=b;)c=c.Prev;for(var g=Math.abs(this.GetDx(b.Pt,c.Pt)),c=b.Next;d.IntPoint.op_Equality(c.Pt,b.Pt)&&c!=b;)c=c.Next;c=Math.abs(this.GetDx(b.Pt,c.Pt));return e>=g&&e>=c||f>=g&&f>=
c};d.Clipper.prototype.GetBottomPt=function(a){for(var b=null,c=a.Next;c!=a;)c.Pt.Y>a.Pt.Y?(a=c,b=null):c.Pt.Y==a.Pt.Y&&c.Pt.X<=a.Pt.X&&(c.Pt.X<a.Pt.X?(b=null,a=c):c.Next!=a&&c.Prev!=a&&(b=c)),c=c.Next;if(null!==b)for(;b!=c;)for(this.FirstIsBottomPt(c,b)||(a=b),b=b.Next;d.IntPoint.op_Inequality(b.Pt,a.Pt);)b=b.Next;return a};d.Clipper.prototype.GetLowermostRec=function(a,b){null===a.BottomPt&&(a.BottomPt=this.GetBottomPt(a.Pts));null===b.BottomPt&&(b.BottomPt=this.GetBottomPt(b.Pts));var c=a.BottomPt,
e=b.BottomPt;return c.Pt.Y>e.Pt.Y?a:c.Pt.Y<e.Pt.Y?b:c.Pt.X<e.Pt.X?a:c.Pt.X>e.Pt.X?b:c.Next==c?b:e.Next==e?a:this.FirstIsBottomPt(c,e)?a:b};d.Clipper.prototype.Param1RightOfParam2=function(a,b){do if(a=a.FirstLeft,a==b)return!0;while(null!==a);return!1};d.Clipper.prototype.GetOutRec=function(a){for(a=this.m_PolyOuts[a];a!=this.m_PolyOuts[a.Idx];)a=this.m_PolyOuts[a.Idx];return a};d.Clipper.prototype.AppendPolygon=function(a,b){var c=this.m_PolyOuts[a.OutIdx],e=this.m_PolyOuts[b.OutIdx],f;f=this.Param1RightOfParam2(c,
e)?e:this.Param1RightOfParam2(e,c)?c:this.GetLowermostRec(c,e);var g=c.Pts,h=g.Prev,l=e.Pts,k=l.Prev;a.Side==d.EdgeSide.esLeft?(b.Side==d.EdgeSide.esLeft?(this.ReversePolyPtLinks(l),l.Next=g,g.Prev=l,h.Next=k,k.Prev=h,c.Pts=k):(k.Next=g,g.Prev=k,l.Prev=h,h.Next=l,c.Pts=l),g=d.EdgeSide.esLeft):(b.Side==d.EdgeSide.esRight?(this.ReversePolyPtLinks(l),h.Next=k,k.Prev=h,l.Next=g,g.Prev=l):(h.Next=l,l.Prev=h,g.Prev=k,k.Next=g),g=d.EdgeSide.esRight);c.BottomPt=null;f==e&&(e.FirstLeft!=c&&(c.FirstLeft=e.FirstLeft),
c.IsHole=e.IsHole);e.Pts=null;e.BottomPt=null;e.FirstLeft=c;f=a.OutIdx;h=b.OutIdx;a.OutIdx=-1;b.OutIdx=-1;for(l=this.m_ActiveEdges;null!==l;){if(l.OutIdx==h){l.OutIdx=f;l.Side=g;break}l=l.NextInAEL}e.Idx=c.Idx};d.Clipper.prototype.ReversePolyPtLinks=function(a){if(null!==a){var b,c;b=a;do c=b.Next,b.Next=b.Prev,b=b.Prev=c;while(b!=a)}};d.Clipper.SwapSides=function(a,b){var c=a.Side;a.Side=b.Side;b.Side=c};d.Clipper.SwapPolyIndexes=function(a,b){var c=a.OutIdx;a.OutIdx=b.OutIdx;b.OutIdx=c};d.Clipper.prototype.IntersectEdges=
function(a,b,c,e){var f=!e&&null===a.NextInLML&&a.Top.X==c.X&&a.Top.Y==c.Y;e=!e&&null===b.NextInLML&&b.Top.X==c.X&&b.Top.Y==c.Y;var g=0<=a.OutIdx,h=0<=b.OutIdx;if(0===a.WindDelta||0===b.WindDelta)0===a.WindDelta&&0===b.WindDelta?(f||e)&&g&&h&&this.AddLocalMaxPoly(a,b,c):a.PolyTyp==b.PolyTyp&&a.WindDelta!=b.WindDelta&&this.m_ClipType==d.ClipType.ctUnion?0===a.WindDelta?h&&(this.AddOutPt(a,c),g&&(a.OutIdx=-1)):g&&(this.AddOutPt(b,c),h&&(b.OutIdx=-1)):a.PolyTyp!=b.PolyTyp&&(0!==a.WindDelta||1!=Math.abs(b.WindCnt)||
this.m_ClipType==d.ClipType.ctUnion&&0!==b.WindCnt2?0!==b.WindDelta||1!=Math.abs(a.WindCnt)||this.m_ClipType==d.ClipType.ctUnion&&0!==a.WindCnt2||(this.AddOutPt(b,c),h&&(b.OutIdx=-1)):(this.AddOutPt(a,c),g&&(a.OutIdx=-1))),f&&(0>a.OutIdx?this.DeleteFromAEL(a):d.Error("Error intersecting polylines")),e&&(0>b.OutIdx?this.DeleteFromAEL(b):d.Error("Error intersecting polylines"));else{if(a.PolyTyp==b.PolyTyp)if(this.IsEvenOddFillType(a)){var l=a.WindCnt;a.WindCnt=b.WindCnt;b.WindCnt=l}else a.WindCnt=
0===a.WindCnt+b.WindDelta?-a.WindCnt:a.WindCnt+b.WindDelta,b.WindCnt=0===b.WindCnt-a.WindDelta?-b.WindCnt:b.WindCnt-a.WindDelta;else this.IsEvenOddFillType(b)?a.WindCnt2=0===a.WindCnt2?1:0:a.WindCnt2+=b.WindDelta,this.IsEvenOddFillType(a)?b.WindCnt2=0===b.WindCnt2?1:0:b.WindCnt2-=a.WindDelta;var k,n,m;a.PolyTyp==d.PolyType.ptSubject?(k=this.m_SubjFillType,m=this.m_ClipFillType):(k=this.m_ClipFillType,m=this.m_SubjFillType);b.PolyTyp==d.PolyType.ptSubject?(n=this.m_SubjFillType,l=this.m_ClipFillType):
(n=this.m_ClipFillType,l=this.m_SubjFillType);switch(k){case d.PolyFillType.pftPositive:k=a.WindCnt;break;case d.PolyFillType.pftNegative:k=-a.WindCnt;break;default:k=Math.abs(a.WindCnt)}switch(n){case d.PolyFillType.pftPositive:n=b.WindCnt;break;case d.PolyFillType.pftNegative:n=-b.WindCnt;break;default:n=Math.abs(b.WindCnt)}if(g&&h)f||e||0!==k&&1!=k||0!==n&&1!=n||a.PolyTyp!=b.PolyTyp&&this.m_ClipType!=d.ClipType.ctXor?this.AddLocalMaxPoly(a,b,c):(this.AddOutPt(a,c),this.AddOutPt(b,c),d.Clipper.SwapSides(a,
b),d.Clipper.SwapPolyIndexes(a,b));else if(g){if(0===n||1==n)this.AddOutPt(a,c),d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b)}else if(h){if(0===k||1==k)this.AddOutPt(b,c),d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b)}else if(!(0!==k&&1!=k||0!==n&&1!=n||f||e)){switch(m){case d.PolyFillType.pftPositive:g=a.WindCnt2;break;case d.PolyFillType.pftNegative:g=-a.WindCnt2;break;default:g=Math.abs(a.WindCnt2)}switch(l){case d.PolyFillType.pftPositive:h=b.WindCnt2;break;case d.PolyFillType.pftNegative:h=
-b.WindCnt2;break;default:h=Math.abs(b.WindCnt2)}if(a.PolyTyp!=b.PolyTyp)this.AddLocalMinPoly(a,b,c);else if(1==k&&1==n)switch(this.m_ClipType){case d.ClipType.ctIntersection:0<g&&0<h&&this.AddLocalMinPoly(a,b,c);break;case d.ClipType.ctUnion:0>=g&&0>=h&&this.AddLocalMinPoly(a,b,c);break;case d.ClipType.ctDifference:(a.PolyTyp==d.PolyType.ptClip&&0<g&&0<h||a.PolyTyp==d.PolyType.ptSubject&&0>=g&&0>=h)&&this.AddLocalMinPoly(a,b,c);break;case d.ClipType.ctXor:this.AddLocalMinPoly(a,b,c)}else d.Clipper.SwapSides(a,
b)}f!=e&&(f&&0<=a.OutIdx||e&&0<=b.OutIdx)&&(d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b));f&&this.DeleteFromAEL(a);e&&this.DeleteFromAEL(b)}};d.Clipper.prototype.DeleteFromAEL=function(a){var b=a.PrevInAEL,c=a.NextInAEL;if(null!==b||null!==c||a==this.m_ActiveEdges)null!==b?b.NextInAEL=c:this.m_ActiveEdges=c,null!==c&&(c.PrevInAEL=b),a.NextInAEL=null,a.PrevInAEL=null};d.Clipper.prototype.DeleteFromSEL=function(a){var b=a.PrevInSEL,c=a.NextInSEL;if(null!==b||null!==c||a==this.m_SortedEdges)null!==
b?b.NextInSEL=c:this.m_SortedEdges=c,null!==c&&(c.PrevInSEL=b),a.NextInSEL=null,a.PrevInSEL=null};d.Clipper.prototype.UpdateEdgeIntoAEL=function(a){null===a.NextInLML&&d.Error("UpdateEdgeIntoAEL: invalid call");var b=a.PrevInAEL,c=a.NextInAEL;a.NextInLML.OutIdx=a.OutIdx;null!==b?b.NextInAEL=a.NextInLML:this.m_ActiveEdges=a.NextInLML;null!==c&&(c.PrevInAEL=a.NextInLML);a.NextInLML.Side=a.Side;a.NextInLML.WindDelta=a.WindDelta;a.NextInLML.WindCnt=a.WindCnt;a.NextInLML.WindCnt2=a.WindCnt2;a=a.NextInLML;
a.Curr.X=a.Bot.X;a.Curr.Y=a.Bot.Y;a.PrevInAEL=b;a.NextInAEL=c;d.ClipperBase.IsHorizontal(a)||this.InsertScanbeam(a.Top.Y);return a};d.Clipper.prototype.ProcessHorizontals=function(a){for(var b=this.m_SortedEdges;null!==b;)this.DeleteFromSEL(b),this.ProcessHorizontal(b,a),b=this.m_SortedEdges};d.Clipper.prototype.GetHorzDirection=function(a,b){a.Bot.X<a.Top.X?(b.Left=a.Bot.X,b.Right=a.Top.X,b.Dir=d.Direction.dLeftToRight):(b.Left=a.Top.X,b.Right=a.Bot.X,b.Dir=d.Direction.dRightToLeft)};d.Clipper.prototype.PrepareHorzJoins=
function(a,b){var c=this.m_PolyOuts[a.OutIdx].Pts;a.Side!=d.EdgeSide.esLeft&&(c=c.Prev);b&&(d.IntPoint.op_Equality(c.Pt,a.Top)?this.AddGhostJoin(c,a.Bot):this.AddGhostJoin(c,a.Top))};d.Clipper.prototype.ProcessHorizontal=function(a,b){var c={Dir:null,Left:null,Right:null};this.GetHorzDirection(a,c);for(var e=c.Dir,f=c.Left,g=c.Right,h=a,l=null;null!==h.NextInLML&&d.ClipperBase.IsHorizontal(h.NextInLML);)h=h.NextInLML;for(null===h.NextInLML&&(l=this.GetMaximaPair(h));;){for(var k=a==h,n=this.GetNextInAEL(a,
e);null!==n&&!(n.Curr.X==a.Top.X&&null!==a.NextInLML&&n.Dx<a.NextInLML.Dx);){c=this.GetNextInAEL(n,e);if(e==d.Direction.dLeftToRight&&n.Curr.X<=g||e==d.Direction.dRightToLeft&&n.Curr.X>=f){0<=a.OutIdx&&0!=a.WindDelta&&this.PrepareHorzJoins(a,b);if(n==l&&k){e==d.Direction.dLeftToRight?this.IntersectEdges(a,n,n.Top,!1):this.IntersectEdges(n,a,n.Top,!1);0<=l.OutIdx&&d.Error("ProcessHorizontal error");return}if(e==d.Direction.dLeftToRight){var m=new d.IntPoint(n.Curr.X,a.Curr.Y);this.IntersectEdges(a,
n,m,!0)}else m=new d.IntPoint(n.Curr.X,a.Curr.Y),this.IntersectEdges(n,a,m,!0);this.SwapPositionsInAEL(a,n)}else if(e==d.Direction.dLeftToRight&&n.Curr.X>=g||e==d.Direction.dRightToLeft&&n.Curr.X<=f)break;n=c}0<=a.OutIdx&&0!==a.WindDelta&&this.PrepareHorzJoins(a,b);if(null!==a.NextInLML&&d.ClipperBase.IsHorizontal(a.NextInLML))a=this.UpdateEdgeIntoAEL(a),0<=a.OutIdx&&this.AddOutPt(a,a.Bot),c={Dir:e,Left:f,Right:g},this.GetHorzDirection(a,c),e=c.Dir,f=c.Left,g=c.Right;else break}null!==a.NextInLML?
0<=a.OutIdx?(e=this.AddOutPt(a,a.Top),a=this.UpdateEdgeIntoAEL(a),0!==a.WindDelta&&(f=a.PrevInAEL,c=a.NextInAEL,null!==f&&f.Curr.X==a.Bot.X&&f.Curr.Y==a.Bot.Y&&0!==f.WindDelta&&0<=f.OutIdx&&f.Curr.Y>f.Top.Y&&d.ClipperBase.SlopesEqual(a,f,this.m_UseFullRange)?(c=this.AddOutPt(f,a.Bot),this.AddJoin(e,c,a.Top)):null!==c&&c.Curr.X==a.Bot.X&&c.Curr.Y==a.Bot.Y&&0!==c.WindDelta&&0<=c.OutIdx&&c.Curr.Y>c.Top.Y&&d.ClipperBase.SlopesEqual(a,c,this.m_UseFullRange)&&(c=this.AddOutPt(c,a.Bot),this.AddJoin(e,c,
a.Top)))):this.UpdateEdgeIntoAEL(a):null!==l?0<=l.OutIdx?(e==d.Direction.dLeftToRight?this.IntersectEdges(a,l,a.Top,!1):this.IntersectEdges(l,a,a.Top,!1),0<=l.OutIdx&&d.Error("ProcessHorizontal error")):(this.DeleteFromAEL(a),this.DeleteFromAEL(l)):(0<=a.OutIdx&&this.AddOutPt(a,a.Top),this.DeleteFromAEL(a))};d.Clipper.prototype.GetNextInAEL=function(a,b){return b==d.Direction.dLeftToRight?a.NextInAEL:a.PrevInAEL};d.Clipper.prototype.IsMinima=function(a){return null!==a&&a.Prev.NextInLML!=a&&a.Next.NextInLML!=
a};d.Clipper.prototype.IsMaxima=function(a,b){return null!==a&&a.Top.Y==b&&null===a.NextInLML};d.Clipper.prototype.IsIntermediate=function(a,b){return a.Top.Y==b&&null!==a.NextInLML};d.Clipper.prototype.GetMaximaPair=function(a){var b=null;d.IntPoint.op_Equality(a.Next.Top,a.Top)&&null===a.Next.NextInLML?b=a.Next:d.IntPoint.op_Equality(a.Prev.Top,a.Top)&&null===a.Prev.NextInLML&&(b=a.Prev);return null===b||-2!=b.OutIdx&&(b.NextInAEL!=b.PrevInAEL||d.ClipperBase.IsHorizontal(b))?b:null};d.Clipper.prototype.ProcessIntersections=
function(a,b){if(null==this.m_ActiveEdges)return!0;try{this.BuildIntersectList(a,b);if(0==this.m_IntersectList.length)return!0;if(1==this.m_IntersectList.length||this.FixupIntersectionOrder())this.ProcessIntersectList();else return!1}catch(c){this.m_SortedEdges=null,this.m_IntersectList.length=0,d.Error("ProcessIntersections error")}this.m_SortedEdges=null;return!0};d.Clipper.prototype.BuildIntersectList=function(a,b){if(null!==this.m_ActiveEdges){var c=this.m_ActiveEdges;for(this.m_SortedEdges=c;null!==
c;)c.PrevInSEL=c.PrevInAEL,c.NextInSEL=c.NextInAEL,c.Curr.X=d.Clipper.TopX(c,b),c=c.NextInAEL;for(var e=!0;e&&null!==this.m_SortedEdges;){e=!1;for(c=this.m_SortedEdges;null!==c.NextInSEL;){var f=c.NextInSEL,g=new d.IntPoint;c.Curr.X>f.Curr.X?(!this.IntersectPoint(c,f,g)&&c.Curr.X>f.Curr.X+1&&d.Error("Intersection error"),g.Y>a&&(g.Y=a,Math.abs(c.Dx)>Math.abs(f.Dx)?g.X=d.Clipper.TopX(f,a):g.X=d.Clipper.TopX(c,a)),e=new d.IntersectNode,e.Edge1=c,e.Edge2=f,e.Pt.X=g.X,e.Pt.Y=g.Y,this.m_IntersectList.push(e),
this.SwapPositionsInSEL(c,f),e=!0):c=f}if(null!==c.PrevInSEL)c.PrevInSEL.NextInSEL=null;else break}this.m_SortedEdges=null}};d.Clipper.prototype.EdgesAdjacent=function(a){return a.Edge1.NextInSEL==a.Edge2||a.Edge1.PrevInSEL==a.Edge2};d.Clipper.IntersectNodeSort=function(a,b){return b.Pt.Y-a.Pt.Y};d.Clipper.prototype.FixupIntersectionOrder=function(){this.m_IntersectList.sort(this.m_IntersectNodeComparer);this.CopyAELToSEL();for(var a=this.m_IntersectList.length,b=0;b<a;b++){if(!this.EdgesAdjacent(this.m_IntersectList[b])){for(var c=
b+1;c<a&&!this.EdgesAdjacent(this.m_IntersectList[c]);)c++;if(c==a)return!1;var e=this.m_IntersectList[b];this.m_IntersectList[b]=this.m_IntersectList[c];this.m_IntersectList[c]=e}this.SwapPositionsInSEL(this.m_IntersectList[b].Edge1,this.m_IntersectList[b].Edge2)}return!0};d.Clipper.prototype.ProcessIntersectList=function(){for(var a=0,b=this.m_IntersectList.length;a<b;a++){var c=this.m_IntersectList[a];this.IntersectEdges(c.Edge1,c.Edge2,c.Pt,!0);this.SwapPositionsInAEL(c.Edge1,c.Edge2)}this.m_IntersectList.length=
0};E=function(a){return 0>a?Math.ceil(a-0.5):Math.round(a)};F=function(a){return 0>a?Math.ceil(a-0.5):Math.floor(a+0.5)};G=function(a){return 0>a?-Math.round(Math.abs(a)):Math.round(a)};H=function(a){if(0>a)return a-=0.5,-2147483648>a?Math.ceil(a):a|0;a+=0.5;return 2147483647<a?Math.floor(a):a|0};d.Clipper.Round=p?E:D?G:J?H:F;d.Clipper.TopX=function(a,b){return b==a.Top.Y?a.Top.X:a.Bot.X+d.Clipper.Round(a.Dx*(b-a.Bot.Y))};d.Clipper.prototype.IntersectPoint=function(a,b,c){c.X=0;c.Y=0;var e,f;if(d.ClipperBase.SlopesEqual(a,
b,this.m_UseFullRange)||a.Dx==b.Dx)return b.Bot.Y>a.Bot.Y?(c.X=b.Bot.X,c.Y=b.Bot.Y):(c.X=a.Bot.X,c.Y=a.Bot.Y),!1;if(0===a.Delta.X)c.X=a.Bot.X,d.ClipperBase.IsHorizontal(b)?c.Y=b.Bot.Y:(f=b.Bot.Y-b.Bot.X/b.Dx,c.Y=d.Clipper.Round(c.X/b.Dx+f));else if(0===b.Delta.X)c.X=b.Bot.X,d.ClipperBase.IsHorizontal(a)?c.Y=a.Bot.Y:(e=a.Bot.Y-a.Bot.X/a.Dx,c.Y=d.Clipper.Round(c.X/a.Dx+e));else{e=a.Bot.X-a.Bot.Y*a.Dx;f=b.Bot.X-b.Bot.Y*b.Dx;var g=(f-e)/(a.Dx-b.Dx);c.Y=d.Clipper.Round(g);Math.abs(a.Dx)<Math.abs(b.Dx)?
c.X=d.Clipper.Round(a.Dx*g+e):c.X=d.Clipper.Round(b.Dx*g+f)}if(c.Y<a.Top.Y||c.Y<b.Top.Y){if(a.Top.Y>b.Top.Y)return c.Y=a.Top.Y,c.X=d.Clipper.TopX(b,a.Top.Y),c.X<a.Top.X;c.Y=b.Top.Y;Math.abs(a.Dx)<Math.abs(b.Dx)?c.X=d.Clipper.TopX(a,c.Y):c.X=d.Clipper.TopX(b,c.Y)}return!0};d.Clipper.prototype.ProcessEdgesAtTopOfScanbeam=function(a){for(var b=this.m_ActiveEdges;null!==b;){var c=this.IsMaxima(b,a);c&&(c=this.GetMaximaPair(b),c=null===c||!d.ClipperBase.IsHorizontal(c));if(c){var e=b.PrevInAEL;this.DoMaxima(b);
b=null===e?this.m_ActiveEdges:e.NextInAEL}else this.IsIntermediate(b,a)&&d.ClipperBase.IsHorizontal(b.NextInLML)?(b=this.UpdateEdgeIntoAEL(b),0<=b.OutIdx&&this.AddOutPt(b,b.Bot),this.AddEdgeToSEL(b)):(b.Curr.X=d.Clipper.TopX(b,a),b.Curr.Y=a),this.StrictlySimple&&(e=b.PrevInAEL,0<=b.OutIdx&&0!==b.WindDelta&&null!==e&&0<=e.OutIdx&&e.Curr.X==b.Curr.X&&0!==e.WindDelta&&(c=this.AddOutPt(e,b.Curr),e=this.AddOutPt(b,b.Curr),this.AddJoin(c,e,b.Curr))),b=b.NextInAEL}this.ProcessHorizontals(!0);for(b=this.m_ActiveEdges;null!==
b;){if(this.IsIntermediate(b,a)){c=null;0<=b.OutIdx&&(c=this.AddOutPt(b,b.Top));var b=this.UpdateEdgeIntoAEL(b),e=b.PrevInAEL,f=b.NextInAEL;null!==e&&e.Curr.X==b.Bot.X&&e.Curr.Y==b.Bot.Y&&null!==c&&0<=e.OutIdx&&e.Curr.Y>e.Top.Y&&d.ClipperBase.SlopesEqual(b,e,this.m_UseFullRange)&&0!==b.WindDelta&&0!==e.WindDelta?(e=this.AddOutPt(e,b.Bot),this.AddJoin(c,e,b.Top)):null!==f&&f.Curr.X==b.Bot.X&&f.Curr.Y==b.Bot.Y&&null!==c&&0<=f.OutIdx&&f.Curr.Y>f.Top.Y&&d.ClipperBase.SlopesEqual(b,f,this.m_UseFullRange)&&
0!==b.WindDelta&&0!==f.WindDelta&&(e=this.AddOutPt(f,b.Bot),this.AddJoin(c,e,b.Top))}b=b.NextInAEL}};d.Clipper.prototype.DoMaxima=function(a){var b=this.GetMaximaPair(a);if(null===b)0<=a.OutIdx&&this.AddOutPt(a,a.Top),this.DeleteFromAEL(a);else{for(var c=a.NextInAEL;null!==c&&c!=b;)this.IntersectEdges(a,c,a.Top,!0),this.SwapPositionsInAEL(a,c),c=a.NextInAEL;-1==a.OutIdx&&-1==b.OutIdx?(this.DeleteFromAEL(a),this.DeleteFromAEL(b)):0<=a.OutIdx&&0<=b.OutIdx?this.IntersectEdges(a,b,a.Top,!1):0===a.WindDelta?
(0<=a.OutIdx&&(this.AddOutPt(a,a.Top),a.OutIdx=-1),this.DeleteFromAEL(a),0<=b.OutIdx&&(this.AddOutPt(b,a.Top),b.OutIdx=-1),this.DeleteFromAEL(b)):d.Error("DoMaxima error")}};d.Clipper.ReversePaths=function(a){for(var b=0,c=a.length;b<c;b++)a[b].reverse()};d.Clipper.Orientation=function(a){return 0<=d.Clipper.Area(a)};d.Clipper.prototype.PointCount=function(a){if(null===a)return 0;var b=0,c=a;do b++,c=c.Next;while(c!=a);return b};d.Clipper.prototype.BuildResult=function(a){d.Clear(a);for(var b=0,c=
this.m_PolyOuts.length;b<c;b++){var e=this.m_PolyOuts[b];if(null!==e.Pts){var e=e.Pts.Prev,f=this.PointCount(e);if(!(2>f)){for(var g=Array(f),h=0;h<f;h++)g[h]=e.Pt,e=e.Prev;a.push(g)}}}};d.Clipper.prototype.BuildResult2=function(a){a.Clear();for(var b=0,c=this.m_PolyOuts.length;b<c;b++){var e=this.m_PolyOuts[b],f=this.PointCount(e.Pts);if(!(e.IsOpen&&2>f||!e.IsOpen&&3>f)){this.FixHoleLinkage(e);var g=new d.PolyNode;a.m_AllPolys.push(g);e.PolyNode=g;g.m_polygon.length=f;for(var e=e.Pts.Prev,h=0;h<
f;h++)g.m_polygon[h]=e.Pt,e=e.Prev}}b=0;for(c=this.m_PolyOuts.length;b<c;b++)e=this.m_PolyOuts[b],null!==e.PolyNode&&(e.IsOpen?(e.PolyNode.IsOpen=!0,a.AddChild(e.PolyNode)):null!==e.FirstLeft&&null!=e.FirstLeft.PolyNode?e.FirstLeft.PolyNode.AddChild(e.PolyNode):a.AddChild(e.PolyNode))};d.Clipper.prototype.FixupOutPolygon=function(a){var b=null;a.BottomPt=null;for(var c=a.Pts;;){if(c.Prev==c||c.Prev==c.Next){this.DisposeOutPts(c);a.Pts=null;return}if(d.IntPoint.op_Equality(c.Pt,c.Next.Pt)||d.IntPoint.op_Equality(c.Pt,
c.Prev.Pt)||d.ClipperBase.SlopesEqual(c.Prev.Pt,c.Pt,c.Next.Pt,this.m_UseFullRange)&&(!this.PreserveCollinear||!this.Pt2IsBetweenPt1AndPt3(c.Prev.Pt,c.Pt,c.Next.Pt)))b=null,c.Prev.Next=c.Next,c=c.Next.Prev=c.Prev;else if(c==b)break;else null===b&&(b=c),c=c.Next}a.Pts=c};d.Clipper.prototype.DupOutPt=function(a,b){var c=new d.OutPt;c.Pt.X=a.Pt.X;c.Pt.Y=a.Pt.Y;c.Idx=a.Idx;b?(c.Next=a.Next,c.Prev=a,a.Next.Prev=c,a.Next=c):(c.Prev=a.Prev,c.Next=a,a.Prev.Next=c,a.Prev=c);return c};d.Clipper.prototype.GetOverlap=
function(a,b,c,e,d){a<b?c<e?(d.Left=Math.max(a,c),d.Right=Math.min(b,e)):(d.Left=Math.max(a,e),d.Right=Math.min(b,c)):c<e?(d.Left=Math.max(b,c),d.Right=Math.min(a,e)):(d.Left=Math.max(b,e),d.Right=Math.min(a,c));return d.Left<d.Right};d.Clipper.prototype.JoinHorz=function(a,b,c,e,f,g){var h=a.Pt.X>b.Pt.X?d.Direction.dRightToLeft:d.Direction.dLeftToRight;e=c.Pt.X>e.Pt.X?d.Direction.dRightToLeft:d.Direction.dLeftToRight;if(h==e)return!1;if(h==d.Direction.dLeftToRight){for(;a.Next.Pt.X<=f.X&&a.Next.Pt.X>=
a.Pt.X&&a.Next.Pt.Y==f.Y;)a=a.Next;g&&a.Pt.X!=f.X&&(a=a.Next);b=this.DupOutPt(a,!g);d.IntPoint.op_Inequality(b.Pt,f)&&(a=b,a.Pt.X=f.X,a.Pt.Y=f.Y,b=this.DupOutPt(a,!g))}else{for(;a.Next.Pt.X>=f.X&&a.Next.Pt.X<=a.Pt.X&&a.Next.Pt.Y==f.Y;)a=a.Next;g||a.Pt.X==f.X||(a=a.Next);b=this.DupOutPt(a,g);d.IntPoint.op_Inequality(b.Pt,f)&&(a=b,a.Pt.X=f.X,a.Pt.Y=f.Y,b=this.DupOutPt(a,g))}if(e==d.Direction.dLeftToRight){for(;c.Next.Pt.X<=f.X&&c.Next.Pt.X>=c.Pt.X&&c.Next.Pt.Y==f.Y;)c=c.Next;g&&c.Pt.X!=f.X&&(c=c.Next);
e=this.DupOutPt(c,!g);d.IntPoint.op_Inequality(e.Pt,f)&&(c=e,c.Pt.X=f.X,c.Pt.Y=f.Y,e=this.DupOutPt(c,!g))}else{for(;c.Next.Pt.X>=f.X&&c.Next.Pt.X<=c.Pt.X&&c.Next.Pt.Y==f.Y;)c=c.Next;g||c.Pt.X==f.X||(c=c.Next);e=this.DupOutPt(c,g);d.IntPoint.op_Inequality(e.Pt,f)&&(c=e,c.Pt.X=f.X,c.Pt.Y=f.Y,e=this.DupOutPt(c,g))}h==d.Direction.dLeftToRight==g?(a.Prev=c,c.Next=a,b.Next=e,e.Prev=b):(a.Next=c,c.Prev=a,b.Prev=e,e.Next=b);return!0};d.Clipper.prototype.JoinPoints=function(a,b,c){var e=a.OutPt1,f=new d.OutPt,
g=a.OutPt2,h=new d.OutPt;if((h=a.OutPt1.Pt.Y==a.OffPt.Y)&&d.IntPoint.op_Equality(a.OffPt,a.OutPt1.Pt)&&d.IntPoint.op_Equality(a.OffPt,a.OutPt2.Pt)){for(f=a.OutPt1.Next;f!=e&&d.IntPoint.op_Equality(f.Pt,a.OffPt);)f=f.Next;f=f.Pt.Y>a.OffPt.Y;for(h=a.OutPt2.Next;h!=g&&d.IntPoint.op_Equality(h.Pt,a.OffPt);)h=h.Next;if(f==h.Pt.Y>a.OffPt.Y)return!1;f?(f=this.DupOutPt(e,!1),h=this.DupOutPt(g,!0),e.Prev=g,g.Next=e,f.Next=h,h.Prev=f):(f=this.DupOutPt(e,!0),h=this.DupOutPt(g,!1),e.Next=g,g.Prev=e,f.Prev=h,
h.Next=f);a.OutPt1=e;a.OutPt2=f;return!0}if(h){for(f=e;e.Prev.Pt.Y==e.Pt.Y&&e.Prev!=f&&e.Prev!=g;)e=e.Prev;for(;f.Next.Pt.Y==f.Pt.Y&&f.Next!=e&&f.Next!=g;)f=f.Next;if(f.Next==e||f.Next==g)return!1;for(h=g;g.Prev.Pt.Y==g.Pt.Y&&g.Prev!=h&&g.Prev!=f;)g=g.Prev;for(;h.Next.Pt.Y==h.Pt.Y&&h.Next!=g&&h.Next!=e;)h=h.Next;if(h.Next==g||h.Next==e)return!1;c={Left:null,Right:null};if(!this.GetOverlap(e.Pt.X,f.Pt.X,g.Pt.X,h.Pt.X,c))return!1;b=c.Left;var l=c.Right;c=new d.IntPoint;e.Pt.X>=b&&e.Pt.X<=l?(c.X=e.Pt.X,
c.Y=e.Pt.Y,b=e.Pt.X>f.Pt.X):g.Pt.X>=b&&g.Pt.X<=l?(c.X=g.Pt.X,c.Y=g.Pt.Y,b=g.Pt.X>h.Pt.X):f.Pt.X>=b&&f.Pt.X<=l?(c.X=f.Pt.X,c.Y=f.Pt.Y,b=f.Pt.X>e.Pt.X):(c.X=h.Pt.X,c.Y=h.Pt.Y,b=h.Pt.X>g.Pt.X);a.OutPt1=e;a.OutPt2=g;return this.JoinHorz(e,f,g,h,c,b)}for(f=e.Next;d.IntPoint.op_Equality(f.Pt,e.Pt)&&f!=e;)f=f.Next;if(l=f.Pt.Y>e.Pt.Y||!d.ClipperBase.SlopesEqual(e.Pt,f.Pt,a.OffPt,this.m_UseFullRange)){for(f=e.Prev;d.IntPoint.op_Equality(f.Pt,e.Pt)&&f!=e;)f=f.Prev;if(f.Pt.Y>e.Pt.Y||!d.ClipperBase.SlopesEqual(e.Pt,
f.Pt,a.OffPt,this.m_UseFullRange))return!1}for(h=g.Next;d.IntPoint.op_Equality(h.Pt,g.Pt)&&h!=g;)h=h.Next;var k=h.Pt.Y>g.Pt.Y||!d.ClipperBase.SlopesEqual(g.Pt,h.Pt,a.OffPt,this.m_UseFullRange);if(k){for(h=g.Prev;d.IntPoint.op_Equality(h.Pt,g.Pt)&&h!=g;)h=h.Prev;if(h.Pt.Y>g.Pt.Y||!d.ClipperBase.SlopesEqual(g.Pt,h.Pt,a.OffPt,this.m_UseFullRange))return!1}if(f==e||h==g||f==h||b==c&&l==k)return!1;l?(f=this.DupOutPt(e,!1),h=this.DupOutPt(g,!0),e.Prev=g,g.Next=e,f.Next=h,h.Prev=f):(f=this.DupOutPt(e,!0),
h=this.DupOutPt(g,!1),e.Next=g,g.Prev=e,f.Prev=h,h.Next=f);a.OutPt1=e;a.OutPt2=f;return!0};d.Clipper.GetBounds=function(a){for(var b=0,c=a.length;b<c&&0==a[b].length;)b++;if(b==c)return new d.IntRect(0,0,0,0);var e=new d.IntRect;e.left=a[b][0].X;e.right=e.left;e.top=a[b][0].Y;for(e.bottom=e.top;b<c;b++)for(var f=0,g=a[b].length;f<g;f++)a[b][f].X<e.left?e.left=a[b][f].X:a[b][f].X>e.right&&(e.right=a[b][f].X),a[b][f].Y<e.top?e.top=a[b][f].Y:a[b][f].Y>e.bottom&&(e.bottom=a[b][f].Y);return e};d.Clipper.prototype.GetBounds2=
function(a){var b=a,c=new d.IntRect;c.left=a.Pt.X;c.right=a.Pt.X;c.top=a.Pt.Y;c.bottom=a.Pt.Y;for(a=a.Next;a!=b;)a.Pt.X<c.left&&(c.left=a.Pt.X),a.Pt.X>c.right&&(c.right=a.Pt.X),a.Pt.Y<c.top&&(c.top=a.Pt.Y),a.Pt.Y>c.bottom&&(c.bottom=a.Pt.Y),a=a.Next;return c};d.Clipper.PointInPolygon=function(a,b){var c=0,e=b.length;if(3>e)return 0;for(var d=b[0],g=1;g<=e;++g){var h=g==e?b[0]:b[g];if(h.Y==a.Y&&(h.X==a.X||d.Y==a.Y&&h.X>a.X==d.X<a.X))return-1;if(d.Y<a.Y!=h.Y<a.Y)if(d.X>=a.X)if(h.X>a.X)c=1-c;else{var l=
(d.X-a.X)*(h.Y-a.Y)-(h.X-a.X)*(d.Y-a.Y);if(0==l)return-1;0<l==h.Y>d.Y&&(c=1-c)}else if(h.X>a.X){l=(d.X-a.X)*(h.Y-a.Y)-(h.X-a.X)*(d.Y-a.Y);if(0==l)return-1;0<l==h.Y>d.Y&&(c=1-c)}d=h}return c};d.Clipper.prototype.PointInPolygon=function(a,b){for(var c=0,e=b;;){var d=b.Pt.X,g=b.Pt.Y,h=b.Next.Pt.X,l=b.Next.Pt.Y;if(l==a.Y&&(h==a.X||g==a.Y&&h>a.X==d<a.X))return-1;if(g<a.Y!=l<a.Y)if(d>=a.X)if(h>a.X)c=1-c;else{d=(d-a.X)*(l-a.Y)-(h-a.X)*(g-a.Y);if(0==d)return-1;0<d==l>g&&(c=1-c)}else if(h>a.X){d=(d-a.X)*(l-
a.Y)-(h-a.X)*(g-a.Y);if(0==d)return-1;0<d==l>g&&(c=1-c)}b=b.Next;if(e==b)break}return c};d.Clipper.prototype.Poly2ContainsPoly1=function(a,b){var c=a;do{var e=this.PointInPolygon(c.Pt,b);if(0<=e)return 0!=e;c=c.Next}while(c!=a);return!0};d.Clipper.prototype.FixupFirstLefts1=function(a,b){for(var c=0,e=this.m_PolyOuts.length;c<e;c++){var d=this.m_PolyOuts[c];null!==d.Pts&&d.FirstLeft==a&&this.Poly2ContainsPoly1(d.Pts,b.Pts)&&(d.FirstLeft=b)}};d.Clipper.prototype.FixupFirstLefts2=function(a,b){for(var c=
0,e=this.m_PolyOuts,d=e.length,g=e[c];c<d;c++,g=e[c])g.FirstLeft==a&&(g.FirstLeft=b)};d.Clipper.ParseFirstLeft=function(a){for(;null!=a&&null==a.Pts;)a=a.FirstLeft;return a};d.Clipper.prototype.JoinCommonEdges=function(){for(var a=0,b=this.m_Joins.length;a<b;a++){var c=this.m_Joins[a],e=this.GetOutRec(c.OutPt1.Idx),f=this.GetOutRec(c.OutPt2.Idx);if(null!=e.Pts&&null!=f.Pts){var g;g=e==f?e:this.Param1RightOfParam2(e,f)?f:this.Param1RightOfParam2(f,e)?e:this.GetLowermostRec(e,f);if(this.JoinPoints(c,
e,f))if(e==f){e.Pts=c.OutPt1;e.BottomPt=null;f=this.CreateOutRec();f.Pts=c.OutPt2;this.UpdateOutPtIdxs(f);if(this.m_UsingPolyTree){g=0;for(var h=this.m_PolyOuts.length;g<h-1;g++){var l=this.m_PolyOuts[g];null!=l.Pts&&d.Clipper.ParseFirstLeft(l.FirstLeft)==e&&l.IsHole!=e.IsHole&&this.Poly2ContainsPoly1(l.Pts,c.OutPt2)&&(l.FirstLeft=f)}}this.Poly2ContainsPoly1(f.Pts,e.Pts)?(f.IsHole=!e.IsHole,f.FirstLeft=e,this.m_UsingPolyTree&&this.FixupFirstLefts2(f,e),(f.IsHole^this.ReverseSolution)==0<this.Area(f)&&
this.ReversePolyPtLinks(f.Pts)):this.Poly2ContainsPoly1(e.Pts,f.Pts)?(f.IsHole=e.IsHole,e.IsHole=!f.IsHole,f.FirstLeft=e.FirstLeft,e.FirstLeft=f,this.m_UsingPolyTree&&this.FixupFirstLefts2(e,f),(e.IsHole^this.ReverseSolution)==0<this.Area(e)&&this.ReversePolyPtLinks(e.Pts)):(f.IsHole=e.IsHole,f.FirstLeft=e.FirstLeft,this.m_UsingPolyTree&&this.FixupFirstLefts1(e,f))}else f.Pts=null,f.BottomPt=null,f.Idx=e.Idx,e.IsHole=g.IsHole,g==f&&(e.FirstLeft=f.FirstLeft),f.FirstLeft=e,this.m_UsingPolyTree&&this.FixupFirstLefts2(f,
e)}}};d.Clipper.prototype.UpdateOutPtIdxs=function(a){var b=a.Pts;do b.Idx=a.Idx,b=b.Prev;while(b!=a.Pts)};d.Clipper.prototype.DoSimplePolygons=function(){for(var a=0;a<this.m_PolyOuts.length;){var b=this.m_PolyOuts[a++],c=b.Pts;if(null!==c){do{for(var e=c.Next;e!=b.Pts;){if(d.IntPoint.op_Equality(c.Pt,e.Pt)&&e.Next!=c&&e.Prev!=c){var f=c.Prev,g=e.Prev;c.Prev=g;g.Next=c;e.Prev=f;f.Next=e;b.Pts=c;f=this.CreateOutRec();f.Pts=e;this.UpdateOutPtIdxs(f);this.Poly2ContainsPoly1(f.Pts,b.Pts)?(f.IsHole=!b.IsHole,
f.FirstLeft=b):this.Poly2ContainsPoly1(b.Pts,f.Pts)?(f.IsHole=b.IsHole,b.IsHole=!f.IsHole,f.FirstLeft=b.FirstLeft,b.FirstLeft=f):(f.IsHole=b.IsHole,f.FirstLeft=b.FirstLeft);e=c}e=e.Next}c=c.Next}while(c!=b.Pts)}}};d.Clipper.Area=function(a){var b=a.length;if(3>b)return 0;for(var c=0,e=0,d=b-1;e<b;++e)c+=(a[d].X+a[e].X)*(a[d].Y-a[e].Y),d=e;return 0.5*-c};d.Clipper.prototype.Area=function(a){var b=a.Pts;if(null==b)return 0;var c=0;do c+=(b.Prev.Pt.X+b.Pt.X)*(b.Prev.Pt.Y-b.Pt.Y),b=b.Next;while(b!=a.Pts);
return 0.5*c};d.Clipper.SimplifyPolygon=function(a,b){var c=[],e=new d.Clipper(0);e.StrictlySimple=!0;e.AddPath(a,d.PolyType.ptSubject,!0);e.Execute(d.ClipType.ctUnion,c,b,b);return c};d.Clipper.SimplifyPolygons=function(a,b){"undefined"==typeof b&&(b=d.PolyFillType.pftEvenOdd);var c=[],e=new d.Clipper(0);e.StrictlySimple=!0;e.AddPaths(a,d.PolyType.ptSubject,!0);e.Execute(d.ClipType.ctUnion,c,b,b);return c};d.Clipper.DistanceSqrd=function(a,b){var c=a.X-b.X,e=a.Y-b.Y;return c*c+e*e};d.Clipper.DistanceFromLineSqrd=
function(a,b,c){var e=b.Y-c.Y;c=c.X-b.X;b=e*b.X+c*b.Y;b=e*a.X+c*a.Y-b;return b*b/(e*e+c*c)};d.Clipper.SlopesNearCollinear=function(a,b,c,e){return d.Clipper.DistanceFromLineSqrd(b,a,c)<e};d.Clipper.PointsAreClose=function(a,b,c){var e=a.X-b.X;a=a.Y-b.Y;return e*e+a*a<=c};d.Clipper.ExcludeOp=function(a){var b=a.Prev;b.Next=a.Next;a.Next.Prev=b;b.Idx=0;return b};d.Clipper.CleanPolygon=function(a,b){"undefined"==typeof b&&(b=1.415);var c=a.length;if(0==c)return[];for(var e=Array(c),f=0;f<c;++f)e[f]=
new d.OutPt;for(f=0;f<c;++f)e[f].Pt=a[f],e[f].Next=e[(f+1)%c],e[f].Next.Prev=e[f],e[f].Idx=0;f=b*b;for(e=e[0];0==e.Idx&&e.Next!=e.Prev;)d.Clipper.PointsAreClose(e.Pt,e.Prev.Pt,f)?(e=d.Clipper.ExcludeOp(e),c--):d.Clipper.PointsAreClose(e.Prev.Pt,e.Next.Pt,f)?(d.Clipper.ExcludeOp(e.Next),e=d.Clipper.ExcludeOp(e),c-=2):d.Clipper.SlopesNearCollinear(e.Prev.Pt,e.Pt,e.Next.Pt,f)?(e=d.Clipper.ExcludeOp(e),c--):(e.Idx=1,e=e.Next);3>c&&(c=0);for(var g=Array(c),f=0;f<c;++f)g[f]=new d.IntPoint(e.Pt),e=e.Next;
return g};d.Clipper.CleanPolygons=function(a,b){for(var c=Array(a.length),e=0,f=a.length;e<f;e++)c[e]=d.Clipper.CleanPolygon(a[e],b);return c};d.Clipper.Minkowski=function(a,b,c,e){var f=e?1:0,g=a.length,h=b.length;e=[];if(c)for(c=0;c<h;c++){for(var l=Array(g),k=0,n=a.length,m=a[k];k<n;k++,m=a[k])l[k]=new d.IntPoint(b[c].X+m.X,b[c].Y+m.Y);e.push(l)}else for(c=0;c<h;c++){l=Array(g);k=0;n=a.length;for(m=a[k];k<n;k++,m=a[k])l[k]=new d.IntPoint(b[c].X-m.X,b[c].Y-m.Y);e.push(l)}a=[];for(c=0;c<h-1+f;c++)for(k=
0;k<g;k++)b=[],b.push(e[c%h][k%g]),b.push(e[(c+1)%h][k%g]),b.push(e[(c+1)%h][(k+1)%g]),b.push(e[c%h][(k+1)%g]),d.Clipper.Orientation(b)||b.reverse(),a.push(b);f=new d.Clipper(0);f.AddPaths(a,d.PolyType.ptSubject,!0);f.Execute(d.ClipType.ctUnion,e,d.PolyFillType.pftNonZero,d.PolyFillType.pftNonZero);return e};d.Clipper.MinkowskiSum=function(){var a=arguments,b=a.length;if(3==b){var c=a[0],e=a[2];return d.Clipper.Minkowski(c,a[1],!0,e)}if(4==b){for(var c=a[0],f=a[1],b=a[2],e=a[3],a=new d.Clipper,g,
h=0,l=f.length;h<l;++h)g=d.Clipper.Minkowski(c,f[h],!0,e),a.AddPaths(g,d.PolyType.ptSubject,!0);e&&a.AddPaths(f,d.PolyType.ptClip,!0);c=new d.Paths;a.Execute(d.ClipType.ctUnion,c,b,b);return c}};d.Clipper.MinkowskiDiff=function(a,b,c){return d.Clipper.Minkowski(a,b,!1,c)};d.Clipper.PolyTreeToPaths=function(a){var b=[];d.Clipper.AddPolyNodeToPaths(a,d.Clipper.NodeType.ntAny,b);return b};d.Clipper.AddPolyNodeToPaths=function(a,b,c){var e=!0;switch(b){case d.Clipper.NodeType.ntOpen:return;case d.Clipper.NodeType.ntClosed:e=
!a.IsOpen}0<a.m_polygon.length&&e&&c.push(a.m_polygon);e=0;a=a.Childs();for(var f=a.length,g=a[e];e<f;e++,g=a[e])d.Clipper.AddPolyNodeToPaths(g,b,c)};d.Clipper.OpenPathsFromPolyTree=function(a){for(var b=new d.Paths,c=0,e=a.ChildCount();c<e;c++)a.Childs()[c].IsOpen&&b.push(a.Childs()[c].m_polygon);return b};d.Clipper.ClosedPathsFromPolyTree=function(a){var b=new d.Paths;d.Clipper.AddPolyNodeToPaths(a,d.Clipper.NodeType.ntClosed,b);return b};K(d.Clipper,d.ClipperBase);d.Clipper.NodeType={ntAny:0,ntOpen:1,
ntClosed:2};d.ClipperOffset=function(a,b){"undefined"==typeof a&&(a=2);"undefined"==typeof b&&(b=d.ClipperOffset.def_arc_tolerance);this.m_destPolys=new d.Paths;this.m_srcPoly=new d.Path;this.m_destPoly=new d.Path;this.m_normals=[];this.m_StepsPerRad=this.m_miterLim=this.m_cos=this.m_sin=this.m_sinA=this.m_delta=0;this.m_lowest=new d.IntPoint;this.m_polyNodes=new d.PolyNode;this.MiterLimit=a;this.ArcTolerance=b;this.m_lowest.X=-1};d.ClipperOffset.two_pi=6.28318530717959;d.ClipperOffset.def_arc_tolerance=
0.25;d.ClipperOffset.prototype.Clear=function(){d.Clear(this.m_polyNodes.Childs());this.m_lowest.X=-1};d.ClipperOffset.Round=d.Clipper.Round;d.ClipperOffset.prototype.AddPath=function(a,b,c){var e=a.length-1;if(!(0>e)){var f=new d.PolyNode;f.m_jointype=b;f.m_endtype=c;if(c==d.EndType.etClosedLine||c==d.EndType.etClosedPolygon)for(;0<e&&d.IntPoint.op_Equality(a[0],a[e]);)e--;f.m_polygon.push(a[0]);var g=0;b=0;for(var h=1;h<=e;h++)d.IntPoint.op_Inequality(f.m_polygon[g],a[h])&&(g++,f.m_polygon.push(a[h]),
a[h].Y>f.m_polygon[b].Y||a[h].Y==f.m_polygon[b].Y&&a[h].X<f.m_polygon[b].X)&&(b=g);if(!(c==d.EndType.etClosedPolygon&&2>g||c!=d.EndType.etClosedPolygon&&0>g)&&(this.m_polyNodes.AddChild(f),c==d.EndType.etClosedPolygon))if(0>this.m_lowest.X)this.m_lowest=new d.IntPoint(0,b);else if(a=this.m_polyNodes.Childs()[this.m_lowest.X].m_polygon[this.m_lowest.Y],f.m_polygon[b].Y>a.Y||f.m_polygon[b].Y==a.Y&&f.m_polygon[b].X<a.X)this.m_lowest=new d.IntPoint(this.m_polyNodes.ChildCount()-1,b)}};d.ClipperOffset.prototype.AddPaths=
function(a,b,c){for(var e=0,d=a.length;e<d;e++)this.AddPath(a[e],b,c)};d.ClipperOffset.prototype.FixOrientations=function(){if(0<=this.m_lowest.X&&!d.Clipper.Orientation(this.m_polyNodes.Childs()[this.m_lowest.X].m_polygon))for(var a=0;a<this.m_polyNodes.ChildCount();a++){var b=this.m_polyNodes.Childs()[a];(b.m_endtype==d.EndType.etClosedPolygon||b.m_endtype==d.EndType.etClosedLine&&d.Clipper.Orientation(b.m_polygon))&&b.m_polygon.reverse()}else for(a=0;a<this.m_polyNodes.ChildCount();a++)b=this.m_polyNodes.Childs()[a],
b.m_endtype!=d.EndType.etClosedLine||d.Clipper.Orientation(b.m_polygon)||b.m_polygon.reverse()};d.ClipperOffset.GetUnitNormal=function(a,b){var c=b.X-a.X,e=b.Y-a.Y;if(0==c&&0==e)return new d.DoublePoint(0,0);var f=1/Math.sqrt(c*c+e*e);return new d.DoublePoint(e*f,-(c*f))};d.ClipperOffset.prototype.DoOffset=function(a){this.m_destPolys=[];this.m_delta=a;if(d.ClipperBase.near_zero(a))for(var b=0;b<this.m_polyNodes.ChildCount();b++){var c=this.m_polyNodes.Childs()[b];c.m_endtype==d.EndType.etClosedPolygon&&
this.m_destPolys.push(c.m_polygon)}else{this.m_miterLim=2<this.MiterLimit?2/(this.MiterLimit*this.MiterLimit):0.5;var b=0>=this.ArcTolerance?d.ClipperOffset.def_arc_tolerance:this.ArcTolerance>Math.abs(a)*d.ClipperOffset.def_arc_tolerance?Math.abs(a)*d.ClipperOffset.def_arc_tolerance:this.ArcTolerance,e=3.14159265358979/Math.acos(1-b/Math.abs(a));this.m_sin=Math.sin(d.ClipperOffset.two_pi/e);this.m_cos=Math.cos(d.ClipperOffset.two_pi/e);this.m_StepsPerRad=e/d.ClipperOffset.two_pi;0>a&&(this.m_sin=
-this.m_sin);for(b=0;b<this.m_polyNodes.ChildCount();b++){c=this.m_polyNodes.Childs()[b];this.m_srcPoly=c.m_polygon;var f=this.m_srcPoly.length;if(!(0==f||0>=a&&(3>f||c.m_endtype!=d.EndType.etClosedPolygon))){this.m_destPoly=[];if(1==f)if(c.m_jointype==d.JoinType.jtRound)for(var c=1,f=0,g=1;g<=e;g++){this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[0].X+c*a),d.ClipperOffset.Round(this.m_srcPoly[0].Y+f*a)));var h=c,c=c*this.m_cos-this.m_sin*f,f=h*this.m_sin+f*this.m_cos}else for(f=
c=-1,g=0;4>g;++g)this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[0].X+c*a),d.ClipperOffset.Round(this.m_srcPoly[0].Y+f*a))),0>c?c=1:0>f?f=1:c=-1;else{for(g=this.m_normals.length=0;g<f-1;g++)this.m_normals.push(d.ClipperOffset.GetUnitNormal(this.m_srcPoly[g],this.m_srcPoly[g+1]));c.m_endtype==d.EndType.etClosedLine||c.m_endtype==d.EndType.etClosedPolygon?this.m_normals.push(d.ClipperOffset.GetUnitNormal(this.m_srcPoly[f-1],this.m_srcPoly[0])):this.m_normals.push(new d.DoublePoint(this.m_normals[f-
2]));if(c.m_endtype==d.EndType.etClosedPolygon)for(h=f-1,g=0;g<f;g++)h=this.OffsetPoint(g,h,c.m_jointype);else if(c.m_endtype==d.EndType.etClosedLine){h=f-1;for(g=0;g<f;g++)h=this.OffsetPoint(g,h,c.m_jointype);this.m_destPolys.push(this.m_destPoly);this.m_destPoly=[];h=this.m_normals[f-1];for(g=f-1;0<g;g--)this.m_normals[g]=new d.DoublePoint(-this.m_normals[g-1].X,-this.m_normals[g-1].Y);this.m_normals[0]=new d.DoublePoint(-h.X,-h.Y);h=0;for(g=f-1;0<=g;g--)h=this.OffsetPoint(g,h,c.m_jointype)}else{h=
0;for(g=1;g<f-1;++g)h=this.OffsetPoint(g,h,c.m_jointype);c.m_endtype==d.EndType.etOpenButt?(g=f-1,h=new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[g].X+this.m_normals[g].X*a),d.ClipperOffset.Round(this.m_srcPoly[g].Y+this.m_normals[g].Y*a)),this.m_destPoly.push(h),h=new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[g].X-this.m_normals[g].X*a),d.ClipperOffset.Round(this.m_srcPoly[g].Y-this.m_normals[g].Y*a)),this.m_destPoly.push(h)):(g=f-1,h=f-2,this.m_sinA=0,this.m_normals[g]=new d.DoublePoint(-this.m_normals[g].X,
-this.m_normals[g].Y),c.m_endtype==d.EndType.etOpenSquare?this.DoSquare(g,h):this.DoRound(g,h));for(g=f-1;0<g;g--)this.m_normals[g]=new d.DoublePoint(-this.m_normals[g-1].X,-this.m_normals[g-1].Y);this.m_normals[0]=new d.DoublePoint(-this.m_normals[1].X,-this.m_normals[1].Y);h=f-1;for(g=h-1;0<g;--g)h=this.OffsetPoint(g,h,c.m_jointype);c.m_endtype==d.EndType.etOpenButt?(h=new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[0].X-this.m_normals[0].X*a),d.ClipperOffset.Round(this.m_srcPoly[0].Y-this.m_normals[0].Y*
a)),this.m_destPoly.push(h),h=new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[0].X+this.m_normals[0].X*a),d.ClipperOffset.Round(this.m_srcPoly[0].Y+this.m_normals[0].Y*a)),this.m_destPoly.push(h)):(this.m_sinA=0,c.m_endtype==d.EndType.etOpenSquare?this.DoSquare(0,1):this.DoRound(0,1))}}this.m_destPolys.push(this.m_destPoly)}}}};d.ClipperOffset.prototype.Execute=function(){var a=arguments;if(a[0]instanceof d.PolyTree)if(b=a[0],c=a[1],b.Clear(),this.FixOrientations(),this.DoOffset(c),a=new d.Clipper(0),
a.AddPaths(this.m_destPolys,d.PolyType.ptSubject,!0),0<c)a.Execute(d.ClipType.ctUnion,b,d.PolyFillType.pftPositive,d.PolyFillType.pftPositive);else if(c=d.Clipper.GetBounds(this.m_destPolys),e=new d.Path,e.push(new d.IntPoint(c.left-10,c.bottom+10)),e.push(new d.IntPoint(c.right+10,c.bottom+10)),e.push(new d.IntPoint(c.right+10,c.top-10)),e.push(new d.IntPoint(c.left-10,c.top-10)),a.AddPath(e,d.PolyType.ptSubject,!0),a.ReverseSolution=!0,a.Execute(d.ClipType.ctUnion,b,d.PolyFillType.pftNegative,d.PolyFillType.pftNegative),
1==b.ChildCount()&&0<b.Childs()[0].ChildCount())for(a=b.Childs()[0],b.Childs()[0]=a.Childs()[0],c=1;c<a.ChildCount();c++)b.AddChild(a.Childs()[c]);else b.Clear();else{var b=a[0],c=a[1];d.Clear(b);this.FixOrientations();this.DoOffset(c);a=new d.Clipper(0);a.AddPaths(this.m_destPolys,d.PolyType.ptSubject,!0);if(0<c)a.Execute(d.ClipType.ctUnion,b,d.PolyFillType.pftPositive,d.PolyFillType.pftPositive);else{var c=d.Clipper.GetBounds(this.m_destPolys),e=new d.Path;e.push(new d.IntPoint(c.left-10,c.bottom+
10));e.push(new d.IntPoint(c.right+10,c.bottom+10));e.push(new d.IntPoint(c.right+10,c.top-10));e.push(new d.IntPoint(c.left-10,c.top-10));a.AddPath(e,d.PolyType.ptSubject,!0);a.ReverseSolution=!0;a.Execute(d.ClipType.ctUnion,b,d.PolyFillType.pftNegative,d.PolyFillType.pftNegative);0<b.length&&b.splice(0,1)}}};d.ClipperOffset.prototype.OffsetPoint=function(a,b,c){this.m_sinA=this.m_normals[b].X*this.m_normals[a].Y-this.m_normals[a].X*this.m_normals[b].Y;if(5E-5>this.m_sinA&&-5E-5<this.m_sinA)return b;
1<this.m_sinA?this.m_sinA=1:-1>this.m_sinA&&(this.m_sinA=-1);if(0>this.m_sinA*this.m_delta)this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_normals[b].X*this.m_delta),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_normals[b].Y*this.m_delta))),this.m_destPoly.push(new d.IntPoint(this.m_srcPoly[a])),this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_normals[a].X*this.m_delta),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_normals[a].Y*
this.m_delta)));else switch(c){case d.JoinType.jtMiter:c=1+(this.m_normals[a].X*this.m_normals[b].X+this.m_normals[a].Y*this.m_normals[b].Y);c>=this.m_miterLim?this.DoMiter(a,b,c):this.DoSquare(a,b);break;case d.JoinType.jtSquare:this.DoSquare(a,b);break;case d.JoinType.jtRound:this.DoRound(a,b)}return a};d.ClipperOffset.prototype.DoSquare=function(a,b){var c=Math.tan(Math.atan2(this.m_sinA,this.m_normals[b].X*this.m_normals[a].X+this.m_normals[b].Y*this.m_normals[a].Y)/4);this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+
this.m_delta*(this.m_normals[b].X-this.m_normals[b].Y*c)),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_delta*(this.m_normals[b].Y+this.m_normals[b].X*c))));this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_delta*(this.m_normals[a].X+this.m_normals[a].Y*c)),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_delta*(this.m_normals[a].Y-this.m_normals[a].X*c))))};d.ClipperOffset.prototype.DoMiter=function(a,b,c){c=this.m_delta/c;this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+
(this.m_normals[b].X+this.m_normals[a].X)*c),d.ClipperOffset.Round(this.m_srcPoly[a].Y+(this.m_normals[b].Y+this.m_normals[a].Y)*c)))};d.ClipperOffset.prototype.DoRound=function(a,b){for(var c=Math.atan2(this.m_sinA,this.m_normals[b].X*this.m_normals[a].X+this.m_normals[b].Y*this.m_normals[a].Y),c=d.Cast_Int32(d.ClipperOffset.Round(this.m_StepsPerRad*Math.abs(c))),e=this.m_normals[b].X,f=this.m_normals[b].Y,g,h=0;h<c;++h)this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+
e*this.m_delta),d.ClipperOffset.Round(this.m_srcPoly[a].Y+f*this.m_delta))),g=e,e=e*this.m_cos-this.m_sin*f,f=g*this.m_sin+f*this.m_cos;this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_normals[a].X*this.m_delta),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_normals[a].Y*this.m_delta)))};d.Error=function(a){try{throw Error(a);}catch(b){alert(b.message)}};d.JS={};d.JS.AreaOfPolygon=function(a,b){b||(b=1);return d.Clipper.Area(a)/(b*b)};d.JS.AreaOfPolygons=function(a,
b){b||(b=1);for(var c=0,e=0;e<a.length;e++)c+=d.Clipper.Area(a[e]);return c/(b*b)};d.JS.BoundsOfPath=function(a,b){return d.JS.BoundsOfPaths([a],b)};d.JS.BoundsOfPaths=function(a,b){b||(b=1);var c=d.Clipper.GetBounds(a);c.left/=b;c.bottom/=b;c.right/=b;c.top/=b;return c};d.JS.Clean=function(a,b){if(!(a instanceof Array))return[];var c=a[0]instanceof Array;a=d.JS.Clone(a);if("number"!=typeof b||null===b)return d.Error("Delta is not a number in Clean()."),a;if(0===a.length||1==a.length&&0===a[0].length||
0>b)return a;c||(a=[a]);for(var e=a.length,f,g,h,l,k,n,m,p=[],q=0;q<e;q++)if(g=a[q],f=g.length,0!==f)if(3>f)h=g,p.push(h);else{h=g;l=b*b;k=g[0];for(m=n=1;m<f;m++)(g[m].X-k.X)*(g[m].X-k.X)+(g[m].Y-k.Y)*(g[m].Y-k.Y)<=l||(h[n]=g[m],k=g[m],n++);k=g[n-1];(g[0].X-k.X)*(g[0].X-k.X)+(g[0].Y-k.Y)*(g[0].Y-k.Y)<=l&&n--;n<f&&h.splice(n,f-n);h.length&&p.push(h)}!c&&p.length?p=p[0]:c||0!==p.length?c&&0===p.length&&(p=[[]]):p=[];return p};d.JS.Clone=function(a){if(!(a instanceof Array)||0===a.length)return[];if(1==
a.length&&0===a[0].length)return[[]];var b=a[0]instanceof Array;b||(a=[a]);var c=a.length,e,d,g,h,l=Array(c);for(d=0;d<c;d++){e=a[d].length;h=Array(e);for(g=0;g<e;g++)h[g]={X:a[d][g].X,Y:a[d][g].Y};l[d]=h}b||(l=l[0]);return l};d.JS.Lighten=function(a,b){if(!(a instanceof Array))return[];if("number"!=typeof b||null===b)return d.Error("Tolerance is not a number in Lighten()."),d.JS.Clone(a);if(0===a.length||1==a.length&&0===a[0].length||0>b)return d.JS.Clone(a);a[0]instanceof Array||(a=[a]);var c,e,
f,g,h,l,k,m,p,q,r,s,t,u,v,x=a.length,y=b*b,w=[];for(c=0;c<x;c++)if(f=a[c],l=f.length,0!=l){for(g=0;1E6>g;g++){h=[];l=f.length;f[l-1].X!=f[0].X||f[l-1].Y!=f[0].Y?(r=1,f.push({X:f[0].X,Y:f[0].Y}),l=f.length):r=0;q=[];for(e=0;e<l-2;e++){k=f[e];p=f[e+1];m=f[e+2];u=k.X;v=k.Y;k=m.X-u;s=m.Y-v;if(0!==k||0!==s)t=((p.X-u)*k+(p.Y-v)*s)/(k*k+s*s),1<t?(u=m.X,v=m.Y):0<t&&(u+=k*t,v+=s*t);k=p.X-u;s=p.Y-v;m=k*k+s*s;m<=y&&(q[e+1]=1,e++)}h.push({X:f[0].X,Y:f[0].Y});for(e=1;e<l-1;e++)q[e]||h.push({X:f[e].X,Y:f[e].Y});
h.push({X:f[l-1].X,Y:f[l-1].Y});r&&f.pop();if(q.length)f=h;else break}l=h.length;h[l-1].X==h[0].X&&h[l-1].Y==h[0].Y&&h.pop();2<h.length&&w.push(h)}!a[0]instanceof Array&&(w=w[0]);"undefined"==typeof w&&(w=[[]]);return w};d.JS.PerimeterOfPath=function(a,b,c){if("undefined"==typeof a)return 0;var e=Math.sqrt,d=0,g,h,k=0,m=g=0;h=0;var n=a.length;if(2>n)return 0;b&&(a[n]=a[0],n++);for(;--n;)g=a[n],k=g.X,g=g.Y,h=a[n-1],m=h.X,h=h.Y,d+=e((k-m)*(k-m)+(g-h)*(g-h));b&&a.pop();return d/c};d.JS.PerimeterOfPaths=
function(a,b,c){c||(c=1);for(var e=0,f=0;f<a.length;f++)e+=d.JS.PerimeterOfPath(a[f],b,c);return e};d.JS.ScaleDownPath=function(a,b){var c,d;b||(b=1);for(c=a.length;c--;)d=a[c],d.X/=b,d.Y/=b};d.JS.ScaleDownPaths=function(a,b){var c,d,f;b||(b=1);for(c=a.length;c--;)for(d=a[c].length;d--;)f=a[c][d],f.X/=b,f.Y/=b};d.JS.ScaleUpPath=function(a,b){var c,d,f=Math.round;b||(b=1);for(c=a.length;c--;)d=a[c],d.X=f(d.X*b),d.Y=f(d.Y*b)};d.JS.ScaleUpPaths=function(a,b){var c,d,f,g=Math.round;b||(b=1);for(c=a.length;c--;)for(d=
a[c].length;d--;)f=a[c][d],f.X=g(f.X*b),f.Y=g(f.Y*b)};d.ExPolygons=function(){return[]};d.ExPolygon=function(){this.holes=this.outer=null};d.JS.AddOuterPolyNodeToExPolygons=function(a,b){var c=new d.ExPolygon;c.outer=a.Contour();var e=a.Childs(),f=e.length;c.holes=Array(f);var g,h,k,m,n;for(h=0;h<f;h++)for(g=e[h],c.holes[h]=g.Contour(),k=0,m=g.Childs(),n=m.length;k<n;k++)g=m[k],d.JS.AddOuterPolyNodeToExPolygons(g,b);b.push(c)};d.JS.ExPolygonsToPaths=function(a){var b,c,e,f,g=new d.Paths;b=0;for(e=
a.length;b<e;b++)for(g.push(a[b].outer),c=0,f=a[b].holes.length;c<f;c++)g.push(a[b].holes[c]);return g};d.JS.PolyTreeToExPolygons=function(a){var b=new d.ExPolygons,c,e,f;c=0;e=a.Childs();for(f=e.length;c<f;c++)a=e[c],d.JS.AddOuterPolyNodeToExPolygons(a,b);return b}})();

View file

@ -0,0 +1,37 @@
/* inspired by https://gist.github.com/1129031 */
/*global document, DOMParser*/
(function(DOMParser) {
"use strict";
var
proto = DOMParser.prototype
, nativeParse = proto.parseFromString
;
// Firefox/Opera/IE throw errors on unsupported types
try {
// WebKit returns null on unsupported types
if ((new DOMParser()).parseFromString("", "text/html")) {
// text/html parsing is natively supported
return;
}
} catch (ex) {}
proto.parseFromString = function(markup, type) {
if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
var
doc = document.implementation.createHTMLDocument("")
;
if (markup.toLowerCase().indexOf('<!doctype') > -1) {
doc.documentElement.innerHTML = markup;
}
else {
doc.body.innerHTML = markup;
}
return doc;
} else {
return nativeParse.apply(this, arguments);
}
};
}(DOMParser));

View file

@ -0,0 +1,11 @@
var isNode = typeof module !== 'undefined' && module.exports;
if (isNode) {
process.once('message', function (code) {
eval(JSON.parse(code).data);
});
} else {
self.onmessage = function (code) {
eval(code.data);
};
}

View file

@ -0,0 +1,188 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.3.2
* 2016-06-16 18:25:19
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs || (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function(node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
}
, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
, throw_outside = function(ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
, arbitrary_revoke_timeout = 1000 * 40 // in ms
, revoke = function(file) {
var revoker = function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
setTimeout(revoker, arbitrary_revoke_timeout);
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, auto_bom = function(blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
}
return blob;
}
, FileSaver = function(blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, force = type === force_saveable_type
, object_url
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function() {
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
var popup = view.open(url, '_blank');
if(!popup) view.location.href = url;
url=undefined; // release reference before dispatching
filesaver.readyState = filesaver.DONE;
dispatch_all();
};
reader.readAsDataURL(blob);
filesaver.readyState = filesaver.INIT;
return;
}
// don't create more object URLs than needed
if (!object_url) {
object_url = get_URL().createObjectURL(blob);
}
if (force) {
view.location.href = object_url;
} else {
var opened = view.open(object_url, "_blank");
if (!opened) {
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
view.location.href = object_url;
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
;
filesaver.readyState = filesaver.INIT;
if (can_use_save_link) {
object_url = get_URL().createObjectURL(blob);
setTimeout(function() {
save_link.href = object_url;
save_link.download = name;
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
});
return;
}
fs_error();
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name, no_auto_bom) {
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
}
;
// IE 10+ (native saveAs)
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function(blob, name, no_auto_bom) {
name = name || blob.name || "download";
if (!no_auto_bom) {
blob = auto_bom(blob);
}
return navigator.msSaveOrOpenBlob(blob, name);
};
}
FS_proto.abort = function(){};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this.content
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
define("FileSaver.js", function() {
return saveAs;
});
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
var JSON;if(!JSON){JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==="string"){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());

View file

@ -0,0 +1,156 @@
// matrix utility from SvgPath
// https://github.com/fontello/svgpath
'use strict';
function Matrix() {
if (!(this instanceof Matrix)) { return new Matrix(); }
this.queue = []; // list of matrixes to apply
this.cache = null; // combined matrix cache
}
(typeof window !== 'undefined' ? window : self).Matrix = Matrix;
// combine 2 matrixes
// m1, m2 - [a, b, c, d, e, g]
//
Matrix.prototype.combine = function(m1, m2) {
return [
m1[0] * m2[0] + m1[2] * m2[1],
m1[1] * m2[0] + m1[3] * m2[1],
m1[0] * m2[2] + m1[2] * m2[3],
m1[1] * m2[2] + m1[3] * m2[3],
m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
];
}
Matrix.prototype.isIdentity = function(){
if (!this.cache) {
this.cache = this.toArray();
}
var m = this.cache;
if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) {
return true;
}
return false;
}
Matrix.prototype.matrix = function (m) {
if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) {
return this;
}
this.cache = null;
this.queue.push(m);
return this;
};
Matrix.prototype.translate = function (tx, ty) {
if (tx !== 0 || ty !== 0) {
this.cache = null;
this.queue.push([ 1, 0, 0, 1, tx, ty ]);
}
return this;
};
Matrix.prototype.scale = function (sx, sy) {
if (sx !== 1 || sy !== 1) {
this.cache = null;
this.queue.push([ sx, 0, 0, sy, 0, 0 ]);
}
return this;
};
Matrix.prototype.rotate = function (angle, rx, ry) {
var rad, cos, sin;
if (angle !== 0) {
this.translate(rx, ry);
rad = angle * Math.PI / 180;
cos = Math.cos(rad);
sin = Math.sin(rad);
this.queue.push([ cos, sin, -sin, cos, 0, 0 ]);
this.cache = null;
this.translate(-rx, -ry);
}
return this;
};
Matrix.prototype.skewX = function (angle) {
if (angle !== 0) {
this.cache = null;
this.queue.push([ 1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0 ]);
}
return this;
};
Matrix.prototype.skewY = function (angle) {
if (angle !== 0) {
this.cache = null;
this.queue.push([ 1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0 ]);
}
return this;
};
// Flatten queue
//
Matrix.prototype.toArray = function () {
if (this.cache) {
return this.cache;
}
if (!this.queue.length) {
this.cache = [ 1, 0, 0, 1, 0, 0 ];
return this.cache;
}
this.cache = this.queue[0];
if (this.queue.length === 1) {
return this.cache;
}
for (var i = 1; i < this.queue.length; i++) {
this.cache = this.combine(this.cache, this.queue[i]);
}
return this.cache;
};
// Apply list of matrixes to (x,y) point.
// If `isRelative` set, `translate` component of matrix will be skipped
//
Matrix.prototype.calc = function (x, y, isRelative) {
var m, i, len;
// Don't change point on empty transforms queue
if (!this.queue.length) { return [ x, y ]; }
// Calculate final matrix, if not exists
//
// NB. if you deside to apply transforms to point one-by-one,
// they should be taken in reverse order
if (!this.cache) {
this.cache = this.toArray();
}
m = this.cache;
// Apply matrix to point
return [
x * m[0] + y * m[2] + (isRelative ? 0 : m[4]),
x * m[1] + y * m[3] + (isRelative ? 0 : m[5])
];
};

View file

@ -0,0 +1,386 @@
(function (root) {
var isCommonJS = typeof module !== 'undefined' && module.exports;
var isNode = !(typeof window !== 'undefined' && root === window);
var setImmediate = setImmediate || function (cb) {
setTimeout(cb, 0);
};
var Worker = isNode ? require(__dirname + '/Worker.js') : root.Worker;
var URL = typeof root !== 'undefined' ? (root.URL ? root.URL : root.webkitURL) : null;
var _supports = (isNode || root.Worker) ? true : false; // node always supports parallel
function extend(from, to) {
if (!to) to = {};
for (var i in from) {
if (to[i] === undefined) to[i] = from[i];
}
return to;
}
function Operation() {
this._callbacks = [];
this._errCallbacks = [];
this._resolved = 0;
this._result = null;
}
Operation.prototype.resolve = function (err, res) {
if (!err) {
this._resolved = 1;
this._result = res;
for (var i = 0; i < this._callbacks.length; ++i) {
this._callbacks[i](res);
}
} else {
this._resolved = 2;
this._result = err;
for (var iE = 0; iE < this._errCallbacks.length; ++iE) {
this._errCallbacks[iE](err);
}
}
this._callbacks = [];
this._errCallbacks = [];
};
Operation.prototype.then = function (cb, errCb) {
if (this._resolved === 1) { // result
if (cb) {
cb(this._result);
}
return;
} else if (this._resolved === 2) { // error
if (errCb) {
errCb(this._result);
}
return;
}
if (cb) {
this._callbacks[this._callbacks.length] = cb;
}
if (errCb) {
this._errCallbacks[this._errCallbacks.length] = errCb;
}
return this;
};
var defaults = {
evalPath: isNode ? __dirname + '/eval.js' : null,
maxWorkers: isNode ? require('os').cpus().length : (navigator.hardwareConcurrency || 4),
synchronous: true,
env: {},
envNamespace: 'env'
};
function Parallel(data, options) {
this.data = data;
this.options = extend(defaults, options);
this.operation = new Operation();
this.operation.resolve(null, this.data);
this.requiredScripts = [];
this.requiredFunctions = [];
}
// static method
Parallel.isSupported = function () { return _supports; }
Parallel.prototype.getWorkerSource = function (cb, env) {
var that = this;
var preStr = '';
var i = 0;
if (!isNode && this.requiredScripts.length !== 0) {
preStr += 'importScripts("' + this.requiredScripts.join('","') + '");\r\n';
}
for (i = 0; i < this.requiredFunctions.length; ++i) {
if (this.requiredFunctions[i].name) {
preStr += 'var ' + this.requiredFunctions[i].name + ' = ' + this.requiredFunctions[i].fn.toString() + ';';
} else {
preStr += this.requiredFunctions[i].fn.toString();
}
}
env = JSON.stringify(env || {});
var ns = this.options.envNamespace;
if (isNode) {
return preStr + 'process.on("message", function(e) {global.' + ns + ' = ' + env + ';process.send(JSON.stringify((' + cb.toString() + ')(JSON.parse(e).data)))})';
} else {
return preStr + 'self.onmessage = function(e) {var global = {}; global.' + ns + ' = ' + env + ';self.postMessage((' + cb.toString() + ')(e.data))}';
}
};
Parallel.prototype.require = function () {
var args = Array.prototype.slice.call(arguments, 0),
func;
for (var i = 0; i < args.length; i++) {
func = args[i];
if (typeof func === 'string') {
this.requiredScripts.push(func);
} else if (typeof func === 'function') {
this.requiredFunctions.push({ fn: func });
} else if (typeof func === 'object') {
this.requiredFunctions.push(func);
}
}
return this;
};
Parallel.prototype._spawnWorker = function (cb, env) {
var wrk;
var src = this.getWorkerSource(cb, env);
if (isNode) {
wrk = new Worker(this.options.evalPath);
wrk.postMessage(src);
} else {
if (Worker === undefined) {
return undefined;
}
try {
if (this.requiredScripts.length !== 0) {
if (this.options.evalPath !== null) {
wrk = new Worker(this.options.evalPath);
wrk.postMessage(src);
} else {
throw new Error('Can\'t use required scripts without eval.js!');
}
} else if (!URL) {
throw new Error('Can\'t create a blob URL in this browser!');
} else {
var blob = new Blob([src], { type: 'text/javascript' });
var url = URL.createObjectURL(blob);
wrk = new Worker(url);
}
} catch (e) {
if (this.options.evalPath !== null) { // blob/url unsupported, cross-origin error
wrk = new Worker(this.options.evalPath);
wrk.postMessage(src);
} else {
throw e;
}
}
}
return wrk;
};
Parallel.prototype.spawn = function (cb, env) {
var that = this;
var newOp = new Operation();
env = extend(this.options.env, env || {});
this.operation.then(function () {
var wrk = that._spawnWorker(cb, env);
if (wrk !== undefined) {
wrk.onmessage = function (msg) {
wrk.terminate();
that.data = msg.data;
newOp.resolve(null, that.data);
};
wrk.onerror = function (e) {
wrk.terminate();
newOp.resolve(e, null);
};
wrk.postMessage(that.data);
} else if (that.options.synchronous) {
setImmediate(function () {
try {
that.data = cb(that.data);
newOp.resolve(null, that.data);
} catch (e) {
newOp.resolve(e, null);
}
});
} else {
throw new Error('Workers do not exist and synchronous operation not allowed!');
}
});
this.operation = newOp;
return this;
};
Parallel.prototype._spawnMapWorker = function (i, cb, done, env, wrk) {
var that = this;
if (!wrk) wrk = that._spawnWorker(cb, env);
if (wrk !== undefined) {
wrk.onmessage = function (msg) {
that.data[i] = msg.data;
done(null, wrk);
};
wrk.onerror = function (e) {
wrk.terminate();
done(e);
};
wrk.postMessage(that.data[i]);
} else if (that.options.synchronous) {
setImmediate(function () {
that.data[i] = cb(that.data[i]);
done();
});
} else {
throw new Error('Workers do not exist and synchronous operation not allowed!');
}
};
Parallel.prototype.map = function (cb, env) {
env = extend(this.options.env, env || {});
if (!this.data.length) {
return this.spawn(cb, env);
}
var that = this;
var startedOps = 0;
var doneOps = 0;
function done(err, wrk) {
if (err) {
newOp.resolve(err, null);
} else if (++doneOps === that.data.length) {
newOp.resolve(null, that.data);
if (wrk) wrk.terminate();
} else if (startedOps < that.data.length) {
that._spawnMapWorker(startedOps++, cb, done, env, wrk);
} else {
if (wrk) wrk.terminate();
}
}
var newOp = new Operation();
this.operation.then(function () {
for (; startedOps - doneOps < that.options.maxWorkers && startedOps < that.data.length; ++startedOps) {
that._spawnMapWorker(startedOps, cb, done, env);
}
}, function (err) {
newOp.resolve(err, null);
});
this.operation = newOp;
return this;
};
Parallel.prototype._spawnReduceWorker = function (data, cb, done, env, wrk) {
var that = this;
if (!wrk) wrk = that._spawnWorker(cb, env);
if (wrk !== undefined) {
wrk.onmessage = function (msg) {
that.data[that.data.length] = msg.data;
done(null, wrk);
};
wrk.onerror = function (e) {
wrk.terminate();
done(e, null);
}
wrk.postMessage(data);
} else if (that.options.synchronous) {
setImmediate(function () {
that.data[that.data.length] = cb(data);
done();
});
} else {
throw new Error('Workers do not exist and synchronous operation not allowed!');
}
};
Parallel.prototype.reduce = function (cb, env) {
env = extend(this.options.env, env || {});
if (!this.data.length) {
throw new Error('Can\'t reduce non-array data');
}
var runningWorkers = 0;
var that = this;
function done(err, wrk) {
--runningWorkers;
if (err) {
newOp.resolve(err, null);
} else if (that.data.length === 1 && runningWorkers === 0) {
that.data = that.data[0];
newOp.resolve(null, that.data);
if (wrk) wrk.terminate();
} else if (that.data.length > 1) {
++runningWorkers;
that._spawnReduceWorker([that.data[0], that.data[1]], cb, done, env, wrk);
that.data.splice(0, 2);
} else {
if (wrk) wrk.terminate();
}
}
var newOp = new Operation();
this.operation.then(function () {
if (that.data.length === 1) {
newOp.resolve(null, that.data[0]);
} else {
for (var i = 0; i < that.options.maxWorkers && i < Math.floor(that.data.length / 2) ; ++i) {
++runningWorkers;
that._spawnReduceWorker([that.data[i * 2], that.data[i * 2 + 1]], cb, done, env);
}
that.data.splice(0, i * 2);
}
});
this.operation = newOp;
return this;
};
Parallel.prototype.then = function (cb, errCb) {
var that = this;
var newOp = new Operation();
errCb = typeof errCb === 'function' ? errCb : function(){};
this.operation.then(function () {
var retData;
try {
if (cb) {
retData = cb(that.data);
if (retData !== undefined) {
that.data = retData;
}
}
newOp.resolve(null, that.data);
} catch (e) {
if (errCb) {
retData = errCb(e);
if (retData !== undefined) {
that.data = retData;
}
newOp.resolve(null, that.data);
} else {
newOp.resolve(null, e);
}
}
}, function (err) {
if (errCb) {
var retData = errCb(err);
if (retData !== undefined) {
that.data = retData;
}
newOp.resolve(null, that.data);
} else {
newOp.resolve(null, err);
}
});
this.operation = newOp;
return this;
};
root.Parallel = Parallel;
})(typeof window !== 'undefined' ? window : self);

View file

@ -0,0 +1,849 @@
// SVGPathSeg API polyfill
// https://github.com/progers/pathseg
//
// This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from
// SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec
// changes which were implemented in Firefox 43 and Chrome 46.
(function() { "use strict";
if (!("SVGPathSeg" in window)) {
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg
window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) {
this.pathSegType = type;
this.pathSegTypeAsLetter = typeAsLetter;
this._owningPathSegList = owningPathSegList;
}
window.SVGPathSeg.prototype.classname = "SVGPathSeg";
window.SVGPathSeg.PATHSEG_UNKNOWN = 0;
window.SVGPathSeg.PATHSEG_CLOSEPATH = 1;
window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2;
window.SVGPathSeg.PATHSEG_MOVETO_REL = 3;
window.SVGPathSeg.PATHSEG_LINETO_ABS = 4;
window.SVGPathSeg.PATHSEG_LINETO_REL = 5;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9;
window.SVGPathSeg.PATHSEG_ARC_ABS = 10;
window.SVGPathSeg.PATHSEG_ARC_REL = 11;
window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12;
window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13;
window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14;
window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
// Notify owning PathSegList on any changes so they can be synchronized back to the path element.
window.SVGPathSeg.prototype._segmentChanged = function() {
if (this._owningPathSegList)
this._owningPathSegList.segmentChanged(this);
}
window.SVGPathSegClosePath = function(owningPathSegList) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList);
}
window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegClosePath.prototype.toString = function() { return "[object SVGPathSegClosePath]"; }
window.SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter; }
window.SVGPathSegClosePath.prototype.clone = function() { return new window.SVGPathSegClosePath(undefined); }
window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegMovetoAbs.prototype.toString = function() { return "[object SVGPathSegMovetoAbs]"; }
window.SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegMovetoAbs.prototype.clone = function() { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegMovetoRel.prototype.toString = function() { return "[object SVGPathSegMovetoRel]"; }
window.SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegMovetoRel.prototype.clone = function() { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoAbs.prototype.toString = function() { return "[object SVGPathSegLinetoAbs]"; }
window.SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegLinetoAbs.prototype.clone = function() { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoRel.prototype.toString = function() { return "[object SVGPathSegLinetoRel]"; }
window.SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegLinetoRel.prototype.clone = function() { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicAbs = function(owningPathSegList, x, y, x1, y1, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicAbs]"; }
window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicRel = function(owningPathSegList, x, y, x1, y1, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicRel]"; }
window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticAbs = function(owningPathSegList, x, y, x1, y1) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
}
window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticAbs]"; }
window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticRel = function(owningPathSegList, x, y, x1, y1) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
}
window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticRel]"; }
window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegArcAbs = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList);
this._x = x;
this._y = y;
this._r1 = r1;
this._r2 = r2;
this._angle = angle;
this._largeArcFlag = largeArcFlag;
this._sweepFlag = sweepFlag;
}
window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegArcAbs.prototype.toString = function() { return "[object SVGPathSegArcAbs]"; }
window.SVGPathSegArcAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; }
window.SVGPathSegArcAbs.prototype.clone = function() { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegArcRel = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList);
this._x = x;
this._y = y;
this._r1 = r1;
this._r2 = r2;
this._angle = angle;
this._largeArcFlag = largeArcFlag;
this._sweepFlag = sweepFlag;
}
window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegArcRel.prototype.toString = function() { return "[object SVGPathSegArcRel]"; }
window.SVGPathSegArcRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; }
window.SVGPathSegArcRel.prototype.clone = function() { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }
Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList);
this._x = x;
}
window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalAbs]"; }
window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; }
window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); }
Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList);
this._x = x;
}
window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalRel]"; }
window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; }
window.SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); }
Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList);
this._y = y;
}
window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalAbs]"; }
window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; }
window.SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); }
Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList);
this._y = y;
}
window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoVerticalRel.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalRel]"; }
window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; }
window.SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); }
Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicSmoothAbs = function(owningPathSegList, x, y, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList);
this._x = x;
this._y = y;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothAbs]"; }
window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicSmoothRel = function(owningPathSegList, x, y, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList);
this._x = x;
this._y = y;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothRel]"; }
window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticSmoothAbs = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothAbs]"; }
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticSmoothRel = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothRel]"; }
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
// Add createSVGPathSeg* functions to window.SVGPathElement.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement.
window.SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new window.SVGPathSegClosePath(undefined); }
window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function(x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function(x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function(x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function(x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); }
window.SVGPathElement.prototype.createSVGPathSegArcAbs = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }
window.SVGPathElement.prototype.createSVGPathSegArcRel = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }
window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function(x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); }
window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function(x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x); }
window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function(y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y); }
window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function(y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function(x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function(x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function(x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function(x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); }
if (!("getPathSegAtLength" in window.SVGPathElement.prototype)) {
// Add getPathSegAtLength to SVGPathElement.
// Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength
// This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm.
window.SVGPathElement.prototype.getPathSegAtLength = function(distance) {
if (distance === undefined || !isFinite(distance))
throw "Invalid arguments.";
var measurementElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
measurementElement.setAttribute("d", this.getAttribute("d"));
var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1;
// If the path is empty, return 0.
if (lastPathSegment <= 0)
return 0;
do {
measurementElement.pathSegList.removeItem(lastPathSegment);
if (distance > measurementElement.getTotalLength())
break;
lastPathSegment--;
} while (lastPathSegment > 0);
return lastPathSegment;
}
}
}
// Checking for SVGPathSegList in window checks for the case of an implementation without the
// SVGPathSegList API.
// The second check for appendItem is specific to Firefox 59+ which removed only parts of the
// SVGPathSegList API (e.g., appendItem). In this case we need to re-implement the entire API
// so the polyfill data (i.e., _list) is used throughout.
if (!("SVGPathSegList" in window) || !("appendItem" in window.SVGPathSegList.prototype)) {
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList
window.SVGPathSegList = function(pathElement) {
this._pathElement = pathElement;
this._list = this._parsePath(this._pathElement.getAttribute("d"));
// Use a MutationObserver to catch changes to the path's "d" attribute.
this._mutationObserverConfig = { "attributes": true, "attributeFilter": ["d"] };
this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this));
this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
}
window.SVGPathSegList.prototype.classname = "SVGPathSegList";
Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", {
get: function() {
this._checkPathSynchronizedToList();
return this._list.length;
},
enumerable: true
});
// Add the pathSegList accessors to window.SVGPathElement.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData
Object.defineProperty(window.SVGPathElement.prototype, "pathSegList", {
get: function() {
if (!this._pathSegList)
this._pathSegList = new window.SVGPathSegList(this);
return this._pathSegList;
},
enumerable: true
});
// FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList.
Object.defineProperty(window.SVGPathElement.prototype, "normalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true });
Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true });
Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true });
// Process any pending mutations to the path element and update the list as needed.
// This should be the first call of all public functions and is needed because
// MutationObservers are not synchronous so we can have pending asynchronous mutations.
window.SVGPathSegList.prototype._checkPathSynchronizedToList = function() {
this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords());
}
window.SVGPathSegList.prototype._updateListFromPathMutations = function(mutationRecords) {
if (!this._pathElement)
return;
var hasPathMutations = false;
mutationRecords.forEach(function(record) {
if (record.attributeName == "d")
hasPathMutations = true;
});
if (hasPathMutations)
this._list = this._parsePath(this._pathElement.getAttribute("d"));
}
// Serialize the list and update the path's 'd' attribute.
window.SVGPathSegList.prototype._writeListToPath = function() {
this._pathElementMutationObserver.disconnect();
this._pathElement.setAttribute("d", window.SVGPathSegList._pathSegArrayAsString(this._list));
this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
}
// When a path segment changes the list needs to be synchronized back to the path element.
window.SVGPathSegList.prototype.segmentChanged = function(pathSeg) {
this._writeListToPath();
}
window.SVGPathSegList.prototype.clear = function() {
this._checkPathSynchronizedToList();
this._list.forEach(function(pathSeg) {
pathSeg._owningPathSegList = null;
});
this._list = [];
this._writeListToPath();
}
window.SVGPathSegList.prototype.initialize = function(newItem) {
this._checkPathSynchronizedToList();
this._list = [newItem];
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
}
window.SVGPathSegList.prototype._checkValidIndex = function(index) {
if (isNaN(index) || index < 0 || index >= this.numberOfItems)
throw "INDEX_SIZE_ERR";
}
window.SVGPathSegList.prototype.getItem = function(index) {
this._checkPathSynchronizedToList();
this._checkValidIndex(index);
return this._list[index];
}
window.SVGPathSegList.prototype.insertItemBefore = function(newItem, index) {
this._checkPathSynchronizedToList();
// Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
if (index > this.numberOfItems)
index = this.numberOfItems;
if (newItem._owningPathSegList) {
// SVG2 spec says to make a copy.
newItem = newItem.clone();
}
this._list.splice(index, 0, newItem);
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
}
window.SVGPathSegList.prototype.replaceItem = function(newItem, index) {
this._checkPathSynchronizedToList();
if (newItem._owningPathSegList) {
// SVG2 spec says to make a copy.
newItem = newItem.clone();
}
this._checkValidIndex(index);
this._list[index] = newItem;
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
}
window.SVGPathSegList.prototype.removeItem = function(index) {
this._checkPathSynchronizedToList();
this._checkValidIndex(index);
var item = this._list[index];
this._list.splice(index, 1);
this._writeListToPath();
return item;
}
window.SVGPathSegList.prototype.appendItem = function(newItem) {
this._checkPathSynchronizedToList();
if (newItem._owningPathSegList) {
// SVG2 spec says to make a copy.
newItem = newItem.clone();
}
this._list.push(newItem);
newItem._owningPathSegList = this;
// TODO: Optimize this to just append to the existing attribute.
this._writeListToPath();
return newItem;
}
window.SVGPathSegList._pathSegArrayAsString = function(pathSegArray) {
var string = "";
var first = true;
pathSegArray.forEach(function(pathSeg) {
if (first) {
first = false;
string += pathSeg._asPathString();
} else {
string += " " + pathSeg._asPathString();
}
});
return string;
}
// This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp.
window.SVGPathSegList.prototype._parsePath = function(string) {
if (!string || string.length == 0)
return [];
var owningPathSegList = this;
var Builder = function() {
this.pathSegList = [];
}
Builder.prototype.appendSegment = function(pathSeg) {
this.pathSegList.push(pathSeg);
}
var Source = function(string) {
this._string = string;
this._currentIndex = 0;
this._endIndex = this._string.length;
this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN;
this._skipOptionalSpaces();
}
Source.prototype._isCurrentSpace = function() {
var character = this._string[this._currentIndex];
return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f");
}
Source.prototype._skipOptionalSpaces = function() {
while (this._currentIndex < this._endIndex && this._isCurrentSpace())
this._currentIndex++;
return this._currentIndex < this._endIndex;
}
Source.prototype._skipOptionalSpacesOrDelimiter = function() {
if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",")
return false;
if (this._skipOptionalSpaces()) {
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") {
this._currentIndex++;
this._skipOptionalSpaces();
}
}
return this._currentIndex < this._endIndex;
}
Source.prototype.hasMoreData = function() {
return this._currentIndex < this._endIndex;
}
Source.prototype.peekSegmentType = function() {
var lookahead = this._string[this._currentIndex];
return this._pathSegTypeFromChar(lookahead);
}
Source.prototype._pathSegTypeFromChar = function(lookahead) {
switch (lookahead) {
case "Z":
case "z":
return window.SVGPathSeg.PATHSEG_CLOSEPATH;
case "M":
return window.SVGPathSeg.PATHSEG_MOVETO_ABS;
case "m":
return window.SVGPathSeg.PATHSEG_MOVETO_REL;
case "L":
return window.SVGPathSeg.PATHSEG_LINETO_ABS;
case "l":
return window.SVGPathSeg.PATHSEG_LINETO_REL;
case "C":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;
case "c":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;
case "Q":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;
case "q":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;
case "A":
return window.SVGPathSeg.PATHSEG_ARC_ABS;
case "a":
return window.SVGPathSeg.PATHSEG_ARC_REL;
case "H":
return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;
case "h":
return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;
case "V":
return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;
case "v":
return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;
case "S":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
case "s":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
case "T":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
case "t":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
default:
return window.SVGPathSeg.PATHSEG_UNKNOWN;
}
}
Source.prototype._nextCommandHelper = function(lookahead, previousCommand) {
// Check for remaining coordinates in the current command.
if ((lookahead == "+" || lookahead == "-" || lookahead == "." || (lookahead >= "0" && lookahead <= "9")) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) {
if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS)
return window.SVGPathSeg.PATHSEG_LINETO_ABS;
if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL)
return window.SVGPathSeg.PATHSEG_LINETO_REL;
return previousCommand;
}
return window.SVGPathSeg.PATHSEG_UNKNOWN;
}
Source.prototype.initialCommandIsMoveTo = function() {
// If the path is empty it is still valid, so return true.
if (!this.hasMoreData())
return true;
var command = this.peekSegmentType();
// Path must start with moveTo.
return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL;
}
// Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF
Source.prototype._parseNumber = function() {
var exponent = 0;
var integer = 0;
var frac = 1;
var decimal = 0;
var sign = 1;
var expsign = 1;
var startIndex = this._currentIndex;
this._skipOptionalSpaces();
// Read the sign.
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+")
this._currentIndex++;
else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") {
this._currentIndex++;
sign = -1;
}
if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != "."))
// The first character of a number must be one of [0-9+-.].
return undefined;
// Read the integer part, build right-to-left.
var startIntPartIndex = this._currentIndex;
while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9")
this._currentIndex++; // Advance to first non-digit.
if (this._currentIndex != startIntPartIndex) {
var scanIntPartIndex = this._currentIndex - 1;
var multiplier = 1;
while (scanIntPartIndex >= startIntPartIndex) {
integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0");
multiplier *= 10;
}
}
// Read the decimals.
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") {
this._currentIndex++;
// There must be a least one digit following the .
if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9")
return undefined;
while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
frac *= 10;
decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
this._currentIndex += 1;
}
}
// Read the exponent part.
if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && (this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m")) {
this._currentIndex++;
// Read the sign of the exponent.
if (this._string.charAt(this._currentIndex) == "+") {
this._currentIndex++;
} else if (this._string.charAt(this._currentIndex) == "-") {
this._currentIndex++;
expsign = -1;
}
// There must be an exponent.
if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9")
return undefined;
while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
exponent *= 10;
exponent += (this._string.charAt(this._currentIndex) - "0");
this._currentIndex++;
}
}
var number = integer + decimal;
number *= sign;
if (exponent)
number *= Math.pow(10, expsign * exponent);
if (startIndex == this._currentIndex)
return undefined;
this._skipOptionalSpacesOrDelimiter();
return number;
}
Source.prototype._parseArcFlag = function() {
if (this._currentIndex >= this._endIndex)
return undefined;
var flag = false;
var flagChar = this._string.charAt(this._currentIndex++);
if (flagChar == "0")
flag = false;
else if (flagChar == "1")
flag = true;
else
return undefined;
this._skipOptionalSpacesOrDelimiter();
return flag;
}
Source.prototype.parseSegment = function() {
var lookahead = this._string[this._currentIndex];
var command = this._pathSegTypeFromChar(lookahead);
if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) {
// Possibly an implicit command. Not allowed if this is the first command.
if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN)
return null;
command = this._nextCommandHelper(lookahead, this._previousCommand);
if (command == window.SVGPathSeg.PATHSEG_UNKNOWN)
return null;
} else {
this._currentIndex++;
}
this._previousCommand = command;
switch (command) {
case window.SVGPathSeg.PATHSEG_MOVETO_REL:
return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_MOVETO_ABS:
return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_REL:
return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_ABS:
return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_CLOSEPATH:
this._skipOptionalSpaces();
return new window.SVGPathSegClosePath(owningPathSegList);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_ARC_REL:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
case window.SVGPathSeg.PATHSEG_ARC_ABS:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
default:
throw "Unknown path seg type."
}
}
var builder = new Builder();
var source = new Source(string);
if (!source.initialCommandIsMoveTo())
return [];
while (source.hasMoreData()) {
var pathSeg = source.parseSegment();
if (!pathSeg)
return [];
builder.appendSegment(pathSeg);
}
return builder.pathSegList;
}
}
}());

View file

@ -0,0 +1,297 @@
// jsClipper uses X/Y instead of x/y...
function toClipperCoordinates(polygon){
var clone = [];
for(var i=0; i<polygon.length; i++){
clone.push({
X: polygon[i].x,
Y: polygon[i].y
});
}
return clone;
};
function toNestCoordinates(polygon, scale){
var clone = [];
for(var i=0; i<polygon.length; i++){
clone.push({
x: polygon[i].X/scale,
y: polygon[i].Y/scale
});
}
return clone;
};
function rotatePolygon(polygon, degrees){
var rotated = [];
var angle = degrees * Math.PI / 180;
for(var i=0; i<polygon.length; i++){
var x = polygon[i].x;
var y = polygon[i].y;
var x1 = x*Math.cos(angle)-y*Math.sin(angle);
var y1 = x*Math.sin(angle)+y*Math.cos(angle);
rotated.push({x:x1, y:y1});
}
if(polygon.children && polygon.children.length > 0){
rotated.children = [];
for(var j=0; j<polygon.children.length; j++){
rotated.children.push(rotatePolygon(polygon.children[j], degrees));
}
}
return rotated;
};
function PlacementWorker(binPolygon, paths, ids, rotations, config, nfpCache){
this.binPolygon = binPolygon;
this.paths = paths;
this.ids = ids;
this.rotations = rotations;
this.config = config;
this.nfpCache = nfpCache || {};
// return a placement for the paths/rotations given
// happens inside a webworker
this.placePaths = function(paths){
var self = global.env.self;
if(!self.binPolygon){
return null;
}
var i, j, k, m, n, path;
// rotate paths by given rotation
var rotated = [];
for(i=0; i<paths.length; i++){
var r = rotatePolygon(paths[i], paths[i].rotation);
r.rotation = paths[i].rotation;
r.source = paths[i].source;
r.id = paths[i].id;
rotated.push(r);
}
paths = rotated;
var allplacements = [];
var fitness = 0;
var binarea = Math.abs(GeometryUtil.polygonArea(self.binPolygon));
var key, nfp;
while(paths.length > 0){
var placed = [];
var placements = [];
fitness += 1; // add 1 for each new bin opened (lower fitness is better)
for(i=0; i<paths.length; i++){
path = paths[i];
// inner NFP
key = JSON.stringify({A:-1,B:path.id,inside:true,Arotation:0,Brotation:path.rotation});
var binNfp = self.nfpCache[key];
// part unplaceable, skip
if(!binNfp || binNfp.length == 0){
continue;
}
// ensure all necessary NFPs exist
var error = false;
for(j=0; j<placed.length; j++){
key = JSON.stringify({A:placed[j].id,B:path.id,inside:false,Arotation:placed[j].rotation,Brotation:path.rotation});
nfp = self.nfpCache[key];
if(!nfp){
error = true;
break;
}
}
// part unplaceable, skip
if(error){
continue;
}
var position = null;
if(placed.length == 0){
// first placement, put it on the left
for(j=0; j<binNfp.length; j++){
for(k=0; k<binNfp[j].length; k++){
if(position === null || binNfp[j][k].x-path[0].x < position.x ){
position = {
x: binNfp[j][k].x-path[0].x,
y: binNfp[j][k].y-path[0].y,
id: path.id,
rotation: path.rotation
}
}
}
}
placements.push(position);
placed.push(path);
continue;
}
var clipperBinNfp = [];
for(j=0; j<binNfp.length; j++){
clipperBinNfp.push(toClipperCoordinates(binNfp[j]));
}
ClipperLib.JS.ScaleUpPaths(clipperBinNfp, self.config.clipperScale);
var clipper = new ClipperLib.Clipper();
var combinedNfp = new ClipperLib.Paths();
for(j=0; j<placed.length; j++){
key = JSON.stringify({A:placed[j].id,B:path.id,inside:false,Arotation:placed[j].rotation,Brotation:path.rotation});
nfp = self.nfpCache[key];
if(!nfp){
continue;
}
for(k=0; k<nfp.length; k++){
var clone = toClipperCoordinates(nfp[k]);
for(m=0; m<clone.length; m++){
clone[m].X += placements[j].x;
clone[m].Y += placements[j].y;
}
ClipperLib.JS.ScaleUpPath(clone, self.config.clipperScale);
clone = ClipperLib.Clipper.CleanPolygon(clone, 0.0001*self.config.clipperScale);
var area = Math.abs(ClipperLib.Clipper.Area(clone));
if(clone.length > 2 && area > 0.1*self.config.clipperScale*self.config.clipperScale){
clipper.AddPath(clone, ClipperLib.PolyType.ptSubject, true);
}
}
}
if(!clipper.Execute(ClipperLib.ClipType.ctUnion, combinedNfp, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)){
continue;
}
// difference with bin polygon
var finalNfp = new ClipperLib.Paths();
clipper = new ClipperLib.Clipper();
clipper.AddPaths(combinedNfp, ClipperLib.PolyType.ptClip, true);
clipper.AddPaths(clipperBinNfp, ClipperLib.PolyType.ptSubject, true);
if(!clipper.Execute(ClipperLib.ClipType.ctDifference, finalNfp, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)){
continue;
}
finalNfp = ClipperLib.Clipper.CleanPolygons(finalNfp, 0.0001*self.config.clipperScale);
for(j=0; j<finalNfp.length; j++){
var area = Math.abs(ClipperLib.Clipper.Area(finalNfp[j]));
if(finalNfp[j].length < 3 || area < 0.1*self.config.clipperScale*self.config.clipperScale){
finalNfp.splice(j,1);
j--;
}
}
if(!finalNfp || finalNfp.length == 0){
continue;
}
var f = [];
for(j=0; j<finalNfp.length; j++){
// back to normal scale
f.push(toNestCoordinates(finalNfp[j], self.config.clipperScale));
}
finalNfp = f;
// choose placement that results in the smallest bounding box
// could use convex hull instead, but it can create oddly shaped nests (triangles or long slivers) which are not optimal for real-world use
// todo: generalize gravity direction
var minwidth = null;
var minarea = null;
var minx = null;
var nf, area, shiftvector;
for(j=0; j<finalNfp.length; j++){
nf = finalNfp[j];
if(Math.abs(GeometryUtil.polygonArea(nf)) < 2){
continue;
}
for(k=0; k<nf.length; k++){
var allpoints = [];
for(m=0; m<placed.length; m++){
for(n=0; n<placed[m].length; n++){
allpoints.push({x:placed[m][n].x+placements[m].x, y: placed[m][n].y+placements[m].y});
}
}
shiftvector = {
x: nf[k].x-path[0].x,
y: nf[k].y-path[0].y,
id: path.id,
rotation: path.rotation,
nfp: combinedNfp
};
for(m=0; m<path.length; m++){
allpoints.push({x: path[m].x+shiftvector.x, y:path[m].y+shiftvector.y});
}
var rectbounds = GeometryUtil.getPolygonBounds(allpoints);
// weigh width more, to help compress in direction of gravity
area = rectbounds.width*2 + rectbounds.height;
if(minarea === null || area < minarea || (GeometryUtil.almostEqual(minarea, area) && (minx === null || shiftvector.x < minx))){
minarea = area;
minwidth = rectbounds.width;
position = shiftvector;
minx = shiftvector.x;
}
}
}
if(position){
placed.push(path);
placements.push(position);
}
}
if(minwidth){
fitness += minwidth/binarea;
}
for(i=0; i<placed.length; i++){
var index = paths.indexOf(placed[i]);
if(index >= 0){
paths.splice(index,1);
}
}
if(placements && placements.length > 0){
allplacements.push(placements);
}
else{
break; // something went wrong
}
}
// there were parts that couldn't be placed
fitness += 2*paths.length;
return {placements: allplacements, fitness: fitness, paths: paths, area: binarea };
};
}
(typeof window !== 'undefined' ? window : self).PlacementWorker = PlacementWorker;
// clipperjs uses alerts for warnings
function alert(message) {
console.log('alert: ', message);
}