Merge branch 'alpha' of https://github.com/Azgaar/Fantasy-Map-Generator into alpha
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--Author: Azgaar. License: Public Domain-->
|
||||
<g id="annulet">
|
||||
<path d="M70 100a30 30 0 1060 0 30 30 0 10-60 0m10 0a20 20 0 1140 0 20 20 0 11-40 0"/>
|
||||
<path d="M70 100c0 16.569 13.431 30 30 30 16.569 0 30-13.431 30-30 0-16.569-13.431-30-30-30-16.569 0-30 13.431-30 30m10 0c0-11.046 8.954-20 20-20s20 8.954 20 20-8.954 20-20 20-20-8.954-20-20"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 371 B |
|
|
@ -4,9 +4,9 @@
|
|||
<path d="M111.33 86.02c1.3-.92 2.46-3.23 3.68-4.16 0 0 .76.01 1.7-.66 2.22-1.56 1.93-3.93 1.39-5.24-2.15-5.21-12.23-3.2-16.1-9.99-1.78-3.1-.49-6.9.61-7.31.77-.3-.17 1.61 1 3.44 3.89 6.1 17.3 2.58 23.27 11.02a10.4 10.4 0 011.83 5.84c.26 8.81-8.43 11.97-11.67 12.48-1.72 1.72-2.53-.01-4.2-.01m-24.08-5.41c-1.3-.92-2.45-3.23-3.68-4.16 0 0-.76.01-1.7-.66-2.22-1.56-1.93-3.93-1.39-5.24 2.15-5.21 12.23-3.2 16.1-9.99 1.78-3.1.49-6.9-.61-7.31-.77-.3.17 1.61-1 3.44-4.04 6.35-17.42 2.46-23.27 10.72-1.4 1.96-1.77 4.35-1.83 6.14-.26 8.81 8.44 11.97 11.67 12.48 1.72 1.72 2.53-.01 4.2-.01"/>
|
||||
<path d="M114.73 92.4s3.4-1.7 5.41-1.7c3.65 0 5.83 2.27 8.87 2.27 1.83 0 3.4-.85 4.06-1.27.32-.09.3.03.33.23-.08 1.23-1.67 4.56-4.9 6.32-.28.7-2.24 1.92-3.04 1.92-.3.24-1.07.94-3.5.94-3.4 0-7.16-1.94-7.16-2.42M85.21 92.4s-3.4-1.7-5.4-1.7c-3.65 0-5.84 2.27-8.87 2.27-1.83 0-3.4-.85-4.07-1.27-.31-.09-.3.03-.32.23.07 1.23 1.67 4.56 4.9 6.32.28.7 2.23 1.92 3.04 1.92.3.24 1.07.94 3.5.94 3.4 0 7.16-1.94 7.16-2.42"/>
|
||||
<path d="M82.7 91.7c-.82 4.79 1.36 5.5 1.24 6.75-1.6 5.22 1.35 6.57 1.35 9 0 1.78.22 1.69.68 3.84.26 1.7 1.8 3.79 2.81 5.78.25.5 1.06 3.97 1.06 4.27 0 .14-1.03 1.82-1.03 4.48 0 3.66 2.14 5.8 3.59 5.8 4.5 0 5.28-.47 7.57-.47 2.3 0 3.06.48 7.58.48 1.45 0 3.58-2.15 3.58-5.8 0-2.67-1.02-4.35-1.02-4.49 0-.3.8-3.78 1.05-4.27 1.01-2 2.56-4.07 2.82-5.78.45-2.15.68-2.06.68-3.84 0-2.43 2.96-3.77 1.35-9-.14-1.25 2.06-1.95 1.23-6.75l-.19-.25c-2.25-1.44-2.4-6.7-1.99-9.5-1.04-1.08-3.32-1.57-6.75-1.57s-5.92 1.2-8.34 1.2c-2.41 0-4.9-1.2-8.33-1.2s-5.71.49-6.76 1.57c.42 2.8.28 8.06-1.99 9.5l-.18.25z"/>
|
||||
<path fill="#F2BC51" d="m 108.72,124.86 c -0.13,0.71 -0.84,1.42 -1.24,1.68 0.75,1.32 1.19,2.81 1.2,4.34 0.026,4.78 -3.88,8.67 -8.67,8.67 -4.78,0 -8.67,-3.88 -8.67,-8.67 0,-1.55 0.5,-3.1 1.2,-4.35 -0.63,-0.42 -1.02,-1.04 -1.22,-1.7 -1.22,1.7851 -1.92,3.89 -1.93,6.06 -0.02,5.87 4.75,10.63 10.63,10.63 5.87,0 10.67,-4.75 10.63,-10.63 -0.01,-2.15 -0.72,-4.24 -1.93,-6.03 z"/>
|
||||
<path fill="#F6F6F6" d="M111 100.6c0 .43.68 1.2 2.3 1.2 1.22 0 1.83-2.59 1.83-2.8 0-.22-.03-1.36-1.13-1.36-1.2-.01-3 2.99-3 2.96zm-22.04 0c0 .43-.68 1.2-2.3 1.2-1.23 0-1.83-2.59-1.83-2.8 0-.22.03-1.36 1.13-1.36 1.19-.01 3 2.99 3 2.96z"/>
|
||||
<path fill="none" stroke-width=".22" d="M112.08 91.93c-.42.47-1.18.92-2.48 1.11-.23.01-.3-.05-.2-.17.47-.6.6-1.19.6-1.67m-2.7 1.4c-.52.38-1.3.7-2.5.9-.16.04-.35.14-.23-.1.42-1-.09-2.3-.09-2.3m-1.43 1.75c-.23.5-.4 1.1-.38 1.77.01.21-.09.21-.34.16a2.14 2.14 0 01-1.55-1m-.56-2s-.2 1.93-2.3 2.42c-.2.01-.34.1-.48-.08.34-.49.35-.87.18-1.2m-2-1.34c-.3.52-.62 1.05-.05 1.77.16.26-.05.24-.28.2-1.27-.22-1.8-.68-2.05-1.21m-1.7-1.83c.62 1.03-1.41 1.35-.25 2.33.03.29-.2.3-.49.3-1.68 0-2.32-.72-2.8-1.5"/>
|
||||
<path fill="#F2BC51" d="M108.72 124.86l-.12.38a2.54 2.54 0 01-.59.9c-.17.17-.36.3-.53.4a9.2 9.2 0 011.2 4.34 8.67 8.67 0 11-17.34 0c0-1.55.5-3.1 1.2-4.35a3 3 0 01-.84-.8 3.09 3.09 0 01-.38-.9 10.97 10.97 0 00-1.93 6.06 10.63 10.63 0 1021.26 0 11.3 11.3 0 00-1.93-6.03z"/>
|
||||
<g fill="#000" stroke="none">
|
||||
<path d="M95.78 67.1c.23.58-1.3 1.41-1 .97.39-.28.68-.64 1-.97zm-2.04.97c.16.73-.75 1.4-1.41 1.09a1.65 1.65 0 001.41-1.09zm-1.88.78c-.07.53-.9.93-1.16.82.4-.27.79-.53 1.16-.82zM74.02 81.99c-.03.68.8 2.66-.05 1.18-.12-.38-.14-.82.05-1.18zm1.23 2.18c.28.54.67 1.72.01.69a.87.87 0 01-.01-.69zm1.47 1.73c.02.31.8 1.35.12.82-.22-.2-.26-.56-.12-.82zm1.56 1.27c-.09.5 1.32 1.53.7 1.36a1.3 1.3 0 01-.7-1.36zm5.36-5.65c-1.44-.67-3-1.95-2.96-3.68.08 1.68 1.64 2.82 3.07 3.41l-.07.17zM104.3 67.1c.19.4 1.77 1.43.67 1-.34-.2-.71-.6-.67-1zm2.06.97c-.1.76 1.98 1.11.98 1.15-.55-.04-1.1-.58-.98-1.15zm1.87.78c.81.43 1.68 1.31.31.48-.15-.12-.24-.3-.31-.48zm17.85 13.14c.4.55-.02 1.68-.41 1.78a5 5 0 00.41-1.78zm-1.24 2.18c.5.53-.89 1.76-.25.67.09-.22.18-.45.25-.67zm-1.47 1.73c.54.54-.94 1.53-.19.52 0-.19.17-.34.19-.52zm-1.56 1.27c.25.6-.52 1.5-.91 1.38.4-.37.78-.83.91-1.38zm-5.47-5.91c1.43-.6 3-1.72 3.07-3.41.05 1.74-1.54 2.99-2.96 3.69l-.07-.17zm-20.51 8.85c.53.44.07 1.3-.13 1.55l-.19-.11s.16-.23.36-.58c.1-.25 0-.64-.04-.86zm8.75-1.01c-.5.53-.16 1.18.01 2 0 .04-.16.04-.18.08-.3-.47-.46-1.7.17-2.08zm5.48 32.5c-.2-.74-.92-1.2-1.48-1.58.83.03 1.57.7 1.81 1.49l-.17.05zm-6.89-2.68c-1.9.36-3.87.75-5.77.08-1.61-.33 2.07.25 2.74.17 1.02.02 2-.37 3.03-.25zm1.68-37.61c-2.3.95-4.81.17-7.2.5-.65.29-3.73-.83-1.64-.29 2.2.25 4.42-.17 6.62.13.75.01 1.5-.1 2.22-.34zm4.06 41.29c-.01 1.25.19 3.11-1.19 3.93-.4.18-.57.43-.87.47-.23.03.28-.39.46-.5 1.47-.99 1.45-2.41 1.45-3.75-.21-.02.18-.27.15-.15z"/>
|
||||
<path d="M108.5 122c-.81.91.22 2.7-1.02 3.33.95-.87-.06-2.64 1.02-3.33zm9.24-27.8c1.92-1.55 4.6-1.64 6.79-.62 2.22.72 4.77.65 6.88-.19-2.73 1.55-6.03.96-8.84-.05-1.63-.54-3.41.08-4.83.86zm4.23.57a5.56 5.56 0 00-3.29 1.71c-.63.19-.87-1.15-.29-.22.95-.68 2.14-1.83 3.57-1.49zM117 97.94c2.8 1.96 6.54 1.95 9.53.44 1.33-.73 3.03-1.94.96-.31-3.07 2.12-7.57 2.45-10.49-.13zm-7.33 28.7c.27-.83.14-1.91-.11-2.66-.68-2.16.8.93.58 1.84-.01.35-.08.82-.28 1.17zm.32.71c.23-.4.37-.86.42-1.29.1.5-.03 1.08-.3 1.53zm-6.24 2.85c-2.52-.3-5.03-.11-7.55.04 2.42-.47 5.07-.56 7.55-.04zm3.8-27.86c-.83 3.13-.28 6.54-1.66 9.57-.38 1.17-1 2.9-.37.47 1.2-3.07.99-6.44 1.8-9.59.1-.12.1-.35.23-.45zm3.51 0c1.11-.27 2.65.65 3.41-.63 1.31-2.06.27 1-1.43 1-.69.01-1.35-.43-1.98-.37zm4.1 3.31c-.4 1.3-.98 2.66-1.52 3.86a7.93 7.93 0 011.52-3.86zm-2.59-8.42c-.11-1.44 3.31-.47 2.09-.43-.71-.11-1.6-.13-2.09.43zm-3.4 4.14c2.33-1.45 2.78-4.27 3.98-6.5 1.2-1.2-.07.09-.17.72-.74 2.23-1.56 4.67-3.81 5.78zm-2.89-7.21c.22.4-.24 1.84-.37 1.58-.05-.56.32-1.03.37-1.58zm.66 1.86c-.4.26-1 1.83-.69.52.15-.25.4-.49.69-.52zm-.32 1.58c-.27 1.11-.8.55-.05.01zm.22.97c.12.78-.5.77-.1.07a.24.24 0 01.1-.07zm.3 1.12c.37.8-.3.76-.07.04l.03-.03zm1.02 15.18c-.29 1.07-.87.5 0 0zm-.11 1.29c.08 1-.52.73-.14.07a.22.22 0 01.14-.07zm.73-3.96c-.31.22-.97 1.45-.7.5.13-.27.42-.5.7-.5zm-.5 1.58c-.58 1-.97.38-.04.01h.04zm.8-15.33c.18-1.12.66-2.61 1.75-3.01-.84.82-1.14 2.04-1.75 3.01zm-19.49 23.07c.22-.76 1.02-1.53 1.81-1.46-.63.35-1.28.82-1.48 1.55 0 .12-.34-.22-.33-.09zm6.61-1.72c1.91.1 3.85.86 5.76.2.8-.29 2.58-.25.86.01-1.9.81-3.99.67-5.87-.07-.24-.09-.54-.06-.75-.14zm-5.09 2.8c.38 1.13.11 2.9 1.36 3.74.73.33 1.64 1.4.2.45-1.58-1.05-1.57-2.76-1.56-4.19z"/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 9 KiB |
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--source: wappenwiki.org, CC BY-NC-SA 3.0-->
|
||||
<g id="bullPassant" stroke-width="3.3">
|
||||
<g id="bullPassant" stroke-width="2">
|
||||
<path d="M255.1 371.8s3.7 2 4.6 5c1.6 6.7-6 18.9-11 24-1.5 1.6-4.4-6.9-5.9-12.6 0 0-.1 3 .4 5.3l.7 3.8s-2.7 3.8-5.3 6.2c-1.6-3.2-.4-7.2-1.7-13.3-1-4.8 4.1-8.3 7.9-11.4 3.8-3.1 10.3-7 10.3-7zm351.1 47.6s1.3 4-.2 6.8c-3.5 5.9-17.4 9.5-24.5 9.7-2.2.1 1.6-8 4.5-13.2 0 0-2.2 2.1-3.4 4.1l-2.1 3.2s-4.6.8-8.1.8c1.1-3.4 4.7-5.5 8-10.7 2.6-4.1 8.7-3.1 13.6-2.7 4.8.3 12.2 2 12.2 2zm-87.7-10.2s1.3 4-.2 6.8c-3.5 5.9-17.4 9.5-24.5 9.7-2.2.1 1.6-8 4.5-13.2 0 0-2.2 2.1-3.4 4.1l-2.1 3.2s-4.6.8-8.1.8c1.1-3.4 4.7-5.5 8-10.7 2.6-4.1 8.7-3.1 13.6-2.7 4.8.3 12.2 2 12.2 2zm-196.6 10.2s1.3 4-.2 6.8c-3.5 5.9-17.4 9.5-24.5 9.7-2.2.1 1.6-8 4.5-13.2 0 0-2.2 2.1-3.4 4.1l-2.1 3.2s-4.6.8-8.1.8c1.1-3.4 4.7-5.5 8-10.7 2.6-4.1 8.7-3.1 13.6-2.7 4.8.3 12.2 2 12.2 2z" transform="matrix(.15094 0 0 .15087 36.54 63.58)"/>
|
||||
<path d="M281.2 303.8c-13.6 9.5-43.7 3.9-41.6 11.9.5 2.5 0 7.7 4.6 19.9 3.3 10 5.9 15.2 8 19 1.8 3.2 2.9 2.2 5.1 3.5 2.2 1.3 1.6 3.4 3.4 5.3-3 3-7-.9-8.5.7.1 5.3 6.9 5.9 5.2 10.2-2.9 3.9-16.3 15-20.7 17.4-.7-2.9-.5-5.2.1-7.2.7-2.4 1-6.7-.3-10.3-1.3-3.5-4.1-7.2-4.4-11.7-.6-7.7-2.6-17-4-22.1-1.9-5.9-6.9-13.8-11.1-21.3-3.5-5.2-1-7.2-1-11.5s-2.6-7.6 2.4-11.5a94 94 0 0118.7-11.7c1.6-1.6 27-22 32.7-25m-47.6-143.9c4.4-9.9-3.3-14.1-3.3-14.1 1 .2-9.8-2.8-9.2-12.4.8-14.3 16.7-16.7 19-32.6.8-9-5.6-13.8-6.4-14-2-.4.9 4.7.1 8.8-3.6 16.5-29 17.8-33 39.8-4.7 21.9 20.7 31.7 18.6 31.3 0 0 9.8 3.1 14.2-6.8z" transform="matrix(.15094 0 0 .15087 36.54 63.58)"/>
|
||||
<path d="M203.7 128.7c-.5 1.1-11.1 1.1-18.7-2.7-5.4-2.8-6.3-5.2-6.7-6.1-1.8-.9-4.8-5.8-4.6-7.7-5.2-7.6-5-16.8-3.8-19.6.3-.4 2.2-.5 2.8 0 1 1.7 1.7 5.2 5.8 7.2 6.8 3.4 14.2.9 22.3 5a41.7 41.7 0 0110.1 9.9m289 161.3c1.1 3.8 2.8 8.3 5 12.9 5.4 11.1 15.3 15.3 17.2 29.7 3 15.2 1.1 33.4-4.9 49.7-1.9 4.7-3.9 15-7.2 22.7-2.1 5.1-5.4 8.5-7.1 10.5-1.9 2.3-4.2 2.9-6.3 4.1a13 13 0 00-5.1 5.1c4.8 1.3 22.2 2.5 27 1.8 4.1-2 1.3-10.9 3.3-11 2.2-.1 2.4 5.5 6.6 5.4 0-2.6 1.1-6.1 1-8.3-.2-7-3-5.8 3.6-24.1 10-27.1 16.7-33.7 20.2-40.6 2.2-4.3.4-14-.1-20.1 2.8-7.7-.4-27.4 3.7-38.5" transform="matrix(.15094 0 0 .15087 36.54 63.58)"/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--source: wappenwiki.org, CC BY-NC-SA 3.0-->
|
||||
<g id="cancer" stroke-width=".5">
|
||||
<g id="cancer" stroke-width=".3">
|
||||
<path d="M105.3 65.7c-.3 0-1.3 1.4-1.4 4.1-.1 2.2.8 5.9.8 5.9s1.8 6.3 2.6 7.1c.8 1 5-3.2 4.6-4.7-.4-1.5-2.2-1.7-2.9-2.8-.4-.3-.2-1-.3-1.1-.1-.2-.7 0-1-.3-.2-.4-.6-.4-.7-1 0-.6.8-.7.5-1.4-.3-.6-1.5-1-1.7-1.5 0-.6.5-1.2.4-1.5 0-.3-.8-.6-.8-1.2s.4-1 .2-1.4c-.1-.4 0-.2-.3-.2zm-10.5 0c.2 0 1.2 1.4 1.3 4.1.1 2.2-.8 5.9-.8 5.9s-1.8 6.3-2.6 7.1c-.8 1-5-3.2-4.6-4.7.4-1.5 2.2-1.7 3-2.8.3-.3.1-1 .2-1.1.1-.2.8 0 1-.3.2-.4.7-.4.7-1 .1-.6-.8-.7-.5-1.4s1.5-1 1.7-1.5c.1-.6-.5-1.2-.4-1.5 0-.3.9-.6.9-1.2s-.4-1-.3-1.4c.1-.4.1-.2.4-.2zm26.4 22.2s1-1.4 1.7-3.8l.6-2.9c0-1.4-.2-2.5 0-2.6.3 0 .6.9.8 2 .2 1.1.2 2.5.2 2.5s.7-.6 1.3-1.5c.4-.6.6-1.6.7-1.6.2.2 0 2.3-1 4.6a22.3 22.3 0 01-3.3 4.8l-1-1.5zm2.2 6.5s1.2-.8 2.3-2.6c.4-.6 1-1.4 1.2-2 .5-1.2.6-2.1.8-2 .2 0 .2.8 0 1.7 0 1-.5 2-.5 2s.8-.3 1.5-.8c.5-.4.9-1.1 1-1 .1.1-.6 1.7-2 3.3-1.6 1.6-4 3-4 3l-.3-1.6z"/>
|
||||
<path d="M106.9 62.7c1-.2 2.5 0 4 1.3 1.6 1.4 3.3 4 4.4 7.5.3.7 1 2.7 1.6 5.2 1.1 4.3.9 7.6 0 9.2-.5 1-2 2.3-3.4 2.7-1.4.4-1.9-.3-2.2-.3-1.3.4-2.5-1.7-3.4-3.5-.7-1.2-1.5-1.9-1.6-2.4-.2-.7.5-.1 1.8-1 .7-.4.1-.9.9-1.6.8-.7 1-.1.9-1-.8-2.4.3-2.7 0-3.6-.1-1-.8-1.4-.9-1.8-.1-.7.3-.6.1-1.6 0-.4-.3-.8-.4-1.2-.1-.8.5-1.4.4-1.7s-1.2-.6-1.3-1c-.1-.5.6-1.2.3-2.3-.2-.8-.6-1.3-.9-1-.5-.3-.3-.6-.6-.8-.6-.6-1 0-1-.2-.2 0 0-.6 1.3-.9zm18.9 39.1l.8-.7 2.3-1.7c1-.7 3-.4 2.9-.4-.1-.5-2-1.2-3.8-.4-1.7.8-3.4 2.5-3.4 2.5l1.2.7zm-1.3 5.6l.8-.6s1.8-1.2 2.6-2.1c.8-1 2.8-1.3 2.8-1.3-.3-.4-2.3-.3-3.8.9a26.6 26.6 0 01-3.6 2.4l1.2.7zm-21.7-22.9c.1-1.4-.8-5.7-.6-11.4.3-6.2 1.9-12 1.7-12.2a48 48 0 00-2.4 12.1c-.6 5.6 0 10.3 0 11.3s1.2 1.6 1.3.2zm-24 3.4s-1-1.4-1.7-3.8l-.6-2.9c0-1.4.2-2.5 0-2.6-.3 0-.6.9-.8 2-.2 1.1-.2 2.5-.2 2.5s-.7-.6-1.3-1.5c-.4-.6-.6-1.6-.7-1.6-.2.2 0 2.3 1 4.6 1.1 2.4 3.3 4.8 3.3 4.8l1-1.5zm-2.2 6.5s-1.2-.8-2.3-2.6c-.4-.6-1-1.4-1.2-2-.5-1.2-.6-2.1-.8-2-.2 0-.2.8 0 1.7 0 1 .5 2 .5 2s-.8-.3-1.5-.8c-.5-.4-.9-1.1-1-1-.1.1.6 1.7 2 3.3 1.6 1.6 4 3 4 3l.3-1.6zm16.5-31.7c-1-.2-2.5 0-4 1.3-1.6 1.4-3.3 4-4.4 7.5-.3.7-1 2.7-1.6 5.2-1.1 4.3-.9 7.6 0 9.2.5 1 2 2.3 3.4 2.7s1.9-.3 2.2-.3c1.3.4 2.5-1.7 3.4-3.5.7-1.2 1.5-1.9 1.6-2.4.2-.7-.5-.1-1.8-1-.7-.4-.1-.9-.9-1.6-.8-.7-1-.1-.9-1 .8-2.4-.3-2.7 0-3.6.1-1 .8-1.4.9-1.8.1-.7-.3-.6-.1-1.6 0-.4.3-.8.4-1.2.1-.8-.5-1.4-.4-1.7 0-.2 1.2-.6 1.3-1 .1-.5-.6-1.2-.3-2.3.2-.8.6-1.3.9-1 .5-.3.3-.6.6-.8.6-.6 1 0 1-.2.2 0 0-.6-1.3-.9zm-18.9 39.1l-.8-.7-2.3-1.7c-1-.7-3-.4-2.9-.4.1-.5 2-1.2 3.8-.4 1.7.8 3.4 2.5 3.4 2.5l-1.2.7zm1.3 5.6l-.8-.6s-1.8-1.2-2.6-2.1c-.8-1-2.8-1.3-2.8-1.3.3-.4 2.3-.3 3.8.9 1.4 1.2 3.6 2.4 3.6 2.4l-1.2.7zm21.7-22.9c-.1-1.4.8-5.7.6-11.4-.3-6.2-1.9-12-1.7-12.2.4-.2 1.8 6 2.4 12.1.5 5.6 0 10.3 0 11.3s-1.2 1.6-1.3.2zm-.7 45.5s-1.4.5-2.6 1.4c-1.3 1-2.3 2.1-2.6 2.5-.3.4-.4 1 .4 2.1.9 1.1 2.8 2.2 4 2.2 1 0 2-1.3 2-1.3v-4.6l-1.2-2.3zm7.1 0s1.4.5 2.6 1.4c1.4 1 2.3 2.1 2.6 2.5.3.4.4 1-.4 2.1-.9 1.1-2.8 2.2-3.9 2.2s-2-1.3-2-1.3v-4.6l1.1-2.3z"/>
|
||||
<path d="M102.2 128.7s1.1 2.4 1.1 6c0 2-.4 3.2-.7 3.6-.3.4-1.4 1-2.6 1-1.1 0-2.2-.6-2.5-1-.3-.4-.7-1.6-.7-3.7 0-3.5 1.1-5.9 1.1-5.9h4.3zm-7.4-.5c-1 0-.6.2-1.6.2s-2.2-1-3.3-1c-1 0-2.2.9-2.2 2.8s1.5 4.3 3 4.3 5.1-4.6 5.1-4.6 0-1.7-1-1.7zm10.5 0c1 0 .6.2 1.6.2s2.2-1 3.3-1c1 0 2.2.9 2.2 2.8s-1.5 4.3-3 4.3-5-4.6-5-4.6 0-1.7 1-1.7z"/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--source: wappenwiki.org, CC BY-NC-SA 3.0-->
|
||||
<g id="cock" stroke-width=".6">
|
||||
<g id="cock" stroke-width=".4">
|
||||
<path d="M116 112.6c1.3.8 2.1 2.2 2.4 3.7.2 1.5-.1 3-.5 4.5 1.2-.1 2.3-1 3-2.2.5-1.1.6-2.5.4-3.7-.2-1.3-.7-2.5-1.2-3.7"/>
|
||||
<path d="M121.2 110a9 9 0 013.2 3.1c.8 1.3 1.1 3 .7 4.4-.3 1-1 2-2 2.7.3-1.6.1-3.4-.8-4.8-.4-.7-1-1.1-1.4-1.8-.5-.7-.7-1.7-1.2-2.4"/>
|
||||
<path d="M121.6 109a8 8 0 013.4 1c1 .8 1.9 1.8 2.1 3 .2 1 0 2-.5 2.7a5 5 0 00-2.2-3.2c-.6-.4-1.2-.4-1.7-.7l-1.7-1.4m-39.5 13.9s-.4-1-1.3-.8c-.9.3-.8.9-.8.9v.6c0 .9-.6 1-1.2 1l-.4-.1c-.2 0 .5 1.2 2.1.8 1.8-.5 1.6-2.4 1.6-2.4zm-9.7-4s1 .5.7 1.4c-.4.8-1 .7-1 .7H71c-.8 0-1 .5-1 1.1l.1.5c0 .2-1.2-.6-.7-2.2.5-1.8 2.5-1.5 2.5-1.5zm-.8-4.9s.9.8.2 1.5-1.1.4-1.1.4l-.6-.3c-.8-.3-1.2.1-1.3.7l-.1.4c-.1.2-.9-1 .2-2.3 1-1.4 2.7-.4 2.7-.4zm3.7-2.7s0 1.2-1 1.2c-.8 0-1-.4-1-.4l-.1-.6c-.4-.8-1.1-.8-1.6-.5-.2.1-.2.3-.4.3s0-1.3 1.8-1.6c1.8-.3 2.3 1.6 2.3 1.6z"/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
|
@ -2,6 +2,6 @@
|
|||
<!--Author: Azgaar. License: Public Domain-->
|
||||
<g id="compassRose" stroke-width=".5">
|
||||
<path d="M100 60l6 28 14-8-8 15 28 5-28 6 8 14-14-8-6 28-6-28-14 8 8-14-28-6 28-6-8-14 14 8z"/>
|
||||
<path fill="#000" d="M100 61v39l7-6 5 1c2.7-5 5.3-10 8-15-4.3 4.3-8.7 9.7-13 14-2.3-10.7-4.7-22.3-7-33zm0 39l7 7c10.7-2.3 21.3-4.7 32-7zm0 0l-7 7c2.3 10.7 4.7 21.3 7 32zm0 0l-7-7-32 7zm-7-7l1-5-14-8zm0 14c-1.7-.3-3.3-.7-5-1-2.7 4.7-5.3 9.3-8 14l13-13zm14 0c-.3 1.7-.7 3.3-1 5 4.7 2.7 9.3 5.3 14 8z" opacity=".5"/>
|
||||
<path fill="#000" stroke="#000" fill-opacity=".5" stroke-opacity=".2" d="M100 61v39l7-6 5 1c2.7-5 5.3-10 8-15-4.3 4.3-8.7 9.7-13 14-2.3-10.7-4.7-22.3-7-33zm0 39l7 7c10.7-2.3 21.3-4.7 32-7zm0 0l-7 7c2.3 10.7 4.7 21.3 7 32zm0 0l-7-7-32 7zm-7-7l1-5-14-8zm0 14c-1.7-.3-3.3-.7-5-1-2.7 4.7-5.3 9.3-8 14l13-13zm14 0c-.3 1.7-.7 3.3-1 5 4.7 2.7 9.3 5.3 14 8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 613 B After Width: | Height: | Size: 652 B |
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--source: wappenwiki.org, CC BY-NC-SA 3.0-->
|
||||
<g id="crown" stroke-width=".86">
|
||||
<g id="crown" stroke-width=".6">
|
||||
<path d="M98.2 84c4.3 0 6.5-6.9 9.6-6.9 4.3 0 3.4 6.6 3.4 8.3 0 1.7 1 8.3-3.4 8.3-3.1 0-5.3-7-9.6-7"/>
|
||||
<path d="M101.8 84c-4.3 0-6.4-6.9-9.6-6.9-4.3 0-3.5 6.6-3.5 8.3 0 1.7-.8 8.3 3.5 8.3 3.1 0 5.3-7 9.6-7m33.5 4.9c1-6.2-3.5-11-2.7-15.5 1-6.2 5.6-3.5 6.9-3.1-1.5 8.7-2.6 18-2.6 18"/>
|
||||
<path d="M138.5 90.3c-4.3-1.4-5.2-8.8-8.3-9.9-4.2-1.3-4.4 5.2-4.8 7-.3 1.6-2.1 7.9 2 9.3 3.1 1 6.4-5.1 10.5-3.7M99 86.4c0-6.5-5.2-9.6-5.2-14.3 0-6.4 5-5.3 6.3-5.3s6.2-1.1 6.2 5.3c0 4.6-5.2 7.8-5.2 14.3m-36.4 5.2c-1-6.2 3.5-11 2.7-15.5-1-6.2-5.6-3.5-7-3.1a474 474 0 012.7 18"/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<g id="escallop" stroke-width=".375">
|
||||
<path stroke-width=".675" d="M106.64 78.61s4.36.63 6.82-2.32c2.45-2.49 1.39-3 3.37-4.27 1.96-1.26 3.89-1.8 3.89-3.8 0-1.69-1.48-1.62-1.66-1.62-2.43 0-1.78.72-9.68 1.9-2.47.12-2.52.05-3.9 0m-10.9.05c-1.37.02-1.44.1-3.92-.02-7.9-1.17-7.24-1.89-9.67-1.89-.18 0-1.67-.25-1.67 1.46 0 1.98 1.9 2.7 3.89 3.94 1.98 1.26.93 1.75 3.38 4.28 2.45 2.94 6.82 2.3 6.82 2.3"/>
|
||||
<path stroke-width=".675" d="M101.19 136.93c.95.76 2.03 1.98 4.12 1.84 2.77-.72 2.1-2.1 3.55-2.65 2.01-.8 4.32.16 6.75-1.6 1.4-1.01 1.22-2.18.8-3.17.15-.83.9-1.03 1.8-.99 1.34.07 2.89.72 4.1-.07 2.89-1.84.14-2.27 2.42-3.96 1.46-.94 2.85-.06 4.14-1.48 1.23-1.13.24-3.33-.75-3.85-.45-1.96.97-1.7 1.38-1.75.74-.12 2.58.45 3.7-.79 1.29-1.5-.51-3.06.05-3.8.54-.77 2.55-1.3 2.34-3.2-.22-1.9-1.2-2.43-1.95-2.52-.97-1.75.1-1.51.47-1.7.67-.3 2.8-.98 3.53-2.46.86-1.8-1.12-3.22-.76-4.07.3-.9 2.09-1.92 1.35-3.72-.72-1.8-1.78-1.66-2.88-1.82-1.04.02-2.12-1.12-.9-1.71 5.1-2.65 1-5.49.47-7.3 2.65-2.87.27-4.35-.52-5.3-.8-.94-8.91 8.66-17.62 8.57-8.86 0-9.13-11.13-9.9-16.17-.36-2.16-.76-4.95-1.48-7.52-.99-3.5-2.57-4.9-5.36-4.9-2.81 0-4.38 1.4-5.37 4.9-.72 2.57-1.13 5.38-1.46 7.52-.79 5.04-1.04 16.17-9.93 16.17-8.7.09-16.83-9.5-17.6-8.57-.8.95-3.2 2.45-.55 5.3-.5 1.83-4.62 4.65.49 7.3 1.24.6.14 1.71-.9 1.71-1.1.16-2.14.05-2.88 1.82-.72 1.8 1.06 2.84 1.35 3.72.34.85-1.62 2.29-.76 4.07.72 1.48 2.85 2.16 3.53 2.47.36.18 1.44-.1.47 1.69-.76.09-1.71.6-1.93 2.52-.21 1.9 1.77 2.43 2.3 3.2.58.72-1.22 2.3.08 3.78 1.1 1.23 2.95.69 3.69.81.41.06 1.82-.21 1.37 1.75-1 .52-2 2.75-.74 3.85 1.28 1.39 2.66.52 4.14 1.48 2.3 1.67-.45 2.12 2.45 3.96 1.2.77 2.75.14 4.08.07.9-.04 1.68.16 1.8.99-.41.99-.6 2.16.81 3.17 2.43 1.74 4.77.8 6.75 1.6 1.44.56.78 1.89 3.55 2.66 2.09.15 3.17-1.08 4.12-1.85.36-.3 1.91-.3 2.29 0z"/>
|
||||
<path stroke-width=".5" d="M106.64 78.61s4.36.63 6.82-2.32c2.45-2.49 1.39-3 3.37-4.27 1.96-1.26 3.89-1.8 3.89-3.8 0-1.69-1.48-1.62-1.66-1.62-2.43 0-1.78.72-9.68 1.9-2.47.12-2.52.05-3.9 0m-10.9.05c-1.37.02-1.44.1-3.92-.02-7.9-1.17-7.24-1.89-9.67-1.89-.18 0-1.67-.25-1.67 1.46 0 1.98 1.9 2.7 3.89 3.94 1.98 1.26.93 1.75 3.38 4.28 2.45 2.94 6.82 2.3 6.82 2.3"/>
|
||||
<path stroke-width=".5" d="M101.19 136.93c.95.76 2.03 1.98 4.12 1.84 2.77-.72 2.1-2.1 3.55-2.65 2.01-.8 4.32.16 6.75-1.6 1.4-1.01 1.22-2.18.8-3.17.15-.83.9-1.03 1.8-.99 1.34.07 2.89.72 4.1-.07 2.89-1.84.14-2.27 2.42-3.96 1.46-.94 2.85-.06 4.14-1.48 1.23-1.13.24-3.33-.75-3.85-.45-1.96.97-1.7 1.38-1.75.74-.12 2.58.45 3.7-.79 1.29-1.5-.51-3.06.05-3.8.54-.77 2.55-1.3 2.34-3.2-.22-1.9-1.2-2.43-1.95-2.52-.97-1.75.1-1.51.47-1.7.67-.3 2.8-.98 3.53-2.46.86-1.8-1.12-3.22-.76-4.07.3-.9 2.09-1.92 1.35-3.72-.72-1.8-1.78-1.66-2.88-1.82-1.04.02-2.12-1.12-.9-1.71 5.1-2.65 1-5.49.47-7.3 2.65-2.87.27-4.35-.52-5.3-.8-.94-8.91 8.66-17.62 8.57-8.86 0-9.13-11.13-9.9-16.17-.36-2.16-.76-4.95-1.48-7.52-.99-3.5-2.57-4.9-5.36-4.9-2.81 0-4.38 1.4-5.37 4.9-.72 2.57-1.13 5.38-1.46 7.52-.79 5.04-1.04 16.17-9.93 16.17-8.7.09-16.83-9.5-17.6-8.57-.8.95-3.2 2.45-.55 5.3-.5 1.83-4.62 4.65.49 7.3 1.24.6.14 1.71-.9 1.71-1.1.16-2.14.05-2.88 1.82-.72 1.8 1.06 2.84 1.35 3.72.34.85-1.62 2.29-.76 4.07.72 1.48 2.85 2.16 3.53 2.47.36.18 1.44-.1.47 1.69-.76.09-1.71.6-1.93 2.52-.21 1.9 1.77 2.43 2.3 3.2.58.72-1.22 2.3.08 3.78 1.1 1.23 2.95.69 3.69.81.41.06 1.82-.21 1.37 1.75-1 .52-2 2.75-.74 3.85 1.28 1.39 2.66.52 4.14 1.48 2.3 1.67-.45 2.12 2.45 3.96 1.2.77 2.75.14 4.08.07.9-.04 1.68.16 1.8.99-.41.99-.6 2.16.81 3.17 2.43 1.74 4.77.8 6.75 1.6 1.44.56.78 1.89 3.55 2.66 2.09.15 3.17-1.08 4.12-1.85.36-.3 1.91-.3 2.29 0z"/>
|
||||
<g fill="#000" stroke="none">
|
||||
<path d="M73.56 123.08a52.6 52.6 0 01-5.23 12.62c.74-.28 1.12-1.97 1.67-2.7a47.64 47.64 0 003.56-9.92zm-14.7.33c3.16-.6 6.05-2.41 8.18-4.76-2.27 2.18-5.06 3.92-8.18 4.54v.22zm37.08 3.68c-.1-.44-.47-.76-.84-.98-.11.4.9.3 1.15.07-.41.13-.95-.12-1.34-.02.3.33.76.55 1.03.93zm2.2-7.59c-.19-.39-.88-.4-.4-.64 1.11-.87-1.06.1-.08.41.15.1.32.14.48.23zm-7.51 14.21c.2-1.05-.33-1.17.78-.93.67-.08-.4-.16-.61-.25-.5-.23-.12 1.04-.17 1.18zm-8.67 4.95c.34-.41.53-.93.56-1.46-.46.18.45.97.88.95-.44-.2-.75-.61-.94-1.03-.25.35-.23 1.07-.5 1.52zm15.2-27.37c-.18-.26-1.39-.74-.96-.4.34.14 1.25-.94.61-.45-.28.25-1.35.23-.54.55.3.08.62.24.89.3zm-.56 3.34c-2.26.69-4.7.5-7 .48 2.27.3 4.63.35 6.87-.2.2-.07.23-.05.13-.28zm-.4 8.59c-3.12-.6-5.91-2.4-8.18-4.52a15.1 15.1 0 008.17 4.75l.01-.23zm-2.3 5.72c-3.2-.93-5.55-3.52-7.64-5.99-.15-.1-1.8-2.38-.9-1.02 2.13 2.84 4.58 5.83 8.02 7.06.29-.07.52.37.52-.05zm-6.25 6.16c-1.63-.87-2.28-2.82-3.1-4.37a69.75 69.75 0 01-2.96-7.71c1.04 3.81 2.4 7.6 4.53 10.95.27.28 1.2 1.68 1.53 1.13zm-9.39 3.71c-.52-3.91-.43-7.89-.56-11.83-.03-1.02-.02-2.1-.11-3.08-.03 4.65.04 9.3.31 13.94.17.18-.11 1.28.36.97zM59.1 127.09c.2-.45.8-.6 1-.98-.31.02-1.23.04-1.26.08.28.28 1.51.02.95-.04-.31.22-.68.53-.69.94zm-2.2-7.59c.41-.08 1.13-.52.45-.82-.83-.67.1.53.15.28-.2.18-.43.34-.6.54zm7.51 14.21c.05-.44.3-1.5-.41-1.12-.85.07-.56.27.15.15.56-.46-.05.49.26.97zm8.67 4.95c-.25-.47-.33-1.02-.44-1.5-.3.27-.51.83-1 1.01.43-.02.98-.43 1.07-.92-.41 0 0 .75.1 1.04.07.14.14.27.27.37zm-15.2-27.36c.25-.14 1.58-.38 1-.6-.31.13-1.25-.74-.78-.16.19.51 1.33.23.53.3-.25.13-.58.21-.75.46zm7.56 4c-2.41.3-4.85.32-7.28.22.75.5 2.33.18 3.46.2 1.28-.05 2.57-.11 3.82-.42z" transform="matrix(1.8 0 0 1.8 -39.5 -113)"/>
|
||||
<path d="M66.94 118.5c-1.81 1.5-3.8 2.86-6.1 3.48-.73.2-1.49.31-2.25.32.06.45.88.05 1.22.1 2.66-.41 5.04-1.9 6.97-3.72l.16-.18zm3.25 2.99a35.21 35.21 0 01-8.35 8.46c.32.3.76-.47 1.11-.6 2.6-2 4.92-4.37 6.75-7.1.18-.24.31-.52.49-.76z" transform="matrix(1.8 0 0 1.8 -39.5 -113)"/>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
|
@ -2,9 +2,9 @@
|
|||
<!--Author: Azgaar. License: Public Domain-->
|
||||
<g id="lozengeFaceted">
|
||||
<polygon points="100 55 130 100 100 145 70 100"/>
|
||||
<g fill="#000">
|
||||
<polygon points="100 99 130 99 100 55" opacity=".2"/>
|
||||
<polygon points="99 100 70 100 99 145" opacity=".2"/>
|
||||
<g fill="#000" stroke="none">
|
||||
<polygon points="100 100 130 100 100 55" opacity=".2"/>
|
||||
<polygon points="100 100 70 100 100 145" opacity=".2"/>
|
||||
<polygon points="100 100 130 100 100 145" opacity=".3"/>
|
||||
</g>
|
||||
</g>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 464 B |
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--Author: Azgaar. License: Public Domain-->
|
||||
<g id="mullet6Pierced">
|
||||
<path d="M100 60l7.64 26.77 27-6.77-19.36 20 19.36 20-27-6.77L100 140l-7.64-26.77-27 6.77 19.36-20-19.36-20 27 6.77zm-8 40a8 8 0 1016 0 8 8 0 10-16 0"/>
|
||||
<path d="M100 60l7.64 26.77 27-6.77-19.36 20 19.36 20-27-6.77L100 140l-7.64-26.77-27 6.77 19.36-20-19.36-20 27 6.77z M 108,100 a 8,8 0 0 0 -8,-8 8,8 0 0 0 -8,8 8,8 0 0 0 8,8 8,8 0 0 0 8,-8 z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 378 B |
|
|
@ -1,6 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="#d7374a" stroke="#000" viewBox="50 50 200 200">
|
||||
<!--Author: Azgaar. License: Public Domain-->
|
||||
<g id="mulletPierced">
|
||||
<path d="M100 60l8.98 27.64h29.06l-23.51 17.08 8.98 27.64L100 115.28l-23.51 17.08 8.98-27.64-23.51-17.08h29.06zm-8 40a8 8 0 1016 0 8 8 0 10-16 0"/>
|
||||
<path d="M100 60l8.98 27.64h29.06l-23.51 17.08 8.98 27.64L100 115.28l-23.51 17.08 8.98-27.64-23.51-17.08h29.06z M 108,100 a 8,8 0 0 0 -8,-8 8,8 0 0 0 -8,8 8,8 0 0 0 8,8 8,8 0 0 0 8,-8 z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 372 B |
14
index.css
|
|
@ -1361,10 +1361,7 @@ div.states>.culturePopulation {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.states > .cultureBase,
|
||||
div.states > .cultureType,
|
||||
div.states > .stateCulture,
|
||||
div.states > .diplomacyRelations {
|
||||
div.states > select {
|
||||
width: 4.6em;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
|
|
@ -1374,10 +1371,7 @@ div.states > .diplomacyRelations {
|
|||
appearance: none;
|
||||
}
|
||||
|
||||
div.states > .cultureBase {
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
div.states > .cultureBase,
|
||||
div.states > .burgName,
|
||||
div.states > .burgState,
|
||||
div.states > .burgCulture {
|
||||
|
|
@ -2003,8 +1997,8 @@ svg.button {
|
|||
}
|
||||
|
||||
#alertMessage ul {
|
||||
padding-left: 15px;
|
||||
margin: 10px 0;
|
||||
padding-left: 1.2em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.pseudoLink {
|
||||
|
|
|
|||
126
index.html
|
|
@ -1107,8 +1107,67 @@
|
|||
<output id="religionsOutput" data-stored="religions" value="auto"></output>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<tr data-tip="Select emblem shape to be applied on generation. Can be set indivudually in Emblem editor">
|
||||
<p data-tip="Tool settings that don't affect maps. Changes are getting applied immediately">Generator settings:</p>
|
||||
<table>
|
||||
<tr data-tip="Set what Generator should do on opening">
|
||||
<td></td>
|
||||
<td>Onload behavior</td>
|
||||
<td>
|
||||
<select id="onloadMap" data-stored="onloadMap">
|
||||
<option value="random" selected>Generate random map</option>
|
||||
<option value="saved">Open last saved map</option>
|
||||
</select>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set user interface size. Please note browser zoom also affects interface size (Ctrl + or Ctrl - to change)">
|
||||
<td></td>
|
||||
<td>Interface size</td>
|
||||
<td>
|
||||
<input id="uiSizeInput" data-stored="uiSize" type="range" min=.6 max=3 step=.1 value=1>
|
||||
</td>
|
||||
<td>
|
||||
<input id="uiSizeOutput" data-stored="uiSize" type="number" min=.6 max=3 step=.1 value=1>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set tooltip size">
|
||||
<td></td>
|
||||
<td>Tooltip size</td>
|
||||
<td>
|
||||
<input id="tooltipSizeInput" data-stored="tooltipSize" type="range" min=4 max=32 value=14>
|
||||
</td>
|
||||
<td>
|
||||
<input id="tooltipSizeOutput" data-stored="tooltipSize" type="number" min=4 max=32 value=14>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set dialog and tool windows transparency">
|
||||
<td></td>
|
||||
<td>Transparency</td>
|
||||
<td>
|
||||
<input id="transparencyInput" data-stored="transparency" type="range" min=0 max=100 value=15>
|
||||
</td>
|
||||
<td>
|
||||
<input id="transparencyOutput" data-stored="transparency" type="number" min=0 max=100 value=15>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select speech synthesis voice to pronounce generated names">
|
||||
<td></td>
|
||||
<td>Speaker voice</td>
|
||||
<td>
|
||||
<select id="speakerVoice" data-stored="speakerVoice"></select>
|
||||
</td>
|
||||
<td>
|
||||
<span id="speakerTest" data-tip="Click to test the voice" style="cursor: pointer">🔊</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select emblem shape. Can be changed indivudually in Emblem editor">
|
||||
<td>
|
||||
<i data-locked=0 id="lock_emblemShape" class="icon-lock"></i>
|
||||
</td>
|
||||
|
|
@ -1184,65 +1243,6 @@
|
|||
<svg class="emblemShapePreview" viewBox="0 0 200 210"><path id="emblemShapeImage"/></svg>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p data-tip="Tool settings that don't affect maps. Changes are getting applied immediately">Generator settings:</p>
|
||||
<table>
|
||||
<tr data-tip="Set what Generator should do on opening">
|
||||
<td></td>
|
||||
<td>Onload behavior</td>
|
||||
<td>
|
||||
<select id="onloadMap" data-stored="onloadMap">
|
||||
<option value="random" selected>Generate random map</option>
|
||||
<option value="saved">Open last saved map</option>
|
||||
</select>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set user interface size. Please note browser zoom also affects interface size (Ctrl + or Ctrl - to change)">
|
||||
<td></td>
|
||||
<td>Interface size</td>
|
||||
<td>
|
||||
<input id="uiSizeInput" data-stored="uiSize" type="range" min=.6 max=3 step=.1 value=1>
|
||||
</td>
|
||||
<td>
|
||||
<input id="uiSizeOutput" data-stored="uiSize" type="number" min=.6 max=3 step=.1 value=1>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set tooltip size">
|
||||
<td></td>
|
||||
<td>Tooltip size</td>
|
||||
<td>
|
||||
<input id="tooltipSizeInput" data-stored="tooltipSize" type="range" min=4 max=32 value=14>
|
||||
</td>
|
||||
<td>
|
||||
<input id="tooltipSizeOutput" data-stored="tooltipSize" type="number" min=4 max=32 value=14>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set dialog and tool windows transparency">
|
||||
<td></td>
|
||||
<td>Transparency</td>
|
||||
<td>
|
||||
<input id="transparencyInput" data-stored="transparency" type="range" min=0 max=100 value=15>
|
||||
</td>
|
||||
<td>
|
||||
<input id="transparencyOutput" data-stored="transparency" type="number" min=0 max=100 value=15>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select speech synthesis voice to pronounce generated names">
|
||||
<td></td>
|
||||
<td>Speaker voice</td>
|
||||
<td>
|
||||
<select id="speakerVoice" data-stored="speakerVoice"></select>
|
||||
</td>
|
||||
<td>
|
||||
<span id="speakerTest" data-tip="Click to test the voice" style="cursor: pointer">🔊</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set minimum and maximum possible zoom level">
|
||||
<td></td>
|
||||
|
|
@ -1289,6 +1289,7 @@
|
|||
<button id="editNamesBaseButton" data-tip="Click to open Namesbase Editor. Shortcut: Shift + N">Namesbase</button>
|
||||
<button id="editZonesButton" data-tip="Click to open Zones Editor. Shortcut: Shift + Z">Zones</button>
|
||||
<button id="editReligions" data-tip="Click to open Religions Editor. Shortcut: Shift + R">Religions</button>
|
||||
<button id="editEmblemButton" data-tip="Click to open Emblem Editor. Shortcut: Shift + Y">Emblems</button>
|
||||
<button id="editUnitsButton" data-tip="Click to open Units Editor. Shortcut: Shift + Q">Units</button>
|
||||
<button id="editNotesButton" data-tip="Click to open Notes Editor. Shortcut: Shift + O">Notes</button>
|
||||
</div>
|
||||
|
|
@ -2603,6 +2604,7 @@
|
|||
<div style="left:24.9em" data-tip="Click to sort by culture area" class="sortable hide" data-sortby="area">Area </div>
|
||||
<div style="left:28.8em" data-tip="Click to sort by culture population" class="sortable hide icon-sort-number-down" data-sortby="population">Population </div>
|
||||
<div style="left:35.8em" data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase </div>
|
||||
<div style="left:42.9em" data-tip="Click to sort by culture emblems shape" class="sortable alphabetically hide" data-sortby="emblems">Emblems </div>
|
||||
</div>
|
||||
<div id="culturesBody" class="table" data-type="absolute"></div>
|
||||
|
||||
|
|
@ -2666,7 +2668,7 @@
|
|||
|
||||
<div id="namesbaseBottom">
|
||||
<button id="namesbaseUpdateExamples" data-tip="Re-generate examples based on provided data" class="icon-arrows-cw"></button>
|
||||
<button id="namesbaseAnalize" data-tip="Analize namesbase to get a validity and quality overview" class="icon-flask"></button>
|
||||
<button id="namesbaseAnalize" data-tip="Analyze namesbase to get a validity and quality overview" class="icon-flask"></button>
|
||||
<button id="namesbaseAdd" data-tip="Add new namesbase" class="icon-plus"></button>
|
||||
<button id="namesbaseDefault" data-tip="Restore default namesbase" class="icon-cancel"></button>
|
||||
<button id="namesbaseDownload" data-tip="Download namesbase to PC" class="icon-download"></button>
|
||||
|
|
@ -2863,8 +2865,8 @@
|
|||
<div id="emblemsBottom">
|
||||
<button id="emblemsRegenerate" data-tip="Regenerate emblem" class="icon-shuffle"></button>
|
||||
<button id="emblemsArmoria" data-tip="Edit the emblem in Armoria - dedicated heraldry editor. Download emblem and upload it back map the generator" class="icon-brush"></button>
|
||||
<button id="emblemsUpload" data-tip="Upload png, jpg or svg image from Armoria or other sources as emblem" class="icon-upload"></button>
|
||||
<button id="emblemsDownload" data-tip="Set size, select file format and download emblem image" class="icon-download"></button>
|
||||
<button id="emblemsUpload" data-tip="Upload png, jpg or svg image from Armoria or other sources as emblem" class="icon-upload"></button>
|
||||
<button id="emblemsGallery" data-tip="Download emblems gallery as html document (open in browser; downloading takes some time)" class="icon-layer-group"></button>
|
||||
<button id="emblemsFocus" data-tip="Show emblem associated area or place" class="icon-target"></button>
|
||||
</div>
|
||||
|
|
|
|||
3
libs/jquery-ui.css
vendored
|
|
@ -434,6 +434,9 @@ body .ui-dialog {
|
|||
font-family: Arial,Helvetica,sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget button[class^="icon-"] {
|
||||
padding: 1px 6px;
|
||||
}
|
||||
.ui-widget.ui-widget-content {
|
||||
border: 1px solid #5e4fa2;
|
||||
color: #333333;
|
||||
|
|
|
|||
43
main.js
|
|
@ -338,7 +338,7 @@ function applyDefaultBiomesSystem() {
|
|||
}
|
||||
|
||||
function showWelcomeMessage() {
|
||||
const post = link("https://www.reddit.com/r/FantasyMapGenerator/comments/ft5b41/update_v15/", "Main changes:"); // announcement on Reddit
|
||||
const post = "Main changes:" //link("https://www.reddit.com/r/FantasyMapGenerator/comments/ft5b41/update_v15/", "Main changes:");
|
||||
const changelog = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "previous version");
|
||||
const reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit community");
|
||||
const discord = link("https://discordapp.com/invite/X7E84HU", "Discord server");
|
||||
|
|
@ -347,18 +347,19 @@ function showWelcomeMessage() {
|
|||
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
|
||||
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
|
||||
<ul>${post}
|
||||
<li>Emblems generation</li>
|
||||
<li>Emblem editor integrated with ${link("https://azgaar.github.io/Armoria", "Armoria")}</li>
|
||||
<li>State, province and burg Emblems generation</li>
|
||||
<li>Emblem editor integrated with ${link("https://azgaar.github.io/Armoria", "Armoria")} — our new dedicated Heraldry generator and editor</li>
|
||||
<li>Burg editor screen update</li>
|
||||
<li>Speak name functionality</li>
|
||||
</ul>
|
||||
<img src="https://raw.githubusercontent.com/Azgaar/Armoria/master/public/preview.png" alt="Armoria preview" width="100%" height="auto"/>
|
||||
<p>Join our ${discord} and ${reddit} to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>
|
||||
<span>Thanks for all supporters on ${patreon}!</i></span>`;
|
||||
|
||||
$("#alert").dialog(
|
||||
{resizable: false, title: "Fantasy Map Generator update", width: "28em",
|
||||
buttons: {OK: function() {$(this).dialog("close")}},
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
position: {my: "center center-80", at: "center", of: "svg"},
|
||||
close: () => localStorage.setItem("version", version)}
|
||||
);
|
||||
}
|
||||
|
|
@ -545,7 +546,6 @@ function generate() {
|
|||
reGraph();
|
||||
drawCoastline();
|
||||
|
||||
elevateLakes();
|
||||
Rivers.generate();
|
||||
defineBiomes();
|
||||
|
||||
|
|
@ -570,7 +570,7 @@ function generate() {
|
|||
Names.getMapName();
|
||||
|
||||
WARN && console.warn(`TOTAL: ${rn((performance.now()-timeStart)/1000,2)}s`);
|
||||
INFO && showStatistics();
|
||||
showStatistics();
|
||||
INFO && console.groupEnd("Generated Map " + seed);
|
||||
}
|
||||
catch(error) {
|
||||
|
|
@ -626,7 +626,7 @@ function calculateVoronoi(graph, points) {
|
|||
TIME && console.timeEnd("calculateDelaunay");
|
||||
|
||||
TIME && console.time("calculateVoronoi");
|
||||
const voronoi = Voronoi(delaunay, allPoints, n);
|
||||
const voronoi = new Voronoi(delaunay, allPoints, n);
|
||||
graph.cells = voronoi.cells;
|
||||
graph.cells.i = n < 65535 ? Uint16Array.from(d3.range(n)) : Uint32Array.from(d3.range(n)); // array of indexes
|
||||
graph.vertices = voronoi.vertices;
|
||||
|
|
@ -1137,22 +1137,6 @@ function reMarkFeatures() {
|
|||
TIME && console.timeEnd("reMarkFeatures");
|
||||
}
|
||||
|
||||
// temporary elevate some lakes to resolve depressions and flux the water to form an open (exorheic) lake
|
||||
function elevateLakes() {
|
||||
if (templateInput.value === "Atoll") return; // no need for Atolls
|
||||
TIME && console.time('elevateLakes');
|
||||
const cells = pack.cells, features = pack.features;
|
||||
const maxCells = cells.i.length / 100; // size limit; let big lakes be closed (endorheic)
|
||||
cells.i.forEach(i => {
|
||||
if (cells.h[i] >= 20) return;
|
||||
if (features[cells.f[i]].group !== "freshwater" || features[cells.f[i]].cells > maxCells) return;
|
||||
cells.h[i] = 20;
|
||||
//debug.append("circle").attr("cx", cells.p[i][0]).attr("cy", cells.p[i][1]).attr("r", .5).attr("fill", "blue");
|
||||
});
|
||||
|
||||
TIME && console.timeEnd('elevateLakes');
|
||||
}
|
||||
|
||||
// assign biome id for each cell
|
||||
function defineBiomes() {
|
||||
TIME && console.time("defineBiomes");
|
||||
|
|
@ -1160,7 +1144,6 @@ function defineBiomes() {
|
|||
cells.biome = new Uint8Array(cells.i.length); // biomes array
|
||||
|
||||
for (const i of cells.i) {
|
||||
if (f[cells.f[i]].group === "freshwater") cells.h[i] = 19; // de-elevate lakes; here to save some resources
|
||||
const t = temp[cells.g[i]]; // cell temperature
|
||||
const h = cells.h[i]; // cell height
|
||||
const m = h < 20 ? 0 : calculateMoisture(i); // cell moisture
|
||||
|
|
@ -1718,11 +1701,7 @@ function addZones(number = 1) {
|
|||
function showStatistics() {
|
||||
const template = templateInput.value;
|
||||
const templateRandom = locked("template") ? "" : "(random)";
|
||||
|
||||
mapId = Date.now(); // unique map id is it's creation date number
|
||||
mapHistory.push({seed, width:graphWidth, height:graphHeight, template, created:mapId});
|
||||
console.log(`
|
||||
Seed: ${seed}
|
||||
const stats = ` Seed: ${seed}
|
||||
Canvas size: ${graphWidth}x${graphHeight}
|
||||
Template: ${template} ${templateRandom}
|
||||
Points: ${grid.points.length}
|
||||
|
|
@ -1733,7 +1712,11 @@ function showStatistics() {
|
|||
Burgs: ${pack.burgs.length-1}
|
||||
Religions: ${pack.religions.length-1}
|
||||
Culture set: ${culturesSet.selectedOptions[0].innerText}
|
||||
Cultures: ${pack.cultures.length-1}`);
|
||||
Cultures: ${pack.cultures.length-1}`;
|
||||
|
||||
mapId = Date.now(); // unique map id is it's creation date number
|
||||
mapHistory.push({seed, width:graphWidth, height:graphHeight, template, created:mapId});
|
||||
INFO && console.log(stats);
|
||||
}
|
||||
|
||||
const regenerateMap = debounce(function() {
|
||||
|
|
|
|||
|
|
@ -208,8 +208,12 @@
|
|||
if (cells.haven[i] && pack.features[cells.f[cells.haven[i]]].type === "lake") return "Lake";
|
||||
if (cells.h[i] > 60) return "Highland";
|
||||
if (cells.r[i] && cells.r[i].length > 100 && cells.r[i].length >= pack.rivers[0].length) return "River";
|
||||
if ([1, 2, 3, 4].includes(cells.biome[i])) return "Nomadic";
|
||||
if (cells.biome[i] > 4 && cells.biome[i] < 10) return "Hunting";
|
||||
|
||||
if (!cells.burg[i] || pack.burgs[cells.burg[i]].population < 6) {
|
||||
if (population < 5 && [1, 2, 3, 4].includes(cells.biome[i])) return "Nomadic";
|
||||
if (cells.biome[i] > 4 && cells.biome[i] < 10) return "Hunting";
|
||||
}
|
||||
|
||||
return "Generic";
|
||||
}
|
||||
|
||||
|
|
@ -419,7 +423,7 @@
|
|||
const hull = getHull(start, s.i, s.cells / 10);
|
||||
const points = [...hull].map(v => pack.vertices.p[v]);
|
||||
const delaunay = Delaunator.from(points);
|
||||
const voronoi = Voronoi(delaunay, points, points.length);
|
||||
const voronoi = new Voronoi(delaunay, points, points.length);
|
||||
const chain = connectCenters(voronoi.vertices, s.pole[1]);
|
||||
const relaxed = chain.map(i => voronoi.vertices.p[i]).filter((p, i) => i%15 === 0 || i+1 === chain.length);
|
||||
paths.push([s.i, relaxed]);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,14 @@
|
|||
metals: { argent: 3, or: 2 },
|
||||
colours: { gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2 },
|
||||
stains: { murrey: 1, sanguine: 1, tenné: 1 },
|
||||
patterns: { semy: 1, vair: 2, vairInPale: 1, vairEnPointe: 2, ermine: 2, chequy: 5, lozengy: 2, fusily: 1, pally: 4, barry: 4, gemelles: 1, bendy: 3, bendySinister: 2, palyBendy: 1, pappellony: 2, masoned: 3, fretty: 2 }
|
||||
patterns: {
|
||||
semy: 8, ermine: 6,
|
||||
vair: 4, counterVair: 1, vairInPale: 1, vairEnPointe: 2, vairAncien: 2,
|
||||
potent: 2, counterPotent: 1, potentInPale: 1, potentEnPointe: 1,
|
||||
chequy: 8, lozengy: 5, fusily: 2, pally: 8, barry: 10, gemelles: 1,
|
||||
bendy: 8, bendySinister: 4, palyBendy: 2, barryBendy: 1,
|
||||
pappellony: 2, pappellony2: 3, scaly: 1, plumetty: 1,
|
||||
masoned: 6, fretty: 3, grillage: 1, chainy: 1, maily: 2, honeycombed: 1 }
|
||||
}
|
||||
|
||||
const charges = {
|
||||
|
|
@ -23,7 +30,7 @@
|
|||
conventional: {
|
||||
lozenge: 2, fusil: 4, mascle: 4, rustre: 2, lozengeFaceted: 3, lozengePloye: 1, roundel: 4, roundel2: 3, annulet: 4,
|
||||
mullet: 5, mulletPierced: 1, mulletFaceted: 1, mullet4: 3, mullet6: 4, mullet6Pierced: 1, mullet6Faceted: 1, mullet7: 1, mullet8: 1, mullet10: 1,
|
||||
estoile: 1, compassRose: 1, billet: 5, delf: 0, triangle: 3, trianglePierced: 1, goutte: 4, heart: 4, pique: 2, сarreau: 1, trefle: 2,
|
||||
estoile: 1, compassRose: 1, billet: 5, delf: 0, triangle: 3, trianglePierced: 1, goutte: 4, heart: 4, pique: 2, carreau: 1, trefle: 2,
|
||||
fleurDeLis: 6, sun: 3, sunInSplendour: 1, crescent: 5, fountain: 1
|
||||
},
|
||||
crosses: {
|
||||
|
|
@ -189,6 +196,18 @@
|
|||
}
|
||||
};
|
||||
|
||||
const shields = {
|
||||
types: {basic: 10, regional: 2, historical: 1, specific: 1, banner: 1, simple: 2, fantasy: 1, middleEarth: 0},
|
||||
basic: {heater: 12, spanish: 6, french: 1},
|
||||
regional: {horsehead: 1, horsehead2: 1, polish: 1, hessen: 1, swiss: 1},
|
||||
historical: {boeotian: 1, roman: 2, kite: 1, oldFrench: 5, renaissance: 2, baroque: 2},
|
||||
specific: {targe: 1, targe2: 0, pavise: 5, wedged: 10},
|
||||
banner: {flag: 1, pennon: 0, guidon: 0, banner: 0, dovetail: 1, gonfalon: 5, pennant: 0},
|
||||
simple: {round: 12, oval: 6, vesicaPiscis: 1, square: 1, diamond: 2, no: 0},
|
||||
fantasy: {fantasy1: 2, fantasy2: 2, fantasy3: 1, fantasy4: 1, fantasy5: 3},
|
||||
middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1}
|
||||
}
|
||||
|
||||
const generate = function(parent, kinship, dominion, type) {
|
||||
if (parent === "custom") parent = null;
|
||||
let usedPattern = null, usedTinctures = [];
|
||||
|
|
@ -398,10 +417,10 @@
|
|||
|
||||
function definePattern(pattern, element, size = "") {
|
||||
let t1 = null, t2 = null;
|
||||
if (P(.15)) size = "-small";
|
||||
else if (P(.05)) size = "-smaller";
|
||||
else if (P(.035)) size = "-big";
|
||||
else if (P(.001)) size = "-smallest";
|
||||
if (P(.1)) size = "-small";
|
||||
else if (P(.1)) size = "-smaller";
|
||||
else if (P(.01)) size = "-big";
|
||||
else if (P(.005)) size = "-smallest";
|
||||
|
||||
// apply standard tinctures
|
||||
if (P(.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {t1 = "azure"; t2 = "argent";}
|
||||
|
|
@ -464,16 +483,19 @@
|
|||
}
|
||||
|
||||
const getShield = function(culture, state) {
|
||||
const emblemShape = document.getElementById("emblemShape").value;
|
||||
if (emblemShape === "state" && state && pack.states[state].coa) return pack.states[state].coa.shield;
|
||||
const emblemShape = document.getElementById("emblemShape");
|
||||
const shapeGroup = emblemShape.selectedOptions[0].parentNode.label;
|
||||
if (shapeGroup !== "Diversiform") return emblemShape.value;
|
||||
|
||||
if (emblemShape.value === "state" && state && pack.states[state].coa) return pack.states[state].coa.shield;
|
||||
if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
|
||||
console.error("Emblem shape is not defined on culture level", pack.cultures[culture]);
|
||||
console.error("Shield shape is not defined on culture level", pack.cultures[culture]);
|
||||
return "heater";
|
||||
}
|
||||
|
||||
const toString = coa => JSON.stringify(coa).replaceAll("#", "%23");
|
||||
const copy = coa => JSON.parse(JSON.stringify(coa));
|
||||
|
||||
return {generate, toString, copy, getShield};
|
||||
return {generate, toString, copy, getShield, shields};
|
||||
|
||||
})));
|
||||
|
|
@ -756,72 +756,112 @@
|
|||
}
|
||||
|
||||
const templates = {
|
||||
// divisions
|
||||
perFess: line => `<path d="${line}"/><rect x="0" y="115" width="200" height="85"/>`,
|
||||
perPale: line => `<path d="${line}" transform="rotate(-90)" transform-origin="center"/><rect x="115" y="0" width="85" height="200"/>`,
|
||||
perBend: line => `<path d="${line}" transform="rotate(45) scale(1.1)" transform-origin="center"/><rect x="0" y="115" width="200" height="85" transform="rotate(45) scale(1.1)" transform-origin="center"/>`,
|
||||
perBendSinister: line => `<path d="${line}" transform="rotate(-45) scale(1.1)" transform-origin="center"/><rect x="0" y="115" width="200" height="85" transform="rotate(-45) scale(1.1)" transform-origin="center"/>`,
|
||||
perChevron: line => `<path d="${line}" transform="translate(-70.7,70.7) rotate(-45) scale(-1,1)" transform-origin="center"/><polygon points="20,200 100,120 180,200"/><path d="${line}" transform="translate(70.7,70.7) rotate(45)" transform-origin="center"/>`,
|
||||
perChevronReversed: line => `<path d="${line}" transform="translate(-70.7,-70.7) rotate(225) scale(1,1)" transform-origin="center"/><polygon points="21,0 100,79 179,0"/><path d="${line}" transform="translate(70.7,-70.7) rotate(-225) scale(-1,1)" transform-origin="center"/>`,
|
||||
perCross: line => `<rect x="100" y="0" width="100" height="92.5"/><rect x="0" y="107.5" width="100" height="92.5"/><path d="${line}" transform="translate(0,50) scale(.5001,.5001)"/><path d="${line}" transform="translate(50,0) scale(-.5001,-.5001)" transform-origin="center"/>`,
|
||||
perPile: line => `<path d="${line}" transform="translate(-35,15) rotate(66.8) scale(-1,1)" transform-origin="center"/><path d="${line}" transform="translate(35,15) rotate(-66.8)" transform-origin="center"/><polygon points="0,0 86,200 114,200 200,0 200,200 0,200"/>`,
|
||||
perSaltire: () => `<polygon points="0,0 0,200 200,0 200,200"/>`,
|
||||
gyronny: () => `<polygon points="0,0 200,200 200,100 0,100"/><polygon points="200,0 0,200 100,200 100,0"/>`,
|
||||
chevronny: () => `<path d="M0,80 100,-15 200,80 200,120 100,25 0,120z M0,160 100,65 200,160 200,200 100,105 0,200z M0,240 100,145 200,240 0,240z"/>`,
|
||||
// oprinaries
|
||||
fess: line => `<path d="${line}" transform="translate(0,-25)"/><path d="${line}" transform="translate(0,25) rotate(180.00001)" transform-origin="center"/><rect x="0" y="88" width="200" height="24" stroke="none"/>`,
|
||||
pale: line => `<path d="${line}" transform="rotate(-90) translate(0,-25)" transform-origin="center"/><path d="${line}" transform="rotate(90) translate(0,-25)" transform-origin="center"/><rect x="88" y="0" width="24" height="200" stroke="none"/>`,
|
||||
bend: line => `<path d="${line}" transform="rotate(45) translate(0,-25) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(225) translate(0,-25) scale(1.1,1)" transform-origin="center"/><rect x="0" y="88" width="200" height="24" transform="rotate(45) scale(1.1,1)" transform-origin="center" stroke="none"/>`,
|
||||
bendSinister: line => `<path d="${line}" transform="rotate(-45) translate(0,-25) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(-225) translate(0,-25) scale(1.1,1)" transform-origin="center"/><rect x="0" y="88" width="200" height="24" transform="rotate(-45) scale(1.1,1)" transform-origin="center" stroke="none"/>`,
|
||||
chief: line => `<path d="${line}" transform="translate(0,-25) rotate(180.00001)" transform-origin="center"/><rect x="0" y="0" width="200" height="62" stroke="none"/>`,
|
||||
bar: line => `<path d="${line}" transform="translate(0,-12.5)" transform-origin="center"/><path d="${line}" transform="translate(0,12.5) rotate(180.00001)" transform-origin="center"/><rect x="0" y="94" width="200" height="12" stroke="none"/>`,
|
||||
gemelle: line => `<path d="${line}" transform="translate(0,-22.5)"/><path d="${line}" transform="translate(0,22.5) rotate(180.00001)" transform-origin="center"/>`,
|
||||
fessCotissed: line => `<path d="${line}" transform="translate(0,-35) scale(1,.5)" transform-origin="center"/><path d="${line}" transform="translate(0,35) rotate(180.0001) scale(1,.5)" transform-origin="center"/><rect x="0" y="80" width="200" height="40"/>`,
|
||||
fessDoubleCotissed: line => `<rect x="0" y="85" width="200" height="30"/><rect x="0" y="72.5" width="200" height="7.5"/><rect x="0" y="120" width="200" height="7.5"/><path d="${line}" transform="translate(0,-40) scale(1,.5)" transform-origin="center"/><path d="${line}" transform="translate(0,40) rotate(180.0001) scale(1,.5)" transform-origin="center"/>`,
|
||||
bendlet: line => `<path d="${line}" transform="rotate(45) translate(0,-16) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(225) translate(0,-16) scale(1.1,1)" transform-origin="center"/><rect x="0" y="94" width="200" height="12" transform="rotate(45) scale(1.1,1)" transform-origin="center" stroke="none"/>`,
|
||||
bendletSinister: line => `<path d="${line}" transform="rotate(-45) translate(0,-16) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(-225) translate(0,-16) scale(1.1,1)" transform-origin="center"/><rect x="0" y="94" width="200" height="12" transform="rotate(-45) scale(1.1,1)" transform-origin="center" stroke="none"/>`,
|
||||
terrace: line => `<path d="${line}" transform="translate(0,50)"/><rect x="0" y="164" width="200" height="36" stroke="none"/>`,
|
||||
cross: line => `<path d="${line}" transform="translate(0,-14.5)" transform-origin="center"/><path d="${line}" transform="rotate(180) translate(0,-14.5)" transform-origin="center"/><path d="${line}" transform="rotate(-90) translate(0,-14.5)" transform-origin="center"/><path d="${line}" transform="rotate(-270) translate(0,-14.5)" transform-origin="center"/>`,
|
||||
crossParted: line => `<path d="${line}" transform="translate(0,-20)" transform-origin="center"/><path d="${line}" transform="rotate(180) translate(0,-20)" transform-origin="center"/><path d="${line}" transform="rotate(-90) translate(0,-20)" transform-origin="center"/><path d="${line}" transform="rotate(-270) translate(0,-20)" transform-origin="center"/>`,
|
||||
saltire: line => `<path d="${line}" transform="rotate(45) translate(0,-14.5) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(225) translate(0,-14.5) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(-45) translate(0,-14.5) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(-225) translate(0,-14.5) scale(1.1,1)" transform-origin="center"/>`,
|
||||
saltireParted: line => `<path d="${line}" transform="rotate(45) translate(0,-20) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(225) translate(0,-20) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(-45) translate(0,-20) scale(1.1,1)" transform-origin="center"/><path d="${line}" transform="rotate(-225) translate(0,-20) scale(1.1,1)" transform-origin="center"/>`,
|
||||
mount: () => `<path d="m0,250 a100,100,0,0,1,200,0"/>`,
|
||||
point: () => `<path d="M0,200 Q80,180 100,135 Q120,180 200,200"/>`,
|
||||
flaunches: () => `<path d="M0,0 q120,100 0,200 M200,0 q-120,100 0,200"/>`,
|
||||
gore: () => `<path d="M20,0 Q30,75 100,100 Q80,150 100,200 L0,200 L0,0 Z"/>`,
|
||||
pall: () => `<polygon points="0,0 30,0 100,70 170,0 200,0 200,30 122,109 122,200 78,200 78,109 0,30"/>`,
|
||||
pallReversed: () => `<polygon points="0,200 0,170 78,91 78,0 122,0 122,91 200,170 200,200 170,200 100,130 30,200"/>`,
|
||||
chevron: () => `<polygon points="0,125 100,60 200,125 200,165 100,100 0,165"/>`,
|
||||
chevronReversed: () => `<polygon points="0,75 100,140 200,75 200,35 100,100 0,35"/>`,
|
||||
gyron: () => `<polygon points="0,0 100,100 0,100"/>`,
|
||||
quarter: () => `<rect x="0" y="0" width="50%" height="50%"/>`,
|
||||
canton: () => `<rect x="0" y="0" width="37.5%" height="37.5%"/>`,
|
||||
pile: () => `<polygon points="70,0 100,175 130,0"/>`,
|
||||
pileInBend: () => `<polygon points="200,200 200,144 25,25 145,200"/>`,
|
||||
pileInBendSinister: () => `<polygon points="0,200 0,144 175,25 55,200"/>`,
|
||||
piles: () => `<polygon points="46,0 75,175 103,0"/><polygon points="95,0 125,175 154,0"/>`,
|
||||
pilesInPoint: () => `<path d="M15,0 100,200 60,0Z M80,0 100,200 120,0Z M140,0 100,200 185,0Z"/>`,
|
||||
label: () => `<path d="m 46,54.8 6.6,-15.6 95.1,0 5.9,15.5 -16.8,0.1 4.5,-11.8 L 104,43 l 4.3,11.9 -16.8,0 4.3,-11.8 -37.2,0 4.5,11.8 -16.9,0 z"/>`
|
||||
// straight divisions
|
||||
perFess: `<rect x="0" y="100" width="200" height="100"/>`,
|
||||
perPale: `<rect x="100" y="0" width="100" height="200"/>`,
|
||||
perBend: `<polygon points="0,0 200,200 0,200"/>`,
|
||||
perBendSinister: `<polygon points="200,0 0,200 200,200"/>`,
|
||||
perChevron: `<polygon points="0,200 100,100 200,200"/>`,
|
||||
perChevronReversed: `<polygon points="0,0 100,100 200,0"/>`,
|
||||
perCross: `<rect x="100" y="0" width="100" height="100"/><rect x="0" y="100" width="100" height="100"/>`,
|
||||
perPile: `<polygon points="0,0 15,0 100,200 185,0 200,0 200,200 0,200"/>`,
|
||||
perSaltire: `<polygon points="0,0 0,200 200,0 200,200"/>`,
|
||||
gyronny: `<polygon points="0,0 200,200 200,100 0,100"/><polygon points="200,0 0,200 100,200 100,0"/>`,
|
||||
chevronny: `<path d="M0,80 100,-15 200,80 200,120 100,25 0,120z M0,160 100,65 200,160 200,200 100,105 0,200z M0,240 100,145 200,240 0,240z"/>`,
|
||||
// lined divisions
|
||||
perFessLined: line => `<path d="${line}"/><rect x="0" y="115" width="200" height="85" shape-rendering="crispedges"/>`,
|
||||
perPaleLined: line => `<path d="${line}" transform="rotate(-90 100 100)"/><rect x="115" y="0" width="85" height="200" shape-rendering="crispedges"/>`,
|
||||
perBendLined: line => `<path d="${line}" transform="translate(-10 -10) rotate(45 110 110) scale(1.1)"/><rect x="0" y="115" width="200" height="85" transform="translate(-10 -10) rotate(45 110 110) scale(1.1)" shape-rendering="crispedges"/>`,
|
||||
perBendSinisterLined: line => `<path d="${line}" transform="translate(-10 -10) rotate(-45 110 110) scale(1.1)"/><rect x="0" y="115" width="200" height="85" transform="translate(-10 -10) rotate(-45 110 110) scale(1.1)" shape-rendering="crispedges"/>`,
|
||||
perChevronLined: line => `<rect x="15" y="115" width="200" height="200" transform="translate(70 70) rotate(45 100 100)"/><path d="${line}" transform="translate(129 71) rotate(-45 -100 100) scale(-1 1)"/><path d="${line}" transform="translate(71 71) rotate(45 100 100)"/>`,
|
||||
perChevronReversedLined: line => `<rect x="15" y="115" width="200" height="200" transform="translate(-70 -70) rotate(225.001 100 100)"/><path d="${line}" transform="translate(-70.7 -70.7) rotate(225 100 100) scale(1 1)"/><path d="${line}" transform="translate(270.7 -70.7) rotate(-225 -100 100) scale(-1 1)"/>`,
|
||||
perCrossLined: line => `<rect x="100" y="0" width="100" height="92.5"/><rect x="0" y="107.5" width="100" height="92.5"/><path d="${line}" transform="translate(0 50) scale(.5001)"/><path d="${line}" transform="translate(200 150) scale(-.5)"/>`,
|
||||
perPileLined: line => `<path d="${line}" transform="translate(161.66 10) rotate(66.66 -100 100) scale(-1 1)"/><path d="${line}" transform="translate(38.33 10) rotate(-66.66 100 100)"/><polygon points="-2.15,0 84.15,200 115.85,200 202.15,0 200,200 0,200"/>`,
|
||||
// straight ordinaries
|
||||
fess: `<rect x="0" y="75" width="200" height="50"/>`,
|
||||
pale: `<rect x="75" y="0" width="50" height="200"/>`,
|
||||
bend: `<polygon points="35,0 200,165 200,200 165,200 0,35 0,0"/>`,
|
||||
bendSinister: `<polygon points="0,165 165,0 200,0 200,35 35,200 0,200"/>`,
|
||||
chief: `<rect width="200" height="75"/>`,
|
||||
bar: `<rect x="0" y="87.5" width="200" height="25"/>`,
|
||||
gemelle: `<rect x="0" y="76" width="200" height="16"/><rect x="0" y="108" width="200" height="16"/>`,
|
||||
fessCotissed: `<rect x="0" y="67" width="200" height="8"/><rect x="0" y="83" width="200" height="34"/><rect x="0" y="125" width="200" height="8"/>`,
|
||||
fessDoubleCotissed: `<rect x="0" y="60" width="200" height="7.5"/><rect x="0" y="72.5" width="200" height="7.5"/><rect x="0" y="85" width="200" height="30"/><rect x="0" y="120" width="200" height="7.5"/><rect x="0" y="132.5" width="200" height="7.5"/>`,
|
||||
bendlet: `<polygon points="22,0 200,178 200,200 178,200 0,22 0,0"/>`,
|
||||
bendletSinister: `<polygon points="0,178 178,0 200,0 200,22 22,200 0,200"/>`,
|
||||
terrace: `<rect x="0" y="145" width="200" height="55"/>`,
|
||||
cross: `<polygon points="85,0 85,85 0,85 0,115 85,115 85,200 115,200 115,115 200,115 200,85 115,85 115,0"/>`,
|
||||
crossParted: `<path d="M 80 0 L 80 80 L 0 80 L 0 95 L 80 95 L 80 105 L 0 105 L 0 120 L 80 120 L 80 200 L 95 200 L 95 120 L 105 120 L 105 200 L 120 200 L 120 120 L 200 120 L 200 105 L 120 105 L 120 95 L 200 95 L 200 80 L 120 80 L 120 0 L 105 0 L 105 80 L 95 80 L 95 0 L 80 0 z M 95 95 L 105 95 L 105 105 L 95 105 L 95 95 z"/>`,
|
||||
saltire: `<path d="M 0,21 79,100 0,179 0,200 21,200 100,121 179,200 200,200 200,179 121,100 200,21 200,0 179,0 100,79 21,0 0,0 Z"/>`,
|
||||
saltireParted: `<path d="M 7 0 L 89 82 L 82 89 L 0 7 L 0 28 L 72 100 L 0 172 L 0 193 L 82 111 L 89 118 L 7 200 L 28 200 L 100 128 L 172 200 L 193 200 L 111 118 L 118 111 L 200 193 L 200 172 L 128 100 L 200 28 L 200 7 L 118 89 L 111 82 L 193 0 L 172 0 L 100 72 L 28 0 L 7 0 z M 100 93 L 107 100 L 100 107 L 93 100 L 100 93 z"/>`,
|
||||
mount: `<path d="m0,250 a100,100,0,0,1,200,0"/>`,
|
||||
point: `<path d="M0,200 Q80,180 100,135 Q120,180 200,200"/>`,
|
||||
flaunches: `<path d="M0,0 q120,100 0,200 M200,0 q-120,100 0,200"/>`,
|
||||
gore: `<path d="M20,0 Q30,75 100,100 Q80,150 100,200 L0,200 L0,0 Z"/>`,
|
||||
pall: `<polygon points="0,0 30,0 100,70 170,0 200,0 200,30 122,109 122,200 78,200 78,109 0,30"/>`,
|
||||
pallReversed: `<polygon points="0,200 0,170 78,91 78,0 122,0 122,91 200,170 200,200 170,200 100,130 30,200"/>`,
|
||||
chevron: `<polygon points="0,125 100,60 200,125 200,165 100,100 0,165"/>`,
|
||||
chevronReversed: `<polygon points="0,75 100,140 200,75 200,35 100,100 0,35"/>`,
|
||||
gyron: `<polygon points="0,0 100,100 0,100"/>`,
|
||||
quarter: `<rect width="50%" height="50%"/>`,
|
||||
canton: `<rect width="37.5%" height="37.5%"/>`,
|
||||
pile: `<polygon points="70,0 100,175 130,0"/>`,
|
||||
pileInBend: `<polygon points="200,200 200,144 25,25 145,200"/>`,
|
||||
pileInBendSinister: `<polygon points="0,200 0,144 175,25 55,200"/>`,
|
||||
piles: `<polygon points="46,0 75,175 103,0"/><polygon points="95,0 125,175 154,0"/>`,
|
||||
pilesInPoint: `<path d="M15,0 100,200 60,0Z M80,0 100,200 120,0Z M140,0 100,200 185,0Z"/>`,
|
||||
label: `<path d="m 46,54.8 6.6,-15.6 95.1,0 5.9,15.5 -16.8,0.1 4.5,-11.8 L 104,43 l 4.3,11.9 -16.8,0 4.3,-11.8 -37.2,0 4.5,11.8 -16.9,0 z"/>`,
|
||||
// lined ordinaries
|
||||
fessLined: line => `<path d="${line}" transform="translate(0 -25)"/><path d="${line}" transform="translate(0 25) rotate(180 100 100)"/><rect x="0" y="88" width="200" height="24" stroke="none"/>`,
|
||||
paleLined: line => `<path d="${line}" transform="rotate(-90 100 100) translate(0 -25)"/><path d="${line}" transform="rotate(90 100 100) translate(0 -25)"/><rect x="88" y="0" width="24" height="200" stroke="none"/>`,
|
||||
bendLined: line => `<path d="${line}" transform="translate(8 -18) rotate(45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(-28 18) rotate(225 110 100) scale(1.1 1)"/><rect x="0" y="88" width="200" height="24" transform="translate(-10 0) rotate(45 110 100) scale(1.1 1)" stroke="none"/>`,
|
||||
bendSinisterLined: line => `<path d="${line}" transform="translate(-28 -18) rotate(-45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(8 18) rotate(-225 110 100) scale(1.1 1)"/><rect x="0" y="88" width="200" height="24" transform="translate(-10 0) rotate(-45 110 100) scale(1.1 1)" stroke="none"/>`,
|
||||
chiefLined: line => `<path d="${line}" transform="translate(0,-25) rotate(180.00001 100 100)"/><rect width="200" height="62" stroke="none"/>`,
|
||||
barLined: line => `<path d="${line}" transform="translate(0,-12.5)"/><path d="${line}" transform="translate(0,12.5) rotate(180.00001 100 100)"/><rect x="0" y="94" width="200" height="12" stroke="none"/>`,
|
||||
gemelleLined: line => `<path d="${line}" transform="translate(0,-22.5)"/><path d="${line}" transform="translate(0,22.5) rotate(180.00001 100 100)"/>`,
|
||||
fessCotissedLined: line => `<path d="${line}" transform="translate(0 15) scale(1 .5)"/><path d="${line}" transform="translate(0 85) rotate(180 100 50) scale(1 .5)"/><rect x="0" y="80" width="200" height="40"/>`,
|
||||
fessDoubleCotissedLined: line => `<rect x="0" y="85" width="200" height="30"/><rect x="0" y="72.5" width="200" height="7.5"/><rect x="0" y="120" width="200" height="7.5"/><path d="${line}" transform="translate(0 10) scale(1 .5)"/><path d="${line}" transform="translate(0 90) rotate(180 100 50) scale(1 .5)"/>`,
|
||||
bendletLined: line => `<path d="${line}" transform="translate(2 -12) rotate(45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(-22 12) rotate(225 110 100) scale(1.1 1)"/><rect x="0" y="94" width="200" height="12" transform="translate(-10 0) rotate(45 110 100) scale(1.1 1)" stroke="none"/>`,
|
||||
bendletSinisterLined: line => `<path d="${line}" transform="translate(-22 -12) rotate(-45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(2 12) rotate(-225 110 100) scale(1.1 1)"/><rect x="0" y="94" width="200" height="12" transform="translate(-10 0) rotate(-45 110 100) scale(1.1 1)" stroke="none"/>`,
|
||||
terraceLined: line => `<path d="${line}" transform="translate(0,50)"/><rect x="0" y="164" width="200" height="36" stroke="none"/>`,
|
||||
crossLined: line => `<path d="${line}" transform="translate(0,-14.5)"/><path d="${line}" transform="rotate(180 100 100) translate(0,-14.5)"/><path d="${line}" transform="rotate(-90 100 100) translate(0,-14.5)"/><path d="${line}" transform="rotate(-270 100 100) translate(0,-14.5)"/>`,
|
||||
crossPartedLined: line => `<path d="${line}" transform="translate(0,-20)"/><path d="${line}" transform="rotate(180 100 100) translate(0,-20)"/><path d="${line}" transform="rotate(-90 100 100) translate(0,-20)"/><path d="${line}" transform="rotate(-270 100 100) translate(0,-20)"/>`,
|
||||
saltireLined: line => `<path d="${line}" transform="translate(0 -10) rotate(45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(-20 10) rotate(225 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(-20 -10) rotate(-45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(0 10) rotate(-225 110 100) scale(1.1 1)"/>`,
|
||||
saltirePartedLined: line => `<path d="${line}" transform="translate(3 -13) rotate(45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(-23 13) rotate(225 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(-23 -13) rotate(-45 110 100) scale(1.1 1)"/><path d="${line}" transform="translate(3 13) rotate(-225 110 100) scale(1.1 1)"/>`
|
||||
}
|
||||
|
||||
const patterns = {
|
||||
semy: (p, c1, c2, size, chargeId) => `<pattern id="${p}" width="${size * .134}" height="${size * .1787}" viewBox="0 0 150 200" stroke="#000"><rect x="0" y="0" width="150" height="200" fill="${c1}" stroke="none"/><g fill="${c2}"><g transform="translate(-60,-50)"><use href="#${chargeId}"/></g><g transform="translate(10,50)"><use href="#${chargeId}"/></g></g></pattern>`,
|
||||
vair: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 25 50" stroke="#000" stroke-width=".2"><rect x="0" y="0" width="25" height="25" fill="${c2}" stroke="none"/><path d="m12.5,0 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c1}"/><rect x="0" y="25" width="25" height="25" fill="${c1}" stroke-width="1" stroke="none"/><path d="m25,25 l-6.25,6.25 v12.5 l-6.25,6.25 l-6.25,-6.25 v-12.5 l-6.25,-6.25 z" fill="${c2}"/></pattern>`,
|
||||
vairInPale: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 25 25"><rect x="0" y="0" width="25" height="25" fill="${c2}"/><path d="m12.5,0 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c1}" stroke="#000" stroke-width=".2"/></pattern>`,
|
||||
vairEnPointe: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 25 50"><rect x="0" y="0" width="25" height="25" fill="${c2}"/><path d="m12.5,0 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c1}"/><rect x="0" y="25" width="25" height="25" fill="${c1}" stroke-width="1" stroke="${c1}"/><path d="m12.5,25 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c2}"/></pattern>`,
|
||||
ermine: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 25 25" fill="${c2}"><rect x="0" y="0" width="25" height="25" fill="${c1}"/><path d="m19.1,14.8 c-0.7,2.9 -2.1,5 -3.5,6.5 0.6,-0.1 1.3,-0.6 2,-0.9 -0.4,0.8 -0.8,1.4 -1.2,2.1 0.2,-0.1 1,-0.8 2,-1.8 0.2,1.4 0.4,2.9 0.7,3.9 0.3,-0.9 0.5,-2.5 0.7,-3.9 0.6,0.6 1.2,1.3 2.1,1.8 l -1.2,-2.2 c 0.6,0.3 1.3,0.8 1.9,1 -1.5,-1.6 -2.8,-3.6 -3.5,-6.5z"/><path d="m16.1,14.9 c-0.1,-0.2 -1,0.4 -1.5,-0.8 1.2,1.1 2.5,-1.2 3.5,0.4 0.3,0.7 -1.1,1.8 -2,0.4z"/><path d="m21.9,14.9 c.1,-.2 1,0.4 1.5,-0.8 -1.2,1.1 -2.5,-1.2 -3.5,0.4 -0.3,0.7 1.1,1.8 2,0.4z"/><path d="m19.4,12.4 c-0.2,-0.1 0.7,-0.7 -0.6,-1.4 1.1,1.2 -2,1.7 -0.3,2.9 0.7,0.4 2.4,-0.5 0.9,-1.5z"/><path d="M5.8,4.6 C5.1,7.5 3.7,9.5 2.3,11 2.9,10.9 3.6,10.5 4.2,10.1 3.8,10.9 3.4,11.5 3,12.2 3.3,12.1 4,11.4 5.1,10.4 c 0.2,1.4 0.4,2.9 0.7,3.9 0.3,-0.9 0.5,-2.5 0.7,-3.9 0.6,0.6 1.2,1.3 2.1,1.8 L 7.3,10 c 0.6,0.3 1.3,0.8 1.9,1 C7.7,9.5 6.4,7.5 5.8,4.6Z"/><path d="M2.9,4.7 C2.8,4.6 1.9,5.1 1.3,4 2.6,5.1 3.8,2.8 4.9,4.3 5.2,5 3.8,6.1 2.9,4.7Z"/><path d="M8.6,4.7 C8.7,4.5 9.6,5.1 10.1,3.9 8.9,5.1 7.6,2.7 6.6,4.3 6.3,5 7.7,6.1 8.6,4.7Z"/><path d="M6.1,2.2 C 5.9,2.1 6.8,1.5 5.5,0.8 6.6,2.1 3.5,2.6 5.2,3.7 5.9,4.1 7.6,3.3 6.1,2.2Z"/></pattern>`,
|
||||
chequy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .25}" height="${size * .25}" viewBox="0 0 50 50" fill="${c2}"><rect x="0" y="0" width="50" height="50"/><rect x="0" y="0" width="25" height="25" fill="${c1}"/><rect x="25" y="25" width="25" height="25" fill="${c1}"/></pattern>`,
|
||||
lozengy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 50 50"><rect x="0" y="0" width="50" height="50" fill="${c1}"/><polygon points="25,0 50,25 25,50 0,25" fill="${c2}"/></pattern>`,
|
||||
fusily: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 50 100"><rect x="0" y="0" width="50" height="100" fill="${c1}"/><polygon points="25,0 50,50 25,100 0,50" fill="${c2}"/></pattern>`,
|
||||
pally: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .5}" height="${size * .125}" viewBox="0 0 100 25"><rect x="0" y="0" width="100" height="25" fill="${c1}"/><rect x="25" y="0" width="25" height="25" fill="${c2}"/><rect x="75" y="0" width="25" height="25" fill="${c2}"/></pattern>`,
|
||||
barry: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .5}" viewBox="0 0 25 100"><rect x="0" y="0" width="25" height="100" fill="${c2}"/><rect x="0" y="25" width="25" height="25" fill="${c1}"/><rect x="0" y="75" width="25" height="25" fill="${c1}"/></pattern>`,
|
||||
gemelles: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .5}" viewBox="0 0 25 100"><rect x="0" y="0" width="25" height="100" fill="${c2}"/><rect x="0" y="35" width="25" height="10" fill="${c1}"/><rect x="0" y="55" width="25" height="10" fill="${c1}"/></pattern>`,
|
||||
bendy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .36}" height="${size * .36}" viewBox="0 0 50 50" patternTransform="rotate(45)"><rect x="0" y="0" width="50" height="50" fill="${c2}"/><line x1="0" y1="37.5" x2="50" y2="37.5" stroke="${c1}" stroke-width="25"/></pattern>`,
|
||||
bendySinister: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .36}" height="${size * .36}" viewBox="0 0 50 50" patternTransform="rotate(-45)"><rect x="0" y="0" width="50" height="50" fill="${c2}"/><line x1="0" y1="37.5" x2="50" y2="37.5" stroke="${c1}" stroke-width="25"/></pattern>`,
|
||||
palyBendy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 50 100" patternTransform="translate(22,44) rotate(-26.5)"><rect x="0" y="0" width="50" height="100" fill="${c1}"/><polygon points="25,0 50,50 25,100 0,50" fill="${c2}"/></pattern>`,
|
||||
pappellony: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100"><rect x="0" y="0" width="100" height="100" fill="${c1}"/><circle cx="0" cy="51" r="45" stroke="${c2}" fill="${c1}" stroke-width="10"/><circle cx="100" cy="51" r="45" stroke="${c2}" fill="${c1}" stroke-width="10"/><circle cx="50" cy="1" r="45" stroke="${c2}" fill="${c1}" stroke-width="10"/></pattern>`,
|
||||
masoned: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100" fill="none"><rect x="0" y="0" width="100" height="100" fill="${c1}"/><rect x="0" y="0" width="100" height="50" stroke="${c2}" stroke-width="4"/><line x1="50" y1="50" x2="50" y2="100" stroke="${c2}" stroke-width="5"/></pattern>`,
|
||||
fretty: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .28}" height="${size * .28}" viewBox="0 0 200 200" patternTransform="translate(-19,21) rotate(45)" stroke="#000" stroke-width="2"><rect x="0" y="0" width="200" height="200" stroke="none" fill="${c1}"/><rect x="0" y="35" width="200" height="30" stroke="none" fill="${c2}"/><rect x="0" y="135" width="200" height="30" stroke="none" fill="${c2}"/><rect x="35" y="0" width="30" height="200" stroke="none" fill="${c2}"/><rect x="135" y="0" width="30" height="200" stroke="none" fill="${c2}"/><line x1="0" y1="35" x2="35" y2="35"/><line x1="0" y1="65" x2="35" y2="65"/><line x1="35" y1="165" x2="35" y2="200"/><line x1="65" y1="165" x2="65" y2="200"/><line x1="135" y1="0" x2="135" y2="35"/><line x1="165" y1="0" x2="165" y2="35"/><line x1="135" y1="65" x2="135" y2="200"/><line x1="165" y1="65" x2="165" y2="200"/><line x1="35" y1="0" x2="35" y2="135"/><line x1="65" y1="0" x2="65" y2="135"/><line x1="65" y1="35" x2="200" y2="35"/><line x1="65" y1="65" x2="200" y2="65"/><line x1="0" y1="135" x2="135" y2="135"/><line x1="0" y1="165" x2="135" y2="165"/><line x1="165" y1="135" x2="200" y2="135"/><line x1="165" y1="165" x2="200" y2="165"/></pattern>`
|
||||
semy: (p, c1, c2, size, chargeId) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 200 200" stroke="#000"><rect width="200" height="200" fill="${c1}" stroke="none"/><g fill="${c2}"><use transform="translate(-100 -50)" href="#${chargeId}"/><use transform="translate(100 -50)" href="#${chargeId}"/><use transform="translate(0 50)" href="#${chargeId}"/></g></pattern>`,
|
||||
vair: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 25 50" stroke="#000" stroke-width=".2"><rect width="25" height="25" fill="${c1}" stroke="none"/><path d="m12.5,0 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c2}"/><rect x="0" y="25" width="25" height="25" fill="${c2}" stroke="none"/><path d="m25,25 l-6.25,6.25 v12.5 l-6.25,6.25 l-6.25,-6.25 v-12.5 l-6.25,-6.25 z" fill="${c1}"/><path d="M0 50 h25" fill="none"/></pattern>`,
|
||||
counterVair: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 25 50" stroke="#000" stroke-width=".2"><rect width="25" height="50" fill="${c2}" stroke="none"/><path d="m 12.5,0 6.25,6.25 v 12.5 L 25,25 18.75,31.25 v 12.5 L 12.5,50 6.25,43.75 V 31.25 L 0,25 6.25,18.75 V 6.25 Z" fill="${c1}"/></pattern>`,
|
||||
vairInPale: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 25 25"><rect width="25" height="25" fill="${c1}"/><path d="m12.5,0 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c2}" stroke="#000" stroke-width=".2"/></pattern>`,
|
||||
vairEnPointe: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 25 50"><rect width="25" height="25" fill="${c2}"/><path d="m12.5,0 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c1}"/><rect x="0" y="25" width="25" height="25" fill="${c1}" stroke-width="1" stroke="${c1}"/><path d="m12.5,25 l6.25,6.25 v12.5 l6.25,6.25 h-25 l6.25,-6.25 v-12.5 z" fill="${c2}"/></pattern>`,
|
||||
vairAncien: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100"><rect width="100" height="100" fill="${c1}"/><path fill="${c2}" stroke="none" d="m 0,90 c 10,0 25,-5 25,-40 0,-25 10,-40 25,-40 15,0 25,15 25,40 0,35 15,40 25,40 v 10 H 0 Z"/><path fill="none" stroke="#000" d="M 0,90 c 10,0 25,-5 25,-40 0,-35 15,-40 25,-40 10,0 25,5 25,40 0,35 15,40 25,40 M0,100 h100"/></pattern>`,
|
||||
potent: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 200 200" stroke="#000"><rect width="200" height="100" fill="${c1}" stroke="none"/><rect y="100" width="200" height="100" fill="${c2}" stroke="none"/><path d="m25 50h50v-50h50v50h50v50h-150z" fill="${c2}"/><path d="m25 100v50h50v50h50v-50h50v-50z" fill="${c1}"/><path d="m0 0h200 M0 100h200" fill="none"/></pattern>`,
|
||||
counterPotent: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 200 200" stroke="none"><rect width="200" height="200" fill="${c1}"/><path d="m25 50h50v-50h50v50h50v100h-50v50h-50v-50h-50v-50z" fill="${c2}"/><path d="m0 0h200 M0 100h200 M0 200h200"/></pattern>`,
|
||||
potentInPale: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .0625}" viewBox="0 0 200 100" stroke-width="1"><rect width="200" height="100" fill="${c1}" stroke="none"/><path d="m25 50h50v-50h50v50h50v50h-150z" fill="${c2}" stroke="#000"/><path d="m0 0h200 M0 100h200" fill="none" stroke="#000"/></pattern>`,
|
||||
potentEnPointe: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 200 200" stroke="none"><rect width="200" height="200" fill="${c1}"/><path d="m0 0h25v50h50v50h50v-50h50v-50h25v100h-25v50h-50v50h-50v-50h-50v-50h-25v-100" fill="${c2}"/></pattern>`,
|
||||
ermine: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 200 200" fill="${c2}"><rect width="200" height="200" fill="${c1}"/><g stroke="none" fill="${c2}"><g transform="translate(-100 -50)"><path d="m100 81.1c-4.25 17.6-12.7 29.8-21.2 38.9 3.65-0.607 7.9-3.04 11.5-5.47-2.42 4.86-4.86 8.51-7.3 12.7 1.82-0.607 6.07-4.86 12.7-10.9 1.21 8.51 2.42 17.6 4.25 23.6 1.82-5.47 3.04-15.2 4.25-23.6 3.65 3.65 7.3 7.9 12.7 10.9l-7.9-13.3c3.65 1.82 7.9 4.86 11.5 6.07-9.11-9.11-17-21.2-20.6-38.9z"/><path d="m82.4 81.7c-0.607-0.607-6.07 2.42-9.72-4.25 7.9 6.68 15.2-7.3 21.8 1.82 1.82 4.25-6.68 10.9-12.1 2.42z"/><path d="m117 81.7c0.607-1.21 6.07 2.42 9.11-4.86-7.3 7.3-15.2-7.3-21.2 2.42-1.82 4.25 6.68 10.9 12.1 2.42z"/><path d="m101 66.5c-1.02-0.607 3.58-4.25-3.07-8.51 5.63 7.9-10.2 10.9-1.54 17.6 3.58 2.42 12.2-2.42 4.6-9.11z"/></g><g transform="translate(100 -50)"><path d="m100 81.1c-4.25 17.6-12.7 29.8-21.2 38.9 3.65-0.607 7.9-3.04 11.5-5.47-2.42 4.86-4.86 8.51-7.3 12.7 1.82-0.607 6.07-4.86 12.7-10.9 1.21 8.51 2.42 17.6 4.25 23.6 1.82-5.47 3.04-15.2 4.25-23.6 3.65 3.65 7.3 7.9 12.7 10.9l-7.9-13.3c3.65 1.82 7.9 4.86 11.5 6.07-9.11-9.11-17-21.2-20.6-38.9z"/><path d="m82.4 81.7c-0.607-0.607-6.07 2.42-9.72-4.25 7.9 6.68 15.2-7.3 21.8 1.82 1.82 4.25-6.68 10.9-12.1 2.42z"/><path d="m117 81.7c0.607-1.21 6.07 2.42 9.11-4.86-7.3 7.3-15.2-7.3-21.2 2.42-1.82 4.25 6.68 10.9 12.1 2.42z"/><path d="m101 66.5c-1.02-0.607 3.58-4.25-3.07-8.51 5.63 7.9-10.2 10.9-1.54 17.6 3.58 2.42 12.2-2.42 4.6-9.11z"/></g><g transform="translate(0 50)"><path d="m100 81.1c-4.25 17.6-12.7 29.8-21.2 38.9 3.65-0.607 7.9-3.04 11.5-5.47-2.42 4.86-4.86 8.51-7.3 12.7 1.82-0.607 6.07-4.86 12.7-10.9 1.21 8.51 2.42 17.6 4.25 23.6 1.82-5.47 3.04-15.2 4.25-23.6 3.65 3.65 7.3 7.9 12.7 10.9l-7.9-13.3c3.65 1.82 7.9 4.86 11.5 6.07-9.11-9.11-17-21.2-20.6-38.9z"/><path d="m82.4 81.7c-0.607-0.607-6.07 2.42-9.72-4.25 7.9 6.68 15.2-7.3 21.8 1.82 1.82 4.25-6.68 10.9-12.1 2.42z"/><path d="m117 81.7c0.607-1.21 6.07 2.42 9.11-4.86-7.3 7.3-15.2-7.3-21.2 2.42-1.82 4.25 6.68 10.9 12.1 2.42z"/><path d="m101 66.5c-1.02-0.607 3.58-4.25-3.07-8.51 5.63 7.9-10.2 10.9-1.54 17.6 3.58 2.42 12.2-2.42 4.6-9.11z"/></g></g></pattern>`,
|
||||
chequy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .25}" height="${size * .25}" viewBox="0 0 50 50" fill="${c2}"><rect width="50" height="50"/><rect width="25" height="25" fill="${c1}"/><rect x="25" y="25" width="25" height="25" fill="${c1}"/></pattern>`,
|
||||
lozengy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 50 50"><rect width="50" height="50" fill="${c1}"/><polygon points="25,0 50,25 25,50 0,25" fill="${c2}"/></pattern>`,
|
||||
fusily: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 50 100"><rect width="50" height="100" fill="${c2}"/><polygon points="25,0 50,50 25,100 0,50" fill="${c1}"/></pattern>`,
|
||||
pally: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .5}" height="${size * .125}" viewBox="0 0 100 25"><rect width="100" height="25" fill="${c2}"/><rect x="25" y="0" width="25" height="25" fill="${c1}"/><rect x="75" y="0" width="25" height="25" fill="${c1}"/></pattern>`,
|
||||
barry: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .5}" viewBox="0 0 25 100"><rect width="25" height="100" fill="${c2}"/><rect x="0" y="25" width="25" height="25" fill="${c1}"/><rect x="0" y="75" width="25" height="25" fill="${c1}"/></pattern>`,
|
||||
gemelles: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 50 50"><rect width="50" height="50" fill="${c1}"/><rect y="5" width="50" height="10" fill="${c2}"/><rect y="40" width="50" height="10" fill="${c2}"/></pattern>`,
|
||||
bendy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .5}" height="${size * .5}" viewBox="0 0 100 100"><rect width="100" height="100" fill="${c1}"/><polygon points="0,25 75,100 25,100 0,75" fill="${c2}"/><polygon points="25,0 75,0 100,25 100,75" fill="${c2}"/></pattern>`,
|
||||
bendySinister: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .5}" height="${size * .5}" viewBox="0 0 100 100"><rect width="100" height="100" fill="${c2}"/><polygon points="0,25 25,0 75,0 0,75" fill="${c1}"/><polygon points="25,100 100,25 100,75 75,100" fill="${c1}"/></pattern>`,
|
||||
palyBendy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .6258}" height="${size * .3576}" viewBox="0 0 175 100"><rect y="0" x="0" width="175" height="100" fill="${c2}"/><g fill="${c1}"><path d="m0 20 35 30v50l-35-30z"/><path d="m35 0 35 30v50l-35-30z"/><path d="m70 0h23l12 10v50l-35-30z"/><path d="m70 80 23 20h-23z"/><path d="m105 60 35 30v10h-35z"/><path d="m105 0h35v40l-35-30z"/><path d="m 140,40 35,30 v 30 h -23 l -12,-10z"/><path d="M 175,0 V 20 L 152,0 Z"/></g></pattern>`,
|
||||
barryBendy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .3572}" height="${size * .6251}" viewBox="0 0 100 175"><rect width="100" height="175" fill="${c2}"/><g fill="${c1}"><path d="m20 0 30 35h50l-30-35z"/><path d="m0 35 30 35h50l-30-35z"/><path d="m0 70v23l10 12h50l-30-35z"/><path d="m80 70 20 23v-23z"/><path d="m60 105 30 35h10v-35z"/><path d="m0 105v35h40l-30-35z"/><path d="m 40,140 30,35 h 30 v -23 l -10,-12 z"/><path d="m0 175h20l-20-23z"/></g></pattern>`,
|
||||
pappellony: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100"><rect width="100" height="100" fill="${c1}"/><circle cx="0" cy="51" r="45" stroke="${c2}" fill="${c1}" stroke-width="10"/><circle cx="100" cy="51" r="45" stroke="${c2}" fill="${c1}" stroke-width="10"/><circle cx="50" cy="1" r="45" stroke="${c2}" fill="${c1}" stroke-width="10"/></pattern>`,
|
||||
pappellony2: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100" stroke="#000" stroke-width="2"><rect width="100" height="100" fill="${c1}" stroke="none"/><circle cy="50" r="49" fill="${c2}"/><circle cx="100" cy="50" r="49" fill="${c2}"/><circle cx="50" cy="0" r="49" fill="${c1}"/></pattern>`,
|
||||
scaly: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100" stroke="#000"><rect width="100" height="100" fill="${c1}" stroke="none"/><path d="M 0,84 C -40,84 -50,49 -50,49 -50,79 -27,99 0,99 27,99 50,79 50,49 50,49 40,84 0,84 Z" fill="${c2}"/><path d="M 100,84 C 60,84 50,49 50,49 c 0,30 23,50 50,50 27,0 50,-20 50,-50 0,0 -10,35 -50,35 z" fill="${c2}"/><path d="M 50,35 C 10,35 0,0 0,0 0,30 23,50 50,50 77,50 100,30 100,0 100,0 90,35 50,35 Z" fill="${c2}"/></pattern>`,
|
||||
plumetty: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .25}" viewBox="0 0 50 100" stroke-width=".8"><rect width="50" height="100" fill="${c2}" stroke="none"/><path fill="${c1}" stroke="none" d="M 25,100 C 44,88 49.5,74 50,50 33.5,40 25,25 25,4e-7 25,25 16.5,40 0,50 0.5,74 6,88 25,100 Z"/><path fill="none" stroke="${c2}" d="m17 40c5.363 2.692 10.7 2.641 16 0m-19 7c7.448 4.105 14.78 3.894 22 0m-27 7c6-2 10.75 3.003 16 3 5.412-0.0031 10-5 16-3m-35 9c4-7 12 3 19 2 7 1 15-9 19-2m-35 6c6-2 11 3 16 3s10-5 16-3m-30 7c8 0 8 3 14 3s7-3 14-3m-25 8c7.385 4.048 14.72 3.951 22 0m-19 8c5.455 2.766 10.78 2.566 16 0m-8 6v-78"/><g fill="none" stroke="${c1}"><path d="m42 90c2.678 1.344 5.337 2.004 8 2m-11 5c3.686 2.032 7.344 3.006 10.97 3m0.0261-1.2e-4v-30"/><path d="m0 92c2.689 0.0045 5.328-0.6687 8-2m-8 10c3.709-0.0033 7.348-1.031 11-3m-11 3v-30"/><path d="m0 7c5.412-0.0031 10-5 16-3m-16 11c7 1 15-9 19-2m-19 9c5 0 10-5 16-3m-16 10c6 0 7-3 14-3m-14.02 11c3.685-0.002185 7.357-1.014 11.02-3m-11 10c2.694-0.01117 5.358-0.7036 7.996-2m-8 6v-48"/><path d="m34 4c6-2 10.75 3.003 16 3m-19 6c4-7 12 3 19 2m-16 4c6-2 11 3 16 3m-14 4c8 0 8 3 14 3m-11 5c3.641 1.996 7.383 2.985 11 3m-8 5c2.762 1.401 5.303 2.154 8.002 2.112m-0.00154 3.888v-48"/></g></pattern>`,
|
||||
masoned: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .125}" height="${size * .125}" viewBox="0 0 100 100" fill="none"><rect width="100" height="100" fill="${c1}"/><rect width="100" height="50" stroke="${c2}" stroke-width="4"/><line x1="50" y1="50" x2="50" y2="100" stroke="${c2}" stroke-width="5"/></pattern>`,
|
||||
fretty: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .2}" height="${size * .2}" viewBox="0 0 140 140" stroke="#000" stroke-width="2"><rect width="140" height="140" fill="${c1}" stroke="none"/><path d="m-15 5 150 150 20-20-150-150z" fill="${c2}"/><path d="m10 150 140-140-20-20-140 140z" fill="${c2}" stroke="none"/><path d="m0 120 20 20 120-120-20-20z" fill="none"/></pattern>`,
|
||||
grillage: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .25}" height="${size * .25}" viewBox="0 0 200 200" stroke="#000" stroke-width="2"><rect width="200" height="200" fill="${c1}" stroke="none"/><path d="m205 65v-30h-210v30z" fill="${c2}"/><path d="m65-5h-30v210h30z" fill="${c2}"/><path d="m205 165v-30h-210v30z" fill="${c2}"/><path d="m165,65h-30v140h30z" fill="${c2}"/><path d="m 165,-5h-30v40h30z" fill="${c2}"/></pattern>`,
|
||||
chainy: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .167}" height="${size * .167}" viewBox="0 0 200 200" stroke="#000" stroke-width="2"><rect x="-6.691e-6" width="200" height="200" fill="${c1}" stroke="none"/><path d="m155-5-20-20-160 160 20 20z" fill="${c2}"/><path d="m45 205 160-160 20 20-160 160z" fill="${c2}"/><path d="m45-5 20-20 160 160-20 20-160-160" fill="${c2}"/><path d="m-5 45-20 20 160 160 20-20-160-160" fill="${c2}"/></pattern>`,
|
||||
maily: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .167}" height="${size * .167}" viewBox="0 0 200 200" stroke="#000" stroke-width="1.2"><path fill="${c1}" stroke="none" d="M0 0h200v200H0z"/><g fill="${c2}"><path d="m80-2c-5.27e-4 2.403-0.1094 6.806-0.3262 9.199 5.014-1.109 10.1-1.768 15.19-2.059 0.09325-1.712 0.1401-5.426 0.1406-7.141z"/><path d="m100 5a95 95 0 0 0-95 95 95 95 0 0 0 95 95 95 95 0 0 0 95-95 95 95 0 0 0-95-95zm0 15a80 80 0 0 1 80 80 80 80 0 0 1-80 80 80 80 0 0 1-80-80 80 80 0 0 1 80-80z"/><path d="m92.8 20.33c-5.562 0.4859-11.04 1.603-16.34 3.217-7.793 25.31-27.61 45.12-52.91 52.91-5.321 1.638-10.8 2.716-16.34 3.217-2.394 0.2168-6.796 0.3256-9.199 0.3262v15c1.714-4.79e-4 5.429-0.04737 7.141-0.1406 5.109-0.2761 10.19-0.9646 15.19-2.059 36.24-7.937 64.54-36.24 72.47-72.47z"/><path d="m202 80c-2.403-5.31e-4 -6.806-0.1094-9.199-0.3262 1.109 5.014 1.768 10.1 2.059 15.19 1.712 0.09326 5.426 0.1401 7.141 0.1406z"/><path d="m179.7 92.8c-0.4859-5.562-1.603-11.04-3.217-16.34-25.31-7.793-45.12-27.61-52.91-52.91-1.638-5.321-2.716-10.8-3.217-16.34-0.2168-2.394-0.3256-6.796-0.3262-9.199h-15c4.8e-4 1.714 0.0474 5.429 0.1406 7.141 0.2761 5.109 0.9646 10.19 2.059 15.19 7.937 36.24 36.24 64.54 72.47 72.47z"/><path d="m120 202c5.3e-4 -2.403 0.1094-6.806 0.3262-9.199-5.014 1.109-10.1 1.768-15.19 2.059-0.0933 1.712-0.1402 5.426-0.1406 7.141z"/><path d="m107.2 179.7c5.562-0.4859 11.04-1.603 16.34-3.217 7.793-25.31 27.61-45.12 52.91-52.91 5.321-1.638 10.8-2.716 16.34-3.217 2.394-0.2168 6.796-0.3256 9.199-0.3262v-15c-1.714 4.7e-4 -5.429 0.0474-7.141 0.1406-5.109 0.2761-10.19 0.9646-15.19 2.059-36.24 7.937-64.54 36.24-72.47 72.47z"/><path d="m -2,120 c 2.403,5.4e-4 6.806,0.1094 9.199,0.3262 -1.109,-5.014 -1.768,-10.1 -2.059,-15.19 -1.712,-0.0933 -5.426,-0.1402 -7.141,-0.1406 z"/><path d="m 20.33,107.2 c 0.4859,5.562 1.603,11.04 3.217,16.34 25.31,7.793 45.12,27.61 52.91,52.91 1.638,5.321 2.716,10.8 3.217,16.34 0.2168,2.394 0.3256,6.796 0.3262,9.199 L 95,202 c -4.8e-4,-1.714 -0.0472,-5.44 -0.1404,-7.152 -0.2761,-5.109 -0.9646,-10.19 -2.059,-15.19 -7.937,-36.24 -36.24,-64.54 -72.47,-72.47 z"/></g></pattern>`,
|
||||
honeycombed: (p, c1, c2, size) => `<pattern id="${p}" width="${size * .143}" height="${size * .24514}" viewBox="0 0 70 120"><rect width="70" height="120" fill="${c1}"/><path d="M 70,0 V 20 L 35,40 m 35,80 V 100 L 35,80 M 0,120 V 100 L 35,80 V 40 L 0,20 V 0" stroke="${c2}" fill="none" stroke-width="3"/></pattern>`
|
||||
}
|
||||
|
||||
const draw = async function(id, coa) {
|
||||
|
|
@ -839,11 +879,7 @@
|
|||
const divisionClip = division ? `<clipPath id="divisionClip_${id}">${getTemplate(division.division, division.line)}</clipPath>` : "";
|
||||
const loadedCharges = await getCharges(coa, id, shieldPath);
|
||||
const loadedPatterns = getPatterns(coa, id);
|
||||
const blacklight = `<radialGradient id="backlight_${id}" cx="100%" cy="100%" r="150%">
|
||||
<stop stop-color="#fff" stop-opacity=".3" offset="0"/>
|
||||
<stop stop-color="#fff" stop-opacity=".15" offset=".25"/>
|
||||
<stop stop-color="#000" stop-opacity="0" offset="1"/>
|
||||
</radialGradient>`;
|
||||
const blacklight = `<radialGradient id="backlight_${id}" cx="100%" cy="100%" r="150%"><stop stop-color="#fff" stop-opacity=".3" offset="0"/><stop stop-color="#fff" stop-opacity=".15" offset=".25"/><stop stop-color="#000" stop-opacity="0" offset="1"/></radialGradient>`;
|
||||
const field = `<rect x="0" y="0" width="200" height="200" fill="${clr(coa.t1)}"/>`;
|
||||
const divisionGroup = division ? templateDivision() : "";
|
||||
const overlay = `<path d="${shieldPath}" fill="url(#backlight_${id})" stroke="#333"/>`;
|
||||
|
|
@ -855,6 +891,7 @@
|
|||
|
||||
// insert coa svg to defs
|
||||
document.getElementById("coas").insertAdjacentHTML("beforeend", svg);
|
||||
return true;
|
||||
|
||||
function templateDivision() {
|
||||
let svg = "";
|
||||
|
|
@ -1000,17 +1037,18 @@
|
|||
}
|
||||
|
||||
function getSizeMod(size) {
|
||||
if (size === "small") return .5;
|
||||
if (size === "smaller") return .25;
|
||||
if (size === "smallest") return .125;
|
||||
if (size === "big") return 2;
|
||||
if (size === "small") return .8;
|
||||
if (size === "smaller") return .5;
|
||||
if (size === "smallest") return .25;
|
||||
if (size === "big") return 1.6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function getTemplate(templateId, lineId) {
|
||||
if (!lineId) return templates[templateId]();
|
||||
const line = lines[lineId] || lines.straight;
|
||||
return templates[templateId](line);
|
||||
function getTemplate(id, line) {
|
||||
const linedId = id+"Lined";
|
||||
if (!line || line === "straight" || !templates[linedId]) return templates[id];
|
||||
const linePath = lines[line];
|
||||
return templates[linedId](linePath);
|
||||
}
|
||||
|
||||
// get color or link to pattern
|
||||
|
|
@ -1027,7 +1065,7 @@
|
|||
}
|
||||
|
||||
// render coa if does not exist
|
||||
const trigger = function(id, coa) {
|
||||
const trigger = async function(id, coa) {
|
||||
if (coa === "custom") {
|
||||
console.warn("Cannot render custom emblem", coa);
|
||||
return;
|
||||
|
|
@ -1036,7 +1074,7 @@
|
|||
console.warn(`Emblem ${id} is undefined`);
|
||||
return;
|
||||
}
|
||||
if (!document.getElementById(id)) draw(id, coa);
|
||||
if (!document.getElementById(id)) return draw(id, coa);
|
||||
}
|
||||
|
||||
const add = function(type, i, coa, x, y) {
|
||||
|
|
@ -1051,6 +1089,6 @@
|
|||
if (layerIsOn("toggleEmblems")) trigger(id, coa);
|
||||
}
|
||||
|
||||
return {trigger, add};
|
||||
return {trigger, add, shieldPaths};
|
||||
|
||||
})));
|
||||
|
|
|
|||
|
|
@ -55,26 +55,9 @@
|
|||
c.origin = 0;
|
||||
c.code = getCode(c.name);
|
||||
cells.culture[cell] = i+1;
|
||||
if (emblemShape === "random") c.shield = getRandomShiled();
|
||||
else if (emblemShape !== "culture" && emblemShape !== "state") c.shield = emblemShape;
|
||||
if (emblemShape === "random") c.shield = getRandomShield();
|
||||
});
|
||||
|
||||
function getRandomShiled() {
|
||||
const shields = {
|
||||
types: {basic: 10, regional: 2, historical: 1, specific: 1, banner: 1, simple: 2, fantasy: 1, middleEarth: 0},
|
||||
basic: {heater: 12, spanish: 6, french: 1},
|
||||
regional: {horsehead: 1, horsehead2: 1, polish: 1, hessen: 1, swiss: 1},
|
||||
historical: {boeotian: 1, roman: 2, kite: 1, oldFrench: 5, renaissance: 2, baroque: 2},
|
||||
specific: {targe: 1, targe2: 0, pavise: 5, wedged: 10},
|
||||
banner: {flag: 1, pennon: 0, guidon: 0, banner: 0, dovetail: 1, gonfalon: 5, pennant: 0},
|
||||
simple: {round: 12, oval: 6, vesicaPiscis: 1, square: 1, diamond: 2, no: 0},
|
||||
fantasy: {fantasy1: 2, fantasy2: 2, fantasy3: 1, fantasy4: 1, fantasy5: 3},
|
||||
middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1}
|
||||
}
|
||||
const type = rw(shields.types);
|
||||
return rw(shields[type]);
|
||||
}
|
||||
|
||||
function placeCenter(v) {
|
||||
let c, spacing = (graphWidth + graphHeight) / 2 / count;
|
||||
const sorted = [...populated].sort((a, b) => v(b) - v(a)), max = Math.floor(sorted.length / 2);
|
||||
|
|
@ -161,7 +144,13 @@
|
|||
const code = getCode(name);
|
||||
const i = pack.cultures.length;
|
||||
const color = d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex();
|
||||
pack.cultures.push({name, color, base, center, i, expansionism:1, type:"Generic", cells:0, area:0, rural:0, urban:0, origin:0, code});
|
||||
|
||||
// define emblem shape
|
||||
let shield = culture.shield;
|
||||
const emblemShape = document.getElementById("emblemShape").value;
|
||||
if (emblemShape === "random") shield = getRandomShield();
|
||||
|
||||
pack.cultures.push({name, color, base, center, i, expansionism:1, type:"Generic", cells:0, area:0, rural:0, urban:0, origin:0, code, shield});
|
||||
}
|
||||
|
||||
const getDefault = function(count) {
|
||||
|
|
@ -428,6 +417,11 @@
|
|||
return 0;
|
||||
}
|
||||
|
||||
return {generate, add, expand, getDefault};
|
||||
const getRandomShield = function() {
|
||||
const type = rw(COA.shields.types);
|
||||
return rw(COA.shields[type]);
|
||||
}
|
||||
|
||||
return {generate, add, expand, getDefault, getRandomShield};
|
||||
|
||||
})));
|
||||
|
|
|
|||
|
|
@ -1,296 +1,376 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.Rivers = factory());
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.Rivers = factory());
|
||||
}(this, (function () {'use strict';
|
||||
|
||||
const generate = function(changeHeights = true) {
|
||||
TIME && console.time('generateRivers');
|
||||
Math.random = aleaPRNG(seed);
|
||||
const cells = pack.cells, p = cells.p, features = pack.features;
|
||||
const generate = function(changeHeights = true) {
|
||||
TIME && console.time('generateRivers');
|
||||
Math.random = aleaPRNG(seed);
|
||||
const cells = pack.cells, p = cells.p, features = pack.features;
|
||||
|
||||
// build distance field in cells from water (cells.t)
|
||||
void function markupLand() {
|
||||
const q = t => cells.i.filter(i => cells.t[i] === t);
|
||||
for (let t = 2, queue = q(t); queue.length; t++, queue = q(t)) {
|
||||
queue.forEach(i => cells.c[i].forEach(c => {
|
||||
if (!cells.t[c]) cells.t[c] = t+1;
|
||||
}));
|
||||
// build distance field in cells from water (cells.t)
|
||||
void function markupLand() {
|
||||
const q = t => cells.i.filter(i => cells.t[i] === t);
|
||||
for (let t = 2, queue = q(t); queue.length; t++, queue = q(t)) {
|
||||
queue.forEach(i => cells.c[i].forEach(c => {
|
||||
if (!cells.t[c]) cells.t[c] = t+1;
|
||||
}));
|
||||
}
|
||||
}()
|
||||
|
||||
// height with added t value to make map less depressed
|
||||
const h = Array.from(cells.h)
|
||||
.map((h, i) => h < 20 || cells.t[i] < 1 ? h : h + cells.t[i] / 100)
|
||||
.map((h, i) => h < 20 || cells.t[i] < 1 ? h : h + d3.mean(cells.c[i].map(c => cells.t[c])) / 10000);
|
||||
|
||||
resolveDepressions(h);
|
||||
features.forEach(f => {delete f.river; delete f.flux; delete f.inlets});
|
||||
|
||||
const riversData = []; // rivers data
|
||||
cells.fl = new Uint16Array(cells.i.length); // water flux array
|
||||
cells.r = new Uint16Array(cells.i.length); // rivers array
|
||||
cells.conf = new Uint8Array(cells.i.length); // confluences array
|
||||
let riverNext = 1; // first river id is 1, not 0
|
||||
|
||||
void function drainWater() {
|
||||
const land = cells.i.filter(i => h[i] >= 20).sort((a,b) => h[b] - h[a]);
|
||||
const outlets = new Uint32Array(features.length);
|
||||
// enumerate lake outlet positions
|
||||
features.filter(f => f.type === "lake" && (f.group === "freshwater" || f.group === "frozen")).forEach(l => {
|
||||
let outlet = 0;
|
||||
if (l.shoreline) {
|
||||
outlet = l.shoreline[d3.scan(l.shoreline, (a,b) => h[a] - h[b])];
|
||||
} else { // in case it got missed or deleted
|
||||
WARN && console.warn('Re-scanning shoreline of a lake');
|
||||
const shallows = cells.i.filter(j => cells.t[j] === -1 && cells.f[j] === l.i);
|
||||
let shoreline = [];
|
||||
shallows.map(w => cells.c[w]).forEach(cList => cList.forEach(s => shoreline.push(s)));
|
||||
outlet = shoreline[d3.scan(shoreline, (a,b) => h[a] - h[b])];
|
||||
}
|
||||
}()
|
||||
outlets[l.i] = outlet;
|
||||
delete l.shoreline // cleanup temp data once used
|
||||
});
|
||||
|
||||
// height with added t value to make map less depressed
|
||||
const h = Array.from(cells.h)
|
||||
.map((h, i) => h < 20 || cells.t[i] < 1 ? h : h + cells.t[i] / 100)
|
||||
.map((h, i) => h < 20 || cells.t[i] < 1 ? h : h + d3.mean(cells.c[i].map(c => cells.t[c])) / 10000);
|
||||
|
||||
resolveDepressions(h);
|
||||
features.forEach(f => {delete f.river; delete f.flux;});
|
||||
|
||||
const riversData = []; // rivers data
|
||||
cells.fl = new Uint16Array(cells.i.length); // water flux array
|
||||
cells.r = new Uint16Array(cells.i.length); // rivers array
|
||||
cells.conf = new Uint8Array(cells.i.length); // confluences array
|
||||
let riverNext = 1; // first river id is 1, not 0
|
||||
|
||||
void function drainWater() {
|
||||
const land = cells.i.filter(i => h[i] >= 20).sort((a,b) => h[b] - h[a]);
|
||||
land.forEach(function(i) {
|
||||
cells.fl[i] += grid.cells.prec[cells.g[i]]; // flux from precipitation
|
||||
const x = p[i][0], y = p[i][1];
|
||||
|
||||
// near-border cell: pour out of the screen
|
||||
if (cells.b[i]) {
|
||||
if (cells.r[i]) {
|
||||
const to = [];
|
||||
const min = Math.min(y, graphHeight - y, x, graphWidth - x);
|
||||
if (min === y) {to[0] = x; to[1] = 0;} else
|
||||
if (min === graphHeight - y) {to[0] = x; to[1] = graphHeight;} else
|
||||
if (min === x) {to[0] = 0; to[1] = y;} else
|
||||
if (min === graphWidth - x) {to[0] = graphWidth; to[1] = y;}
|
||||
riversData.push({river: cells.r[i], cell: i, x: to[0], y: to[1]});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//const min = cells.c[i][d3.scan(cells.c[i], (a, b) => h[a] - h[b])]; // downhill cell
|
||||
let min = cells.c[i][d3.scan(cells.c[i], (a, b) => h[a] - h[b])]; // downhill cell
|
||||
|
||||
// allow only one river can flow through a lake
|
||||
const cf = features[cells.f[i]]; // current cell feature
|
||||
if (cf.river && cf.river !== cells.r[i]) {
|
||||
cells.fl[i] = 0;
|
||||
}
|
||||
|
||||
if (cells.fl[i] < 30) {
|
||||
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
||||
return; // flux is too small to operate as river
|
||||
}
|
||||
|
||||
// Proclaim a new river
|
||||
if (!cells.r[i]) {
|
||||
cells.r[i] = riverNext;
|
||||
riversData.push({river: riverNext, cell: i, x, y});
|
||||
riverNext++;
|
||||
}
|
||||
|
||||
if (cells.r[min]) { // downhill cell already has river assigned
|
||||
if (cells.fl[min] < cells.fl[i]) {
|
||||
cells.conf[min] = cells.fl[min]; // mark confluence
|
||||
if (h[min] >= 20) riversData.find(r => r.river === cells.r[min]).parent = cells.r[i]; // min river is a tributary of current river
|
||||
cells.r[min] = cells.r[i]; // re-assign river if downhill part has less flux
|
||||
} else {
|
||||
cells.conf[min] += cells.fl[i]; // mark confluence
|
||||
if (h[min] >= 20) riversData.find(r => r.river === cells.r[i]).parent = cells.r[min]; // current river is a tributary of min river
|
||||
}
|
||||
} else cells.r[min] = cells.r[i]; // assign the river to the downhill cell
|
||||
|
||||
const nx = p[min][0], ny = p[min][1];
|
||||
if (h[min] < 20) {
|
||||
// pour water to the sea haven
|
||||
riversData.push({river: cells.r[i], cell: cells.haven[i], x: nx, y: ny});
|
||||
const flowDown = function(min, mFlux, iFlux, ri, i = 0){
|
||||
if (cells.r[min]) { // downhill cell already has river assigned
|
||||
if (mFlux < iFlux) {
|
||||
cells.conf[min] = cells.fl[min]; // mark confluence
|
||||
if (h[min] >= 20) riversData.find(r => r.river === cells.r[min]).parent = ri; // min river is a tributary of current river
|
||||
cells.r[min] = ri; // re-assign river if downhill part has less flux
|
||||
} else {
|
||||
const mf = features[cells.f[min]]; // feature of min cell
|
||||
if (mf.type === "lake") {
|
||||
if (!mf.river || cells.fl[i] > mf.flux) {
|
||||
mf.river = cells.r[i]; // pour water to temporaly elevated lake
|
||||
mf.flux = cells.fl[i]; // entering flux
|
||||
cells.conf[min] += iFlux; // mark confluence
|
||||
if (h[min] >= 20) riversData.find(r => r.river === ri).parent = cells.r[min]; // current river is a tributary of min river
|
||||
}
|
||||
} else cells.r[min] = ri; // assign the river to the downhill cell
|
||||
|
||||
if (h[min] < 20) {
|
||||
// pour water to the sea haven
|
||||
const oh = i ? cells.haven[i] : min;
|
||||
riversData.push({river: ri, cell: oh, x: p[min][0], y: p[min][1]});
|
||||
const mf = features[cells.f[min]]; // feature of min cell
|
||||
if (mf.type === "lake") {
|
||||
if (!mf.river || iFlux > mf.flux) {
|
||||
mf.river = ri; // pour water to temporaly elevated lake
|
||||
mf.flux = iFlux; // entering flux
|
||||
}
|
||||
mf.totalFlux += iFlux;
|
||||
if (mf.inlets) {
|
||||
mf.inlets.push(ri);
|
||||
} else {
|
||||
mf.inlets = [ri];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cells.fl[min] += iFlux; // propagate flux
|
||||
riversData.push({river: ri, cell: min, x: p[min][0], y: p[min][1]}); // add next River segment
|
||||
}
|
||||
}
|
||||
|
||||
land.forEach(function(i) {
|
||||
cells.fl[i] += grid.cells.prec[cells.g[i]]; // flux from precipitation
|
||||
const x = p[i][0], y = p[i][1];
|
||||
|
||||
// lake outlets draw from lake
|
||||
let n = -1, out2 = 0;
|
||||
while (outlets.includes(i, n+1)) {
|
||||
n = outlets.indexOf(i, n+1);
|
||||
const l = features[n];
|
||||
if ( ! l ) {continue;}
|
||||
const j = cells.haven[i];
|
||||
// allow chain lakes to retain identity
|
||||
if(cells.r[j] !== l.river) {
|
||||
let touch = false;
|
||||
for (const c of cells.c[j]){
|
||||
if (cells.r[c] === l.river) {
|
||||
touch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cells.fl[min] += cells.fl[i]; // propagate flux
|
||||
riversData.push({river: cells.r[i], cell: min, x: nx, y: ny}); // add next River segment
|
||||
}
|
||||
|
||||
});
|
||||
}()
|
||||
|
||||
void function defineRivers() {
|
||||
pack.rivers = []; // rivers data
|
||||
const riverPaths = []; // temporary data for all rivers
|
||||
|
||||
for (let r = 1; r <= riverNext; r++) {
|
||||
const riverSegments = riversData.filter(d => d.river === r);
|
||||
|
||||
if (riverSegments.length > 2) {
|
||||
const riverEnhanced = addMeandring(riverSegments);
|
||||
const width = rn(.8 + Math.random() * .4, 1); // river width modifier
|
||||
const increment = rn(.8 + Math.random() * .6, 1); // river bed widening modifier
|
||||
const [path, length] = getPath(riverEnhanced, width, increment);
|
||||
riverPaths.push([r, path, width, increment]);
|
||||
const source = riverSegments[0], mouth = riverSegments[riverSegments.length-2];
|
||||
const parent = source.parent || 0;
|
||||
pack.rivers.push({i:r, parent, length, source:source.cell, mouth:mouth.cell});
|
||||
} else {
|
||||
// remove too short rivers
|
||||
riverSegments.filter(s => cells.r[s.cell] === r).forEach(s => cells.r[s.cell] = 0);
|
||||
if (touch) {
|
||||
cells.r[j] = l.river;
|
||||
riversData.push({river: l.river, cell: j, x: p[j][0], y: p[j][1]});
|
||||
} else {
|
||||
cells.r[j] = riverNext;
|
||||
riversData.push({river: riverNext, cell: j, x: p[j][0], y: p[j][1]});
|
||||
riverNext++;
|
||||
}
|
||||
}
|
||||
cells.fl[j] = l.totalFlux; // signpost river size
|
||||
flowDown(i, cells.fl[i], l.totalFlux, cells.r[j]);
|
||||
// prevent dropping imediately back into the lake
|
||||
out2 = cells.c[i].filter(c => (h[c] >= 20 || cells.f[c] !== cells.f[j])).sort((a,b) => h[a] - h[b])[0]; // downhill cell not in the source lake
|
||||
// assign all to outlet basin
|
||||
if (l.inlets) l.inlets.forEach(fork => riversData.find(r => r.river === fork).parent = cells.r[j]);
|
||||
}
|
||||
|
||||
const html = riverPaths.map(r =>`<path id="river${r[0]}" d="${r[1]}" data-width="${r[2]}" data-increment="${r[3]}"/>`).join("");
|
||||
rivers.html(html);
|
||||
}()
|
||||
|
||||
// apply change heights as basic one
|
||||
if (changeHeights) cells.h = Uint8Array.from(h);
|
||||
|
||||
TIME && console.timeEnd('generateRivers');
|
||||
}
|
||||
|
||||
// depression filling algorithm (for a correct water flux modeling)
|
||||
const resolveDepressions = function(h) {
|
||||
const cells = pack.cells;
|
||||
const land = cells.i.filter(i => h[i] >= 20 && h[i] < 100 && !cells.b[i]); // exclude near-border cells
|
||||
land.sort((a,b) => h[b] - h[a]); // highest cells go first
|
||||
let depressed = false;
|
||||
|
||||
for (let l = 0, depression = Infinity; depression && l < 100; l++) {
|
||||
depression = 0;
|
||||
for (const i of land) {
|
||||
const minHeight = d3.min(cells.c[i].map(c => h[c]));
|
||||
if (minHeight === 100) continue; // already max height
|
||||
if (h[i] <= minHeight) {
|
||||
h[i] = Math.min(minHeight + 1, 100);
|
||||
depression++;
|
||||
depressed = true;
|
||||
// near-border cell: pour out of the screen
|
||||
if (cells.b[i]) {
|
||||
if (cells.r[i]) {
|
||||
const to = [];
|
||||
const min = Math.min(y, graphHeight - y, x, graphWidth - x);
|
||||
if (min === y) {to[0] = x; to[1] = 0;} else
|
||||
if (min === graphHeight - y) {to[0] = x; to[1] = graphHeight;} else
|
||||
if (min === x) {to[0] = 0; to[1] = y;} else
|
||||
if (min === graphWidth - x) {to[0] = graphWidth; to[1] = y;}
|
||||
riversData.push({river: cells.r[i], cell: i, x: to[0], y: to[1]});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const min = out2 ? out2 : cells.c[i][d3.scan(cells.c[i], (a, b) => h[a] - h[b])]; // downhill cell
|
||||
|
||||
if (cells.fl[i] < 30) {
|
||||
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
||||
return; // flux is too small to operate as river
|
||||
}
|
||||
|
||||
// Proclaim a new river
|
||||
if (!cells.r[i]) {
|
||||
cells.r[i] = riverNext;
|
||||
riversData.push({river: riverNext, cell: i, x, y});
|
||||
riverNext++;
|
||||
}
|
||||
|
||||
flowDown(min, cells.fl[min], cells.fl[i], cells.r[i], i);
|
||||
|
||||
});
|
||||
}()
|
||||
|
||||
void function defineRivers() {
|
||||
pack.rivers = []; // rivers data
|
||||
const riverPaths = []; // temporary data for all rivers
|
||||
|
||||
for (let r = 1; r <= riverNext; r++) {
|
||||
const riverSegments = riversData.filter(d => d.river === r);
|
||||
|
||||
if (riverSegments.length > 2) {
|
||||
const source = riverSegments[0], mouth = riverSegments[riverSegments.length-2];
|
||||
const riverEnhanced = addMeandring(riverSegments);
|
||||
let width = rn(.8 + Math.random() * .4, 1); // river width modifier [.2, 10]
|
||||
let increment = rn(.8 + Math.random() * .6, 1); // river bed widening modifier [.01, 3]
|
||||
const [path, length] = getPath(riverEnhanced, width, increment, cells.h[source.cell] >= 20 ? .1 : .6);
|
||||
riverPaths.push([r, path, width, increment]);
|
||||
const parent = source.parent || 0;
|
||||
pack.rivers.push({i:r, parent, length, source:source.cell, mouth:mouth.cell});
|
||||
} else {
|
||||
// remove too short rivers
|
||||
riverSegments.filter(s => cells.r[s.cell] === r).forEach(s => cells.r[s.cell] = 0);
|
||||
}
|
||||
}
|
||||
|
||||
return depressed;
|
||||
}
|
||||
// drawRivers
|
||||
rivers.selectAll("path").remove();
|
||||
rivers.selectAll("path").data(riverPaths).enter()
|
||||
.append("path").attr("d", d => d[1]).attr("id", d => "river"+d[0])
|
||||
.attr("data-width", d => d[2]).attr("data-increment", d => d[3]);
|
||||
}()
|
||||
|
||||
// add more river points on 1/3 and 2/3 of length
|
||||
const addMeandring = function(segments, rndFactor = 0.3) {
|
||||
const riverEnhanced = []; // to store enhanced segments
|
||||
let side = 1; // to control meandring direction
|
||||
// apply change heights as basic one
|
||||
if (changeHeights) cells.h = Uint8Array.from(h);
|
||||
|
||||
for (let s = 0; s < segments.length; s++) {
|
||||
const sX = segments[s].x, sY = segments[s].y; // segment start coordinates
|
||||
const c = pack.cells.conf[segments[s].cell] || 0; // if segment is river confluence
|
||||
riverEnhanced.push([sX, sY, c]);
|
||||
TIME && console.timeEnd('generateRivers');
|
||||
}
|
||||
|
||||
if (s+1 === segments.length) break; // do not enhance last segment
|
||||
|
||||
const eX = segments[s+1].x, eY = segments[s+1].y; // segment end coordinates
|
||||
const angle = Math.atan2(eY - sY, eX - sX);
|
||||
const sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
const serpentine = 1 / (s + 1) + 0.3;
|
||||
const meandr = serpentine + Math.random() * rndFactor;
|
||||
if (P(.5)) side *= -1; // change meandring direction in 50%
|
||||
const dist2 = (eX - sX) ** 2 + (eY - sY) ** 2;
|
||||
// if dist2 is big or river is small add extra points at 1/3 and 2/3 of segment
|
||||
if (dist2 > 64 || (dist2 > 16 && segments.length < 6)) {
|
||||
const p1x = (sX * 2 + eX) / 3 + side * -sin * meandr;
|
||||
const p1y = (sY * 2 + eY) / 3 + side * cos * meandr;
|
||||
if (P(.2)) side *= -1; // change 2nd extra point meandring direction in 20%
|
||||
const p2x = (sX + eX * 2) / 3 + side * sin * meandr;
|
||||
const p2y = (sY + eY * 2) / 3 + side * cos * meandr;
|
||||
riverEnhanced.push([p1x, p1y], [p2x, p2y]);
|
||||
// if dist is medium or river is small add 1 extra middlepoint
|
||||
} else if (dist2 > 16 || segments.length < 6) {
|
||||
const p1x = (sX + eX) / 2 + side * -sin * meandr;
|
||||
const p1y = (sY + eY) / 2 + side * cos * meandr;
|
||||
riverEnhanced.push([p1x, p1y]);
|
||||
// depression filling algorithm (for a correct water flux modeling)
|
||||
const resolveDepressions = function(h) {
|
||||
const cells = pack.cells;
|
||||
const land = cells.i.filter(i => h[i] >= 20 && h[i] < 100 && !cells.b[i]); // exclude near-border cells
|
||||
const lakes = pack.features.filter(f => f.type === "lake" && (f.group === "freshwater" || f.group === "frozen")); // to keep lakes flat
|
||||
lakes.forEach(l => {
|
||||
l.shoreline = [];
|
||||
l.height = 21;
|
||||
l.totalFlux = grid.cells.prec[cells.g[l.firstCell]];
|
||||
});
|
||||
for (let i of land.filter(i => cells.t[i] === 1)) { // select shoreline cells
|
||||
cells.c[i].map(c => pack.features[cells.f[c]]).forEach(cf => {
|
||||
if (lakes.includes(cf) && !cf.shoreline.includes(i)) {
|
||||
cf.shoreline.push(i);
|
||||
}
|
||||
})
|
||||
}
|
||||
land.sort((a,b) => h[b] - h[a]); // highest cells go first
|
||||
let depressed = false;
|
||||
|
||||
for (let l = 0, depression = Infinity; depression && l < 100; l++) {
|
||||
depression = 0;
|
||||
for (const l of lakes) {
|
||||
const minHeight = d3.min(l.shoreline.map(s => h[s]));
|
||||
if (minHeight === 100) continue; // already max height
|
||||
if (l.height <= minHeight) {
|
||||
l.height = Math.min(minHeight + 1, 100);
|
||||
depression++;
|
||||
depressed = true;
|
||||
}
|
||||
}
|
||||
for (const i of land) {
|
||||
const minHeight = d3.min(cells.c[i].map(c => cells.t[c] > 0 ? h[c] :
|
||||
pack.features[cells.f[c]].height || h[c] // NB undefined is falsy (a || b is short for a ? a : b)
|
||||
));
|
||||
if (minHeight === 100) continue; // already max height
|
||||
if (h[i] <= minHeight) {
|
||||
h[i] = Math.min(minHeight + 1, 100);
|
||||
depression++;
|
||||
depressed = true;
|
||||
}
|
||||
}
|
||||
return riverEnhanced;
|
||||
}
|
||||
|
||||
const getPath = function(points, width = 1, increment = 1) {
|
||||
let offset, extraOffset = .1; // starting river width (to make river source visible)
|
||||
const riverLength = points.reduce((s, v, i, p) => s + (i ? Math.hypot(v[0] - p[i-1][0], v[1] - p[i-1][1]) : 0), 0); // summ of segments length
|
||||
const widening = rn((1000 + (riverLength * 30)) * increment);
|
||||
const riverPointsLeft = [], riverPointsRight = []; // store points on both sides to build a valid polygon
|
||||
const last = points.length - 1;
|
||||
const factor = riverLength / points.length;
|
||||
return depressed;
|
||||
}
|
||||
|
||||
// first point
|
||||
let x = points[0][0], y = points[0][1], c;
|
||||
let angle = Math.atan2(y - points[1][1], x - points[1][0]);
|
||||
let sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
let xLeft = x + -sin * extraOffset, yLeft = y + cos * extraOffset;
|
||||
riverPointsLeft.push([xLeft, yLeft]);
|
||||
let xRight = x + sin * extraOffset, yRight = y + -cos * extraOffset;
|
||||
riverPointsRight.unshift([xRight, yRight]);
|
||||
// add more river points on 1/3 and 2/3 of length
|
||||
const addMeandring = function(segments, rndFactor = 0.3) {
|
||||
const riverEnhanced = []; // to store enhanced segments
|
||||
let side = 1; // to control meandring direction
|
||||
|
||||
// middle points
|
||||
for (let p = 1; p < last; p++) {
|
||||
x = points[p][0], y = points[p][1], c = points[p][2] || 0;
|
||||
const xPrev = points[p-1][0], yPrev = points[p - 1][1];
|
||||
const xNext = points[p+1][0], yNext = points[p + 1][1];
|
||||
angle = Math.atan2(yPrev - yNext, xPrev - xNext);
|
||||
sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
offset = (Math.atan(Math.pow(p * factor, 2) / widening) / 2 * width) + extraOffset;
|
||||
const confOffset = Math.atan(c * 5 / widening);
|
||||
extraOffset += confOffset;
|
||||
xLeft = x + -sin * offset, yLeft = y + cos * (offset + confOffset);
|
||||
riverPointsLeft.push([xLeft, yLeft]);
|
||||
xRight = x + sin * offset, yRight = y + -cos * offset;
|
||||
riverPointsRight.unshift([xRight, yRight]);
|
||||
for (let s = 0; s < segments.length; s++) {
|
||||
const sX = segments[s].x, sY = segments[s].y; // segment start coordinates
|
||||
const c = pack.cells.conf[segments[s].cell] || 0; // if segment is river confluence
|
||||
riverEnhanced.push([sX, sY, c]);
|
||||
|
||||
if (s+1 === segments.length) break; // do not enhance last segment
|
||||
|
||||
const eX = segments[s+1].x, eY = segments[s+1].y; // segment end coordinates
|
||||
const angle = Math.atan2(eY - sY, eX - sX);
|
||||
const sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
const serpentine = 1 / (s + 1) + 0.3;
|
||||
const meandr = serpentine + Math.random() * rndFactor;
|
||||
if (P(.5)) side *= -1; // change meandring direction in 50%
|
||||
const dist2 = (eX - sX) ** 2 + (eY - sY) ** 2;
|
||||
// if dist2 is big or river is small add extra points at 1/3 and 2/3 of segment
|
||||
if (dist2 > 64 || (dist2 > 16 && segments.length < 6)) {
|
||||
const p1x = (sX * 2 + eX) / 3 + side * -sin * meandr;
|
||||
const p1y = (sY * 2 + eY) / 3 + side * cos * meandr;
|
||||
if (P(.2)) side *= -1; // change 2nd extra point meandring direction in 20%
|
||||
const p2x = (sX + eX * 2) / 3 + side * sin * meandr;
|
||||
const p2y = (sY + eY * 2) / 3 + side * cos * meandr;
|
||||
riverEnhanced.push([p1x, p1y], [p2x, p2y]);
|
||||
// if dist is medium or river is small add 1 extra middlepoint
|
||||
} else if (dist2 > 16 || segments.length < 6) {
|
||||
const p1x = (sX + eX) / 2 + side * -sin * meandr;
|
||||
const p1y = (sY + eY) / 2 + side * cos * meandr;
|
||||
riverEnhanced.push([p1x, p1y]);
|
||||
}
|
||||
|
||||
// end point
|
||||
x = points[last][0], y = points[last][1], c = points[last][2];
|
||||
if (c) extraOffset += Math.atan(c * 10 / widening); // add extra width on river confluence
|
||||
angle = Math.atan2(points[last-1][1] - y, points[last-1][0] - x);
|
||||
}
|
||||
return riverEnhanced;
|
||||
}
|
||||
|
||||
const getPath = function(points, width = 1, increment = 1, starting = .1) {
|
||||
let offset, extraOffset = starting; // starting river width (to make river source visible)
|
||||
const riverLength = points.reduce((s, v, i, p) => s + (i ? Math.hypot(v[0] - p[i-1][0], v[1] - p[i-1][1]) : 0), 0); // summ of segments length
|
||||
const widening = rn((1000 + (riverLength * 30)) * increment);
|
||||
const riverPointsLeft = [], riverPointsRight = []; // store points on both sides to build a valid polygon
|
||||
const last = points.length - 1;
|
||||
const factor = riverLength / points.length;
|
||||
|
||||
// first point
|
||||
let x = points[0][0], y = points[0][1], c;
|
||||
let angle = Math.atan2(y - points[1][1], x - points[1][0]);
|
||||
let sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
let xLeft = x + -sin * extraOffset, yLeft = y + cos * extraOffset;
|
||||
riverPointsLeft.push([xLeft, yLeft]);
|
||||
let xRight = x + sin * extraOffset, yRight = y + -cos * extraOffset;
|
||||
riverPointsRight.unshift([xRight, yRight]);
|
||||
|
||||
// middle points
|
||||
for (let p = 1; p < last; p++) {
|
||||
x = points[p][0], y = points[p][1], c = points[p][2] || 0;
|
||||
const xPrev = points[p-1][0], yPrev = points[p - 1][1];
|
||||
const xNext = points[p+1][0], yNext = points[p + 1][1];
|
||||
angle = Math.atan2(yPrev - yNext, xPrev - xNext);
|
||||
sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
xLeft = x + -sin * offset, yLeft = y + cos * offset;
|
||||
offset = (Math.atan(Math.pow(p * factor, 2) / widening) / 2 * width) + extraOffset;
|
||||
const confOffset = Math.atan(c * 5 / widening);
|
||||
extraOffset += confOffset;
|
||||
xLeft = x + -sin * offset, yLeft = y + cos * (offset + confOffset);
|
||||
riverPointsLeft.push([xLeft, yLeft]);
|
||||
xRight = x + sin * offset, yRight = y + -cos * offset;
|
||||
riverPointsRight.unshift([xRight, yRight]);
|
||||
|
||||
// generate polygon path and return
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const right = lineGen(riverPointsRight);
|
||||
let left = lineGen(riverPointsLeft);
|
||||
left = left.substring(left.indexOf("C"));
|
||||
return [round(right + left, 2), rn(riverLength, 2)];
|
||||
}
|
||||
|
||||
const specify = function() {
|
||||
if (!pack.rivers.length) return;
|
||||
Math.random = aleaPRNG(seed);
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const smallType = {"Creek":9, "River":3, "Brook":3, "Stream":1}; // weighted small river types
|
||||
// end point
|
||||
x = points[last][0], y = points[last][1], c = points[last][2];
|
||||
if (c) extraOffset += Math.atan(c * 10 / widening); // add extra width on river confluence
|
||||
angle = Math.atan2(points[last-1][1] - y, points[last-1][0] - x);
|
||||
sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
xLeft = x + -sin * offset, yLeft = y + cos * offset;
|
||||
riverPointsLeft.push([xLeft, yLeft]);
|
||||
xRight = x + sin * offset, yRight = y + -cos * offset;
|
||||
riverPointsRight.unshift([xRight, yRight]);
|
||||
|
||||
for (const r of pack.rivers) {
|
||||
r.basin = getBasin(r.i, r.parent);
|
||||
r.name = getName(r.mouth);
|
||||
//debug.append("circle").attr("cx", pack.cells.p[r.mouth][0]).attr("cy", pack.cells.p[r.mouth][1]).attr("r", 2);
|
||||
const small = r.length < smallLength;
|
||||
r.type = r.parent && !(r.i%6) ? small ? "Branch" : "Fork" : small ? rw(smallType) : "River";
|
||||
}
|
||||
// generate polygon path and return
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const right = lineGen(riverPointsRight);
|
||||
let left = lineGen(riverPointsLeft);
|
||||
left = left.substring(left.indexOf("C"));
|
||||
return [round(right + left, 2), rn(riverLength, 2)];
|
||||
}
|
||||
|
||||
const specify = function() {
|
||||
if (!pack.rivers.length) return;
|
||||
Math.random = aleaPRNG(seed);
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const smallType = {"Creek":9, "River":3, "Brook":3, "Stream":1}; // weighted small river types
|
||||
|
||||
for (const r of pack.rivers) {
|
||||
r.basin = getBasin(r.i, r.parent);
|
||||
r.name = getName(r.mouth);
|
||||
//debug.append("circle").attr("cx", pack.cells.p[r.mouth][0]).attr("cy", pack.cells.p[r.mouth][1]).attr("r", 2);
|
||||
const small = r.length < smallLength;
|
||||
r.type = r.parent && !(r.i%6) ? small ? "Branch" : "Fork" : small ? rw(smallType) : "River";
|
||||
}
|
||||
}
|
||||
|
||||
const getName = function(cell) {
|
||||
return Names.getCulture(pack.cells.culture[cell]);
|
||||
const getName = function(cell) {
|
||||
return Names.getCulture(pack.cells.culture[cell]);
|
||||
}
|
||||
|
||||
// remove river and all its tributaries
|
||||
const remove = function(id) {
|
||||
const cells = pack.cells;
|
||||
const riversToRemove = pack.rivers.filter(r => r.i === id || getBasin(r.i, r.parent, id) === id).map(r => r.i);
|
||||
riversToRemove.forEach(r => rivers.select("#river"+r).remove());
|
||||
cells.r.forEach((r, i) => {
|
||||
if (!r || !riversToRemove.includes(r)) return;
|
||||
cells.r[i] = 0;
|
||||
cells.fl[i] = grid.cells.prec[cells.g[i]];
|
||||
cells.conf[i] = 0;
|
||||
});
|
||||
pack.rivers = pack.rivers.filter(r => !riversToRemove.includes(r.i));
|
||||
}
|
||||
|
||||
const getBasin = function(r, p, e) {
|
||||
while (p && r !== p && r !== e) {
|
||||
const parent = pack.rivers.find(r => r.i === p);
|
||||
if (!parent) return r;
|
||||
r = parent.i;
|
||||
p = parent.parent;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// remove river and all its tributaries
|
||||
const remove = function(id) {
|
||||
const cells = pack.cells;
|
||||
const riversToRemove = pack.rivers.filter(r => r.i === id || getBasin(r.i, r.parent, id) === id).map(r => r.i);
|
||||
riversToRemove.forEach(r => rivers.select("#river"+r).remove());
|
||||
cells.r.forEach((r, i) => {
|
||||
if (!r || !riversToRemove.includes(r)) return;
|
||||
cells.r[i] = 0;
|
||||
cells.fl[i] = grid.cells.prec[cells.g[i]];
|
||||
cells.conf[i] = 0;
|
||||
});
|
||||
pack.rivers = pack.rivers.filter(r => !riversToRemove.includes(r.i));
|
||||
}
|
||||
return {generate, resolveDepressions, addMeandring, getPath, specify, getName, getBasin, remove};
|
||||
|
||||
const getBasin = function(r, p, e) {
|
||||
while (p && r !== p && r !== e) {
|
||||
const parent = pack.rivers.find(r => r.i === p);
|
||||
if (!parent) return r;
|
||||
r = parent.i;
|
||||
p = parent.parent;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
return {generate, resolveDepressions, addMeandring, getPath, specify, getName, getBasin, remove};
|
||||
|
||||
})));
|
||||
})));
|
||||
|
|
@ -1060,27 +1060,32 @@ function parseLoadedData(data) {
|
|||
}
|
||||
|
||||
if (version < 1.5) {
|
||||
// v 1.5 added emblems
|
||||
emblems = viewbox.append("g").attr("id", "emblems").style("display", "none");
|
||||
emblems.append("g").attr("id", "burgEmblems");
|
||||
emblems.append("g").attr("id", "provinceEmblems");
|
||||
emblems.append("g").attr("id", "stateEmblems");
|
||||
regenerateEmblems();
|
||||
toggleEmblems();
|
||||
|
||||
// not need to store default styles from v 1.5
|
||||
localStorage.removeItem("styleClean");
|
||||
localStorage.removeItem("styleGloom");
|
||||
localStorage.removeItem("styleAncient");
|
||||
localStorage.removeItem("styleMonochrome");
|
||||
|
||||
// v 1.5 cultures has shield attribute
|
||||
pack.cultures.forEach(culture => {
|
||||
if (culture.removed) return;
|
||||
culture.shield = Cultures.getRandomShield();
|
||||
});
|
||||
|
||||
// v 1.5 added burg type value
|
||||
pack.burgs.forEach(burg => {
|
||||
if (!burg.i || burg.removed) return;
|
||||
burg.type = BurgsAndStates.getType(burg.cell, burg.port);
|
||||
});
|
||||
|
||||
BurgsAndStates.getType(cell, false);
|
||||
// v 1.5 added emblems
|
||||
defs.append("g").attr("id", "defs-emblems");
|
||||
emblems = viewbox.insert("g", "#population").attr("id", "emblems").style("display", "none");
|
||||
emblems.append("g").attr("id", "burgEmblems");
|
||||
emblems.append("g").attr("id", "provinceEmblems");
|
||||
emblems.append("g").attr("id", "stateEmblems");
|
||||
regenerateEmblems();
|
||||
toggleEmblems();
|
||||
}
|
||||
|
||||
}()
|
||||
|
|
@ -1155,7 +1160,7 @@ function parseLoadedData(data) {
|
|||
invokeActiveZooming();
|
||||
|
||||
WARN && console.warn(`TOTAL: ${rn((performance.now()-uploadMap.timeStart)/1000,2)}s`);
|
||||
INFO && showStatistics();
|
||||
showStatistics();
|
||||
INFO && console.groupEnd("Loaded Map " + seed);
|
||||
tip("Map is successfully loaded", true, "success", 7000);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ function editCultures() {
|
|||
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
|
||||
let lines = "", totalArea = 0, totalPopulation = 0;
|
||||
|
||||
const emblemShapeGroup = document.getElementById("emblemShape").selectedOptions[0].parentNode.label;
|
||||
const selectShape = emblemShapeGroup === "Diversiform"
|
||||
|
||||
for (const c of pack.cultures) {
|
||||
if (c.removed) continue;
|
||||
const area = c.area * (distanceScaleInput.value ** 2);
|
||||
|
|
@ -73,7 +76,7 @@ function editCultures() {
|
|||
if (!c.i) {
|
||||
// Uncultured (neutral) line
|
||||
lines += `<div class="states" data-id=${c.i} data-name="${c.name}" data-color="" data-cells=${c.cells}
|
||||
data-area=${area} data-population=${population} data-base=${c.base} data-type="" data-expansionism="">
|
||||
data-area=${area} data-population=${population} data-base=${c.base} data-type="" data-expansionism="" data-emblems="${c.shield}">
|
||||
<svg width="9" height="9" class="placeholder"></svg>
|
||||
<input data-tip="Culture name. Click and type to change" class="cultureName italic" value="${c.name}" autocorrect="off" spellcheck="false">
|
||||
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
||||
|
|
@ -86,26 +89,28 @@ function editCultures() {
|
|||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
|
||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||
<select data-tip="Culture namesbase. Click to change" class="cultureBase">${getBaseOptions(c.base)}</select>
|
||||
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(c.base)}</select>
|
||||
${selectShape ? `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureShape hide">${getShapeOptions(c.shield)}</select>` : ""}
|
||||
</div>`;
|
||||
continue;
|
||||
}
|
||||
|
||||
lines += `<div class="states cultures" data-id=${c.i} data-name="${c.name}" data-color="${c.color}" data-cells=${c.cells}
|
||||
data-area=${area} data-population=${population} data-base=${c.base} data-type=${c.type} data-expansionism=${c.expansionism}>
|
||||
data-area=${area} data-population=${population} data-base=${c.base} data-type=${c.type} data-expansionism=${c.expansionism} data-emblems="${c.shield}">
|
||||
<svg data-tip="Culture fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${c.color}" class="fillRect pointer"></svg>
|
||||
<input data-tip="Culture name. Click and type to change" class="cultureName" value="${c.name}" autocorrect="off" spellcheck="false">
|
||||
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
||||
<div data-tip="Cells count" class="stateCells hide">${c.cells}</div>
|
||||
<span data-tip="Culture expansionism. Defines competitive size" class="icon-resize-full hide"></span>
|
||||
<input data-tip="Culture expansionism. Defines competitive size. Click to change" class="statePower hide" type="number" min=0 max=99 step=.1 value=${c.expansionism}>
|
||||
<input data-tip="Culture expansionism. Defines competitive size. Click to change, then click Recalculate to apply change" class="statePower hide" type="number" min=0 max=99 step=.1 value=${c.expansionism}>
|
||||
<select data-tip="Culture type. Defines growth model. Click to change" class="cultureType">${getTypeOptions(c.type)}</select>
|
||||
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||
<div data-tip="Culture area" class="biomeArea hide">${si(area) + unit}</div>
|
||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
|
||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||
<select data-tip="Culture namesbase. Change and then click on the Re-generate button to get new names" class="cultureBase">${getBaseOptions(c.base)}</select>
|
||||
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(c.base)}</select>
|
||||
${selectShape ? `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureShape hide">${getShapeOptions(c.shield)}</select>` : ""}
|
||||
<span data-tip="Remove culture" class="icon-trash-empty hide"></span>
|
||||
</div>`;
|
||||
}
|
||||
|
|
@ -128,6 +133,7 @@ function editCultures() {
|
|||
body.querySelectorAll("div > input.statePower").forEach(el => el.addEventListener("input", cultureChangeExpansionism));
|
||||
body.querySelectorAll("div > select.cultureType").forEach(el => el.addEventListener("change", cultureChangeType));
|
||||
body.querySelectorAll("div > select.cultureBase").forEach(el => el.addEventListener("change", cultureChangeBase));
|
||||
body.querySelectorAll("div > select.cultureShape").forEach(el => el.addEventListener("change", cultureChangeShape));
|
||||
body.querySelectorAll("div > div.culturePopulation").forEach(el => el.addEventListener("click", changePopulation));
|
||||
body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.addEventListener("click", cultureRegenerateBurgs));
|
||||
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", cultureRemove));
|
||||
|
|
@ -150,6 +156,11 @@ function editCultures() {
|
|||
return options;
|
||||
}
|
||||
|
||||
function getShapeOptions(selected) {
|
||||
const shapes = Object.keys(COA.shields.types).map(type => Object.keys(COA.shields[type])).flat();
|
||||
return shapes.map(shape => `<option ${shape === selected ? "selected" : ""} value="${shape}">${capitalize(shape)}</option>`);
|
||||
}
|
||||
|
||||
function cultureHighlightOn(event) {
|
||||
const culture = +event.target.dataset.id;
|
||||
const info = document.getElementById("cultureInfo");
|
||||
|
|
@ -225,6 +236,40 @@ function editCultures() {
|
|||
this.parentNode.dataset.base = pack.cultures[culture].base = v;
|
||||
}
|
||||
|
||||
function cultureChangeShape() {
|
||||
const culture = +this.parentNode.dataset.id;
|
||||
const shape = this.value;
|
||||
this.parentNode.dataset.emblems = pack.cultures[culture].shield = shape;
|
||||
|
||||
const rerenderCOA = (id, coa) => {
|
||||
const coaEl = document.getElementById(id);
|
||||
if (!coaEl) return; // not rendered
|
||||
coaEl.remove();
|
||||
COArenderer.trigger(id, coa);
|
||||
}
|
||||
|
||||
pack.states.forEach(state => {
|
||||
if (state.culture !== culture || !state.i || state.removed || !state.coa || state.coa === "custom") return;
|
||||
if (shape === state.coa.shield) return;
|
||||
state.coa.shield = shape;
|
||||
rerenderCOA("stateCOA" + state.i, state.coa);
|
||||
});
|
||||
|
||||
pack.provinces.forEach(province => {
|
||||
if (pack.cells.culture[province.center] !== culture || !province.i || province.removed || !province.coa || province.coa === "custom") return;
|
||||
if (shape === province.coa.shield) return;
|
||||
province.coa.shield = shape;
|
||||
rerenderCOA("provinceCOA" + province.i, province.coa);
|
||||
});
|
||||
|
||||
pack.burgs.forEach(burg => {
|
||||
if (burg.culture !== culture || !burg.i || burg.removed || !burg.coa || burg.coa === "custom") return;
|
||||
if (shape === burg.coa.shield) return;
|
||||
burg.coa.shield = shape
|
||||
rerenderCOA("burgCOA" + burg.i, burg.coa);
|
||||
});
|
||||
}
|
||||
|
||||
function changePopulation() {
|
||||
const culture = +this.parentNode.dataset.id;
|
||||
const c = pack.cultures[culture];
|
||||
|
|
@ -293,7 +338,7 @@ function editCultures() {
|
|||
b.name = Names.getCulture(culture);
|
||||
labels.select("[data-id='" + b.i +"']").text(b.name);
|
||||
});
|
||||
tip(`Names for ${cBurgs.length} burgs are re-generated`);
|
||||
tip(`Names for ${cBurgs.length} burgs are regenerated`, false, "success");
|
||||
}
|
||||
|
||||
function cultureRemove() {
|
||||
|
|
@ -306,12 +351,12 @@ function editCultures() {
|
|||
Remove: function() {
|
||||
cults.select("#culture"+culture).remove();
|
||||
debug.select("#cultureCenter"+culture).remove();
|
||||
|
||||
|
||||
pack.burgs.filter(b => b.culture == culture).forEach(b => b.culture = 0);
|
||||
pack.states.forEach((s, i) => {if(s.culture === culture) s.culture = 0;});
|
||||
pack.cells.culture.forEach((c, i) => {if(c === culture) pack.cells.culture[i] = 0;});
|
||||
pack.cultures[culture].removed = true;
|
||||
|
||||
|
||||
const origin = pack.cultures[culture].origin;
|
||||
pack.cultures.forEach(c => {if(c.origin === culture) c.origin = origin;});
|
||||
refreshCulturesEditor();
|
||||
|
|
@ -493,8 +538,8 @@ function editCultures() {
|
|||
|
||||
culturesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
|
||||
culturesHeader.querySelector("div[data-sortby='type']").style.left = "8.8em";
|
||||
culturesHeader.querySelector("div[data-sortby='base']").style.left = "13.6em";
|
||||
culturesFooter.style.display = "none";
|
||||
culturesHeader.querySelector("div[data-sortby='base']").style.marginLeft = "20px";
|
||||
body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none");
|
||||
$("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
|
||||
|
||||
|
|
@ -589,8 +634,8 @@ function editCultures() {
|
|||
|
||||
culturesEditor.querySelectorAll(".hide").forEach(el => el.classList.remove("hidden"));
|
||||
culturesHeader.querySelector("div[data-sortby='type']").style.left = "18.6em";
|
||||
culturesHeader.querySelector("div[data-sortby='base']").style.left = "35.8em";
|
||||
culturesFooter.style.display = "block";
|
||||
culturesHeader.querySelector("div[data-sortby='base']").style.marginLeft = "2px";
|
||||
body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all");
|
||||
if(!close) $("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
|
||||
|
||||
|
|
@ -634,7 +679,7 @@ function editCultures() {
|
|||
|
||||
function downloadCulturesData() {
|
||||
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
|
||||
let data = "Id,Culture,Color,Cells,Expansionism,Type,Area "+unit+",Population,Namesbase\n"; // headers
|
||||
let data = "Id,Culture,Color,Cells,Expansionism,Type,Area "+unit+",Population,Namesbase,Emblems Shape\n"; // headers
|
||||
|
||||
body.querySelectorAll(":scope > div").forEach(function(el) {
|
||||
data += el.dataset.id + ",";
|
||||
|
|
@ -646,7 +691,8 @@ function editCultures() {
|
|||
data += el.dataset.area + ",";
|
||||
data += el.dataset.population + ",";
|
||||
const base = +el.dataset.base;
|
||||
data += nameBases[base].name + "\n";
|
||||
data += nameBases[base].name + ",";
|
||||
data += el.dataset.emblems + "\n";
|
||||
});
|
||||
|
||||
const name = getFileName("Cultures") + ".csv";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function editEmblem(type, id, el) {
|
|||
updateElementSelectors(type, id, el);
|
||||
|
||||
$("#emblemEditor").dialog({
|
||||
title: "Edit Emblem", resizable: true, width: "18em", height: "auto",
|
||||
title: "Edit Emblem", resizable: true, width: "18.2em", height: "auto",
|
||||
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"},
|
||||
close: closeEmblemEditor
|
||||
});
|
||||
|
|
@ -150,7 +150,8 @@ function editEmblem(type, id, el) {
|
|||
|
||||
function changeShape() {
|
||||
el.coa.shield = this.value;
|
||||
document.getElementById(id).remove();
|
||||
const coaEl = document.getElementById(id);
|
||||
if (coaEl) coaEl.remove();
|
||||
COArenderer.trigger(id, el.coa);
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +181,7 @@ function editEmblem(type, id, el) {
|
|||
function openInArmoria() {
|
||||
const coa = el.coa && el.coa !== "custom" ? el.coa : {t1: "sable"};
|
||||
const json = JSON.stringify(coa).replaceAll("#", "%23");
|
||||
const url = `http://azgaar.github.io/Armoria/?coa=${json}`;
|
||||
const url = `https://azgaar.github.io/Armoria/?coa=${json}&from=FMG`;
|
||||
openURL(url);
|
||||
}
|
||||
|
||||
|
|
@ -232,10 +233,10 @@ function editEmblem(type, id, el) {
|
|||
buttons.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
function download(format) {
|
||||
async function download(format) {
|
||||
const coa = document.getElementById(id);
|
||||
const size = +emblemsDownloadSize.value;
|
||||
const url = getURL(coa, el.coa, size);
|
||||
const url = await getURL(coa, size);
|
||||
const link = document.createElement("a");
|
||||
link.download = getFileName(`Emblem ${el.fullName || el.name}`) + "." + format;
|
||||
|
||||
|
|
@ -246,7 +247,6 @@ function editEmblem(type, id, el) {
|
|||
function downloadSVG(url, link) {
|
||||
link.href = url;
|
||||
link.click();
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||
}
|
||||
|
||||
function downloadRaster(format, url, link, size) {
|
||||
|
|
@ -263,52 +263,49 @@ function editEmblem(type, id, el) {
|
|||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
const URL = canvas.toDataURL("image/" + format, .92);
|
||||
link.href = URL;
|
||||
const dataURL = canvas.toDataURL("image/" + format, .92);
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(dataURL), 6000);
|
||||
}
|
||||
}
|
||||
|
||||
function getURL(svg, coa, size) {
|
||||
const serialized = getSVG(svg, coa, size);
|
||||
const blob = new Blob([serialized], { type: 'image/svg+xml;charset=utf-8' });
|
||||
async function getURL(svg, size) {
|
||||
const serialized = getSVG(svg, size);
|
||||
const blob = new Blob([serialized], {type: 'image/svg+xml;charset=utf-8'});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(url), 6000);
|
||||
return url;
|
||||
}
|
||||
|
||||
function getSVG(svg, size) {
|
||||
const clone = svg.cloneNode(true); // clone svg
|
||||
const clone = svg.cloneNode(true);
|
||||
clone.setAttribute("width", size);
|
||||
clone.setAttribute("height", size);
|
||||
return (new XMLSerializer()).serializeToString(clone);
|
||||
}
|
||||
|
||||
function downloadGallery() {
|
||||
async function downloadGallery() {
|
||||
const name = getFileName("Emblems Gallery");
|
||||
const validStates = pack.states.filter(s => s.i && !s.removed && s.coa);
|
||||
const validProvinces = pack.provinces.filter(p => p.i && !p.removed && p.coa);
|
||||
const validBurgs = pack.burgs.filter(b => b.i && !b.removed && b.coa);
|
||||
triggerCOALoad(validStates, validProvinces, validBurgs);
|
||||
const timeout = (validStates.length + validProvinces.length + validBurgs.length) * 8;
|
||||
tip("Preparing to download...", true, "warn", timeout);
|
||||
d3.timeout(runDownload, timeout);
|
||||
await renderAllEmblems(validStates, validProvinces, validBurgs);
|
||||
runDownload();
|
||||
|
||||
function runDownload() {
|
||||
const back = `<a href="javascript:history.back()">Go Back</a>`;
|
||||
|
||||
const stateSection = `<div><h2>States</h2>` + validStates.map(state => {
|
||||
const el = document.getElementById("stateCOA"+state.i);
|
||||
const svg = getSVG(el, state.coa, 200);
|
||||
return `<figure id="state_${state.i}"><a href="#provinces_${state.i}"><figcaption>${state.fullName}</figcaption>${svg}</a></figure>`;
|
||||
return `<figure id="state_${state.i}"><a href="#provinces_${state.i}"><figcaption>${state.fullName}</figcaption>${getSVG(el, 200)}</a></figure>`;
|
||||
}).join("") + `</div>`;
|
||||
|
||||
const provinceSections = validStates.map(state => {
|
||||
const stateProvinces = validProvinces.filter(p => p.state === state.i);
|
||||
const figures = stateProvinces.map(province => {
|
||||
const el = document.getElementById("provinceCOA"+province.i);
|
||||
const svg = getSVG(el, province.coa, 200);
|
||||
return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${province.fullName}</figcaption>${svg}</a></figure>`;
|
||||
return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${province.fullName}</figcaption>${getSVG(el, 200)}</a></figure>`;
|
||||
}).join("");
|
||||
return stateProvinces.length ? `<div id="provinces_${state.i}">${back}<h2>${state.fullName} provinces</h2>${figures}</div>` : "";
|
||||
}).join("");
|
||||
|
|
@ -319,8 +316,7 @@ function editEmblem(type, id, el) {
|
|||
const provinceBurgs = stateBurgs.filter(b => pack.cells.province[b.cell] === province.i);
|
||||
const provinceBurgFigures = provinceBurgs.map(burg => {
|
||||
const el = document.getElementById("burgCOA"+burg.i);
|
||||
const svg = getSVG(el, burg.coa, 200);
|
||||
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${svg}</figure>`;
|
||||
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
|
||||
}).join("");
|
||||
return provinceBurgs.length ? `<div id="burgs_${province.i}">${back}<h2>${province.fullName} burgs</h2>${provinceBurgFigures}</div>` : "";
|
||||
}).join("");
|
||||
|
|
@ -328,8 +324,7 @@ function editEmblem(type, id, el) {
|
|||
const stateBurgOutOfProvinces = stateBurgs.filter(b => !pack.cells.province[b.cell]);
|
||||
const stateBurgOutOfProvincesFigures = stateBurgOutOfProvinces.map(burg => {
|
||||
const el = document.getElementById("burgCOA"+burg.i);
|
||||
const svg = getSVG(el, burg.coa, 200);
|
||||
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${svg}</figure>`;
|
||||
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
|
||||
}).join("");
|
||||
if (stateBurgOutOfProvincesFigures) stateBurgSections += `<div><h2>${state.fullName} burgs under direct control</h2>${stateBurgOutOfProvincesFigures}</div>`;
|
||||
return stateBurgSections;
|
||||
|
|
@ -338,8 +333,7 @@ function editEmblem(type, id, el) {
|
|||
const neutralBurgs = validBurgs.filter(b => !b.state);
|
||||
const neutralsSection = neutralBurgs.length ? "<div><h2>Independent burgs</h2>" + neutralBurgs.map(burg => {
|
||||
const el = document.getElementById("burgCOA"+burg.i);
|
||||
const svg = getSVG(el, burg.coa, 200);
|
||||
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${svg}</figure>`;
|
||||
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
|
||||
}).join("") + "</div>" : "";
|
||||
|
||||
const FMG = `<a href="https://azgaar.github.io/Fantasy-Map-Generator" target="_blank">Azgaar's Fantasy Map Generator</a>`;
|
||||
|
|
@ -370,10 +364,15 @@ function editEmblem(type, id, el) {
|
|||
}
|
||||
}
|
||||
|
||||
function triggerCOALoad(states, provinces, burgs) {
|
||||
states.forEach(state => COArenderer.trigger("stateCOA"+state.i, state.coa));
|
||||
provinces.forEach(province => COArenderer.trigger("provinceCOA"+province.i, province.coa));
|
||||
burgs.forEach(burg => COArenderer.trigger("burgCOA"+burg.i, burg.coa));
|
||||
async function renderAllEmblems(states, provinces, burgs) {
|
||||
tip("Preparing for download...", true, "warn");
|
||||
|
||||
const statePromises = states.map(state => COArenderer.trigger("stateCOA"+state.i, state.coa));
|
||||
const provincePromises = provinces.map(province => COArenderer.trigger("provinceCOA"+province.i, province.coa));
|
||||
const burgPromises = burgs.map(burg => COArenderer.trigger("burgCOA"+burg.i, burg.coa));
|
||||
const promises = [...statePromises, ...provincePromises, ...burgPromises];
|
||||
|
||||
return Promise.allSettled(promises).then(res => clearMainTip());
|
||||
}
|
||||
|
||||
function dragEmblem() {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ document.getElementById("dialogs").addEventListener("mousemove", showDataTip);
|
|||
document.getElementById("optionsContainer").addEventListener("mousemove", showDataTip);
|
||||
document.getElementById("exitCustomization").addEventListener("mousemove", showDataTip);
|
||||
|
||||
/**
|
||||
* @param {string} tip Tooltip text
|
||||
* @param {boolean} main Show above other tooltips
|
||||
* @param {string} type Message type (color): error, warn, success
|
||||
* @param {number} time Timeout to auto hide, ms
|
||||
*/
|
||||
function tip(tip = "Tip is undefined", main, type, time) {
|
||||
tooltip.innerHTML = tip;
|
||||
tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)";
|
||||
|
|
@ -100,13 +106,13 @@ function showMapTooltip(point, e, i, g) {
|
|||
parent.id === "provinceEmblems" ? [pack.provinces, "province"] :
|
||||
[pack.states, "state"];
|
||||
const i = +e.target.dataset.i;
|
||||
highlightEmblemElement(type, g[i]);
|
||||
if (event.shiftKey) highlightEmblemElement(type, g[i]);
|
||||
|
||||
d3.select(e.target).raise();
|
||||
d3.select(parent).raise();
|
||||
|
||||
const name = g[i].fullName || g[i].name;
|
||||
tip(`${name} ${type} emblem. Click to edit`);
|
||||
tip(`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`);
|
||||
return;
|
||||
}
|
||||
if (group === "rivers") {
|
||||
|
|
@ -297,13 +303,12 @@ function getPopulationTip(i) {
|
|||
}
|
||||
|
||||
function highlightEmblemElement(type, el) {
|
||||
if (emblems.selectAll("line, circle").size()) return;
|
||||
const i = el.i, cells = pack.cells;
|
||||
const animation = d3.transition().duration(1000).ease(d3.easeSinIn);
|
||||
|
||||
if (type === "burg") {
|
||||
const {x, y} = el;
|
||||
emblems.append("circle").attr("cx", x).attr("cy", y).attr("r", 0)
|
||||
debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 0)
|
||||
.attr("fill", "none").attr("stroke", "#d0240f").attr("stroke-width", 1).attr("opacity", 1)
|
||||
.transition(animation).attr("r", 20).attr("opacity", .1).attr("stroke-width", 0).remove();
|
||||
return;
|
||||
|
|
@ -314,7 +319,7 @@ function highlightEmblemElement(type, el) {
|
|||
const borderCells = cells.i.filter(id => obj[id] === i && cells.c[id].some(n => obj[n] !== i));
|
||||
const data = Array.from(borderCells).filter((c, i) => !(i%2)).map(i => cells.p[i]).map(i => [i[0], i[1], Math.hypot(i[0]-x, i[1]-y)]);
|
||||
|
||||
emblems.selectAll("line").data(data).enter().append("line")
|
||||
debug.selectAll("line").data(data).enter().append("line")
|
||||
.attr("x1", x).attr("y1", y).attr("x2", d => d[0]).attr("y2", d => d[1])
|
||||
.attr("stroke", "#d0240f").attr("stroke-width", .5).attr("opacity", .2)
|
||||
.attr("stroke-dashoffset", d => d[2]).attr("stroke-dasharray", d => d[2])
|
||||
|
|
@ -474,6 +479,7 @@ document.addEventListener("keyup", event => {
|
|||
else if (shift && key === 78) editNamesbase(); // Shift + "N" to edit Namesbase
|
||||
else if (shift && key === 90) editZones(); // Shift + "Z" to edit Zones
|
||||
else if (shift && key === 82) editReligions(); // Shift + "R" to edit Religions
|
||||
else if (shift && key === 89) openEmblemEditor(); // Shift + "Y" to edit Emblems
|
||||
else if (shift && key === 81) editUnits(); // Shift + "Q" to edit Units
|
||||
else if (shift && key === 79) editNotes(); // Shift + "O" to edit Notes
|
||||
else if (shift && key === 84) overviewBurgs(); // Shift + "T" to open Burgs overview
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@ function editHeightmap() {
|
|||
reGraph();
|
||||
drawCoastline();
|
||||
|
||||
elevateLakes();
|
||||
Rivers.generate(change);
|
||||
|
||||
if (!change) {
|
||||
|
|
@ -288,10 +287,7 @@ function editHeightmap() {
|
|||
reGraph();
|
||||
drawCoastline();
|
||||
|
||||
if (changeHeights.checked) {
|
||||
elevateLakes();
|
||||
Rivers.generate(changeHeights.checked);
|
||||
}
|
||||
if (changeHeights.checked) Rivers.generate(changeHeights.checked);
|
||||
|
||||
// assign saved pack data from grid back to pack
|
||||
const n = pack.cells.i.length;
|
||||
|
|
@ -314,7 +310,6 @@ function editHeightmap() {
|
|||
|
||||
for (const i of pack.cells.i) {
|
||||
const g = pack.cells.g[i];
|
||||
if (pack.features[pack.cells.f[i]].group === "freshwater") pack.cells.h[i] = 19; // de-elevate lakes
|
||||
const land = pack.cells.h[i] >= 20;
|
||||
|
||||
// check biome
|
||||
|
|
|
|||
|
|
@ -289,15 +289,45 @@ function changeCultureSet() {
|
|||
if (+culturesOutput.value > +max) culturesInput.value = culturesOutput.value = max;
|
||||
}
|
||||
|
||||
function changeEmblemShape(value) {
|
||||
function changeEmblemShape(emblemShape) {
|
||||
const image = document.getElementById("emblemShapeImage");
|
||||
const shapeEl = document.getElementById(value);
|
||||
if (shapeEl) {
|
||||
const shape = shapeEl.querySelector("path").getAttribute("d");
|
||||
image.setAttribute("d", shape);
|
||||
} else {
|
||||
image.removeAttribute("d");
|
||||
const shapePath = window.COArenderer && COArenderer.shieldPaths[emblemShape];
|
||||
shapePath ? image.setAttribute("d", shapePath) : image.removeAttribute("d");
|
||||
|
||||
const specificShape = ["culture", "state", "random"].includes(emblemShape) ? null : emblemShape;
|
||||
if (emblemShape === "random") pack.cultures.filter(c => !c.removed).forEach(c => c.shield = Cultures.getRandomShield());
|
||||
|
||||
const rerenderCOA = (id, coa) => {
|
||||
const coaEl = document.getElementById(id);
|
||||
if (!coaEl) return; // not rendered
|
||||
coaEl.remove();
|
||||
COArenderer.trigger(id, coa);
|
||||
}
|
||||
|
||||
pack.states.forEach(state => {
|
||||
if (!state.i || state.removed || !state.coa || state.coa === "custom") return;
|
||||
const newShield = specificShape || COA.getShield(state.culture, null);
|
||||
if (newShield === state.coa.shield) return;
|
||||
state.coa.shield = newShield;
|
||||
rerenderCOA("stateCOA" + state.i, state.coa);
|
||||
});
|
||||
|
||||
pack.provinces.forEach(province => {
|
||||
if (!province.i || province.removed || !province.coa || province.coa === "custom") return;
|
||||
const culture = pack.cells.culture[province.center];
|
||||
const newShield = specificShape || COA.getShield(culture, province.state);
|
||||
if (newShield === province.coa.shield) return;
|
||||
province.coa.shield = newShield;
|
||||
rerenderCOA("provinceCOA" + province.i, province.coa);
|
||||
});
|
||||
|
||||
pack.burgs.forEach(burg => {
|
||||
if (!burg.i || burg.removed || !burg.coa || burg.coa === "custom") return;
|
||||
const newShield = specificShape || COA.getShield(burg.culture, burg.state);
|
||||
if (newShield === burg.coa.shield) return;
|
||||
burg.coa.shield = newShield
|
||||
rerenderCOA("burgCOA" + burg.i, burg.coa);
|
||||
});
|
||||
}
|
||||
|
||||
function changeStatesNumber(value) {
|
||||
|
|
@ -419,7 +449,6 @@ function randomizeOptions() {
|
|||
|
||||
// World settings
|
||||
generateEra();
|
||||
changeEmblemShape(emblemShape.value); // change emblem shape image
|
||||
}
|
||||
|
||||
// select heightmap template pseudo-randomly
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ toolsContent.addEventListener("click", function(event) {
|
|||
if (button === "editDiplomacyButton") editDiplomacy(); else
|
||||
if (button === "editCulturesButton") editCultures(); else
|
||||
if (button === "editReligions") editReligions(); else
|
||||
if (button === "editEmblemButton") openEmblemEditor(); else
|
||||
if (button === "editNamesBaseButton") editNamesbase(); else
|
||||
if (button === "editUnitsButton") editUnits(); else
|
||||
if (button === "editNotesButton") editNotes(); else
|
||||
|
|
@ -72,13 +73,28 @@ function processFeatureRegeneration(event, button) {
|
|||
if (button === "regenerateZones") regenerateZones(event);
|
||||
}
|
||||
|
||||
function regenerateRivers() {
|
||||
elevateLakes();
|
||||
Rivers.generate();
|
||||
for (const i of pack.cells.i) {
|
||||
const f = pack.features[pack.cells.f[i]]; // feature
|
||||
if (f.group === "freshwater") pack.cells.h[i] = 19; // de-elevate lakes
|
||||
async function openEmblemEditor() {
|
||||
let type, id, el;
|
||||
|
||||
if (pack.states[1]?.coa) {
|
||||
type = "state";
|
||||
id = "stateCOA1";
|
||||
el = pack.states[1];
|
||||
} else if (pack.burgs[1]?.coa) {
|
||||
type = "burg";
|
||||
id = "burgCOA1";
|
||||
el = pack.burgs[1];
|
||||
} else {
|
||||
tip("No emblems to edit, please generate states and burgs first", false, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
await COArenderer.trigger(id, el.coa);
|
||||
editEmblem(type, id, el);
|
||||
}
|
||||
|
||||
function regenerateRivers() {
|
||||
Rivers.generate();
|
||||
Rivers.specify();
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ function editWorld() {
|
|||
updateGlobePosition();
|
||||
calculateTemperatures();
|
||||
generatePrecipitation();
|
||||
elevateLakes();
|
||||
const heights = new Uint8Array(pack.cells.h);
|
||||
Rivers.generate();
|
||||
Rivers.specify();
|
||||
|
|
|
|||
|
|
@ -1,82 +1,135 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.Voronoi = factory());
|
||||
}(this, (function () { 'use strict';
|
||||
class Voronoi {
|
||||
/**
|
||||
* Creates a Voronoi diagram from the given Delaunator, a list of points, and the number of points. The Voronoi diagram is constructed using (I think) the {@link https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm |Bowyer-Watson Algorithm}
|
||||
* The {@link https://github.com/mapbox/delaunator/ |Delaunator} library uses {@link https://en.wikipedia.org/wiki/Doubly_connected_edge_list |half-edges} to represent the relationship between points and triangles.
|
||||
* @param {{triangles: Uint32Array, halfedges: Int32Array}} delaunay A {@link https://github.com/mapbox/delaunator/blob/master/index.js |Delaunator} instance.
|
||||
* @param {[number, number][]} points A list of coordinates.
|
||||
* @param {number} pointsN The number of points.
|
||||
*/
|
||||
constructor(delaunay, points, pointsN) {
|
||||
this.delaunay = delaunay;
|
||||
this.points = points;
|
||||
this.pointsN = pointsN;
|
||||
this.cells = { v: [], c: [], b: [] }; // voronoi cells: v = cell vertices, c = adjacent cells, b = near-border cell
|
||||
this.vertices = { p: [], v: [], c: [] }; // cells vertices: p = vertex coordinates, v = neighboring vertices, c = adjacent cells
|
||||
|
||||
var Voronoi = function Voronoi(delaunay, points, pointsN) {
|
||||
const cells = {v: [], c: [], b: []}; // voronoi cells: v = cell vertices, c = adjacent cells, b = near-border cell
|
||||
const vertices = {p: [], v: [], c: []}; // cells vertices: p = vertex coordinates, v = neighboring vertices, c = adjacent cells
|
||||
// Half-edges are the indices into the delaunator outputs:
|
||||
// delaunay.triangles[e] gives the point ID where the half-edge starts
|
||||
// delaunay.triangles[e] returns either the opposite half-edge in the adjacent triangle, or -1 if there's not an adjacent triangle.
|
||||
for (let e = 0; e < this.delaunay.triangles.length; e++) {
|
||||
|
||||
for (let e=0; e < delaunay.triangles.length; e++) {
|
||||
|
||||
const p = delaunay.triangles[nextHalfedge(e)];
|
||||
if (p < pointsN && !cells.c[p]) {
|
||||
const edges = edgesAroundPoint(e);
|
||||
cells.v[p] = edges.map(e => triangleOfEdge(e)); // cell: adjacent vertex
|
||||
cells.c[p] = edges.map(e => delaunay.triangles[e]).filter(c => c < pointsN); // cell: adjacent valid cells
|
||||
cells.b[p] = edges.length > cells.c[p].length ? 1 : 0; // cell: is border
|
||||
const p = this.delaunay.triangles[this.nextHalfedge(e)];
|
||||
if (p < this.pointsN && !this.cells.c[p]) {
|
||||
const edges = this.edgesAroundPoint(e);
|
||||
this.cells.v[p] = edges.map(e => this.triangleOfEdge(e)); // cell: adjacent vertex
|
||||
this.cells.c[p] = edges.map(e => this.delaunay.triangles[e]).filter(c => c < this.pointsN); // cell: adjacent valid cells
|
||||
this.cells.b[p] = edges.length > this.cells.c[p].length ? 1 : 0; // cell: is border
|
||||
}
|
||||
|
||||
const t = triangleOfEdge(e);
|
||||
if (!vertices.p[t]) {
|
||||
vertices.p[t] = triangleCenter(t); // vertex: coordinates
|
||||
vertices.v[t] = trianglesAdjacentToTriangle(t); // vertex: adjacent vertices
|
||||
vertices.c[t] = pointsOfTriangle(t); // vertex: adjacent cells
|
||||
const t = this.triangleOfEdge(e);
|
||||
if (!this.vertices.p[t]) {
|
||||
this.vertices.p[t] = this.triangleCenter(t); // vertex: coordinates
|
||||
this.vertices.v[t] = this.trianglesAdjacentToTriangle(t); // vertex: adjacent vertices
|
||||
this.vertices.c[t] = this.pointsOfTriangle(t); // vertex: adjacent cells
|
||||
}
|
||||
}
|
||||
|
||||
function pointsOfTriangle(t) {
|
||||
return edgesOfTriangle(t).map(e => delaunay.triangles[e]);
|
||||
}
|
||||
|
||||
function trianglesAdjacentToTriangle(t) {
|
||||
let triangles = [];
|
||||
for (let e of edgesOfTriangle(t)) {
|
||||
let opposite = delaunay.halfedges[e];
|
||||
triangles.push(triangleOfEdge(opposite));
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
function edgesAroundPoint(start) {
|
||||
let result = [], incoming = start;
|
||||
do {
|
||||
result.push(incoming);
|
||||
const outgoing = nextHalfedge(incoming);
|
||||
incoming = delaunay.halfedges[outgoing];
|
||||
} while (incoming !== -1 && incoming !== start && result.length < 20);
|
||||
return result;
|
||||
}
|
||||
|
||||
function triangleCenter(t) {
|
||||
let vertices = pointsOfTriangle(t).map(p => points[p]);
|
||||
return circumcenter(vertices[0], vertices[1], vertices[2]);
|
||||
}
|
||||
|
||||
return {cells, vertices}
|
||||
|
||||
}
|
||||
|
||||
function edgesOfTriangle(t) {return [3*t, 3*t+1, 3*t+2];}
|
||||
/**
|
||||
* Gets the IDs of the points comprising the given triangle. Taken from {@link https://mapbox.github.io/delaunator/#triangle-to-points| the Delaunator docs.}
|
||||
* @param {number} t The index of the triangle
|
||||
* @returns {[number, number, number]} The IDs of the points comprising the given triangle.
|
||||
*/
|
||||
pointsOfTriangle(t) {
|
||||
return this.edgesOfTriangle(t).map(edge => this.delaunay.triangles[edge]);
|
||||
}
|
||||
|
||||
function triangleOfEdge(e) {return Math.floor(e/3);}
|
||||
/**
|
||||
* Identifies what triangles are adjacent to the given triangle. Taken from {@link https://mapbox.github.io/delaunator/#triangle-to-triangles| the Delaunator docs.}
|
||||
* @param {number} t The index of the triangle
|
||||
* @returns {number[]} The indices of the triangles that share half-edges with this triangle.
|
||||
*/
|
||||
trianglesAdjacentToTriangle(t) {
|
||||
let triangles = [];
|
||||
for (let edge of this.edgesOfTriangle(t)) {
|
||||
let opposite = this.delaunay.halfedges[edge];
|
||||
triangles.push(this.triangleOfEdge(opposite));
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
function nextHalfedge(e) {return (e % 3 === 2) ? e-2 : e+1;}
|
||||
/**
|
||||
* Gets the indices of all the incoming and outgoing half-edges that touch the given point. Taken from {@link https://mapbox.github.io/delaunator/#point-to-edges| the Delaunator docs.}
|
||||
* @param {number} start The index of an incoming half-edge that leads to the desired point
|
||||
* @returns {number[]} The indices of all half-edges (incoming or outgoing) that touch the point.
|
||||
*/
|
||||
edgesAroundPoint(start) {
|
||||
const result = [];
|
||||
let incoming = start;
|
||||
do {
|
||||
result.push(incoming);
|
||||
const outgoing = this.nextHalfedge(incoming);
|
||||
incoming = this.delaunay.halfedges[outgoing];
|
||||
} while (incoming !== -1 && incoming !== start && result.length < 20);
|
||||
return result;
|
||||
}
|
||||
|
||||
function prevHalfedge(e) {return (e % 3 === 0) ? e+2 : e-1;}
|
||||
/**
|
||||
* Returns the center of the triangle located at the given index.
|
||||
* @param {number} t The index of the triangle
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
triangleCenter(t) {
|
||||
let vertices = this.pointsOfTriangle(t).map(p => this.points[p]);
|
||||
return this.circumcenter(vertices[0], vertices[1], vertices[2]);
|
||||
}
|
||||
|
||||
function circumcenter(a, b, c) {
|
||||
let ad = a[0]*a[0] + a[1]*a[1],
|
||||
bd = b[0]*b[0] + b[1]*b[1],
|
||||
cd = c[0]*c[0] + c[1]*c[1];
|
||||
let D = 2 * (a[0] * (b[1] - c[1]) + b[0] * (c[1] - a[1]) + c[0] * (a[1] - b[1]));
|
||||
/**
|
||||
* Retrieves all of the half-edges for a specific triangle `t`. Taken from {@link https://mapbox.github.io/delaunator/#edge-and-triangle| the Delaunator docs.}
|
||||
* @param {number} t The index of the triangle
|
||||
* @returns {[number, number, number]} The edges of the triangle.
|
||||
*/
|
||||
edgesOfTriangle(t) { return [3 * t, 3 * t + 1, 3 * t + 2]; }
|
||||
|
||||
/**
|
||||
* Enables lookup of a triangle, given one of the half-edges of that triangle. Taken from {@link https://mapbox.github.io/delaunator/#edge-and-triangle| the Delaunator docs.}
|
||||
* @param {number} e The index of the edge
|
||||
* @returns {number} The index of the triangle
|
||||
*/
|
||||
triangleOfEdge(e) { return Math.floor(e / 3); }
|
||||
|
||||
/**
|
||||
* Moves to the next half-edge of a triangle, given the current half-edge's index. Taken from {@link https://mapbox.github.io/delaunator/#edge-to-edges| the Delaunator docs.}
|
||||
* @param {number} e The index of the current half edge
|
||||
* @returns {number} The index of the next half edge
|
||||
*/
|
||||
nextHalfedge(e) { return (e % 3 === 2) ? e - 2 : e + 1; }
|
||||
|
||||
/**
|
||||
* Moves to the previous half-edge of a triangle, given the current half-edge's index. Taken from {@link https://mapbox.github.io/delaunator/#edge-to-edges| the Delaunator docs.}
|
||||
* @param {number} e The index of the current half edge
|
||||
* @returns {number} The index of the previous half edge
|
||||
*/
|
||||
prevHalfedge(e) { return (e % 3 === 0) ? e + 2 : e - 1; }
|
||||
|
||||
/**
|
||||
* Finds the circumcenter of the triangle identified by points a, b, and c. Taken from {@link https://en.wikipedia.org/wiki/Circumscribed_circle#Circumcenter_coordinates| Wikipedia}
|
||||
* @param {[number, number]} a The coordinates of the first point of the triangle
|
||||
* @param {[number, number]} b The coordinates of the second point of the triangle
|
||||
* @param {[number, number]} c The coordinates of the third point of the triangle
|
||||
* @return {[number, number]} The coordinates of the circumcenter of the triangle.
|
||||
*/
|
||||
circumcenter(a, b, c) {
|
||||
const [ax, ay] = a;
|
||||
const [bx, by] = b;
|
||||
const [cx, cy] = c;
|
||||
const ad = ax * ax + ay * ay;
|
||||
const bd = bx * bx + by * by;
|
||||
const cd = cx * cx + cy * cy;
|
||||
const D = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
||||
return [
|
||||
Math.floor(1/D * (ad * (b[1] - c[1]) + bd * (c[1] - a[1]) + cd * (a[1] - b[1]))),
|
||||
Math.floor(1/D * (ad * (c[0] - b[0]) + bd * (a[0] - c[0]) + cd * (b[0] - a[0])))
|
||||
Math.floor(1 / D * (ad * (by - cy) + bd * (cy - ay) + cd * (ay - by))),
|
||||
Math.floor(1 / D * (ad * (cx - bx) + bd * (ax - cx) + cd * (bx - ax)))
|
||||
];
|
||||
}
|
||||
|
||||
return Voronoi;
|
||||
|
||||
})));
|
||||
}
|
||||